现在的位置: 首页 > 综合 > 正文

在移动设备上使用M3G编程手册

2013年08月26日 ⁄ 综合 ⁄ 共 6514字 ⁄ 字号 评论关闭

在移动设备上使用M3G编程手册

第一部分快速进入移动Java3D编程

译者:张惠明(碧云天)

原文:3D programming tutorial for mobile devices using M3G (JSR 184)

摘要

在开始前我建议你去了解一些关于M3G领域的一些网络链接,这样对我们的编程是有帮助的。

首先也是最重要的就是Sony Ericsson Developer World。第二个我们要经常去看的就是Sony Ericsson Mobile Java 3D forum。除此之外我们还可以使用Sony Ericsson Developer World portal,在这里你能够发现你问题的答案或者更多。

既然你已经知道了如果出了问题我们将去那里找到答案,那么让我们进行我们的教程。这个教程的目标就是教会每个人如何设置你自己的3D动画和在屏幕上材质着色。对于着色模式,我们将展示如何载入他们并且告诉你那些创建M3G模型时使用的工具。然后我们将通过操作摄像机的一些参数使得我们可以在我们的场景中走动。我们只是想使你在开发第一个使用M3G3D游戏中感到更直观,所以这个教程将相当快和直接,而有很少的解释,在这系列教程的其他部分将会对M3G的其他属性进行详细的讨论。

既然这个程序代码是针对教学目的的,它不是最佳的也不能覆盖所有可能出现的错误。我们将在以后的部分中讨论更多的深入的问题。

你需要知道什么

我们在开始阅读这篇文章之前,你应当了解一个MIDlet类和一个Canvas类的基本部分。如果你感到迷糊,参考游戏代码(这个教程中讨论的)和查看M3GMIDletM3GConvas类,这并不是一个困难的问题。当然如果你有3D编程和数学的背景那将是一件很好的事情,但并不是必须的。

Convas画板

当我们使用JSR184库编程时,我们使用的时MIDP2.0的配置文件,这意味着我们可以自用的使用更多的功能。让我们从设置我们的屏幕画板开始。和我们在通常的2D游戏编程有着同样的过程。你需要设置你的MIDlet类,并且启动你的屏幕画板和在Paint方法中进行绘画。既然你已经知道如何作并且这是一个相当容易的过程,我们将跳过这个过程。首先让我们看一下Canvas类的开头,这里是一些导入文件和变量声明。

import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.M3G.Camera;
import javax.microedition.M3G.Graphics3D;
import javax.microedition.M3G.Light;
import javax.microedition.M3G.Loader;
import javax.microedition.M3G.Object3D;
import javax.microedition.M3G.Transform;
import javax.microedition.M3G.World;

 

/**
 *
 * @author  Biovenger
 * @version
 */
public class M3GCanvas
extends GameCanvas
implements Runnable {
    // Thread-control
    boolean running = false;
    boolean done = true;
   
    // If the game should end
    public static boolean gameOver = false;
   
    // Rendering hints
    public static final int STRONG_RENDERING_HINTS = Graphics3D.ANTIALIAS | Graphics3D.TRUE_COLOR | Graphics3D.DITHER;
    public static final int WEAK_RENDERING_HINTS = 0;
    public static int RENDERING_HINTS = STRONG_RENDERING_HINTS;
   
    // Key array
    boolean[] key = new boolean[5];
   
    // Key constants
    public static final int FIRE = 0;
    public static final int UP = FIRE + 1;
    public static final int DOWN = UP + 1;
    public static final int LEFT = DOWN + 1;
    public static final int RIGHT = LEFT + 1;

 

这是相当基础的部分,让我们快速的查看这段是用来干什么的。事项我们看到的是一些导入文件,我们只导入了我们在教程中使用的所有的类和你在JSR184  API文档中发现的其他的类文件。我们也看到了一些其他的例如runningdone这样的一些线程变量,不过这些都是一些自解释型的。

 

现在,让我们看一下“rendering hints”,这些“hits”是用来告诉我们当我们要着色的时候使用的是什么样的质量。既然这些着色的质量是依赖于手机的,所以我们第一了两个不同的“hits”:弱和强。正如你看到的一样,弱的着色模式是使用双消除混叠现象、真实的颜色、和抖动。弱的模式根本就没有着色的效果,这是我们能够得到的最快和最差的着色模式。当你查看代码的时候,这些不同的效果可以通过使用简单的逻辑OR来合并。在我们系列文章的后面章节中将会展示更多的特效效果。

 

接下来我们看到的实按键数组,我们使用了简单的数组形式,在键盘操作进程中他们将被改变。如果你对按键是如何处理的感到好奇,你可以在例子代码中找到相关的代码。举例说明一下,如果我们的UP键被按下那么if(key[UP])将是正确的。

 

M3G文件格式

 

JSR184有它自己的文件格式,叫做M3G。这是一个通用的3D文件格式,这里可以存储例如模型、灯光、摄像机、蒙皮甚至动画。注意,不但这个格式是好的,将模型载入你的应用程序也是相当容易的。我敢说M3G将是无止境的。我们怎样来建立一个M3G文件呢?我在下面的部分将来解释集中建立M3G的方法:

 

1.             首先,最新的Discreet's 3D Studio Max版本已经内建了一个M3G的导出器。我们只需要点击导出按钮,你就能导出你建立的场景、动画、骨骼、材质等成为M3G文件。然而,很多人发现Discreet的导出器比较麻烦并且有一些错误,所以为了得到更好的效果,我们使用第二种方法。

2.             HI公司是索尼爱立信公司的JSR184接口的提供者,他们制作了针对3个十分流行的3D建模软件例如3D Studio Max, LightWave Maya的插件,你可以在here>>找到他们。

 

3.             Blender,这是一个具有可用的M3G导出器的免费的3D建模工具,然而它在开发的比较早,并且有一些错误。

 

那么我们怎样在我们的程序中调入这些非常有用的文件呢?非常简单。在JSR184中包含一个类叫做Loader,它可以正确的做到这点。使用一个简单的方法就能从一个M3G文件中载入所有的对象。一个可以是字符串类型的URL另一个方法是一个原始字节流中的偏移,下面是如何使用的例子:

 

Object3D[] objects = Loader.load("file.M3G");

 

Object3D[] objects2 = Loader.load(byteArray, offset);

 

       这个Load方法通常返回的是一个Object3D类型的数组,对于这点有一个非常重要的原因。最好的就是使得Loader类可以载入碧M3G文件更多的数据。它可以转换成任何一个从Object3D类中继承过来的类。虽然,你们几乎都是使用这种方式来调入M3G文件。

 

       现在,我创建了一个简单的M3G文件,命名为map.M3G并且显示它。我们将使用loader.load方法来载入这个文件,正如我们看见的那样,这个函数返回的是一个Object3D的数组。正如程序显示的那样我们不能使用Object3D直接进行显示,我们必须将它转化成能够在屏幕上显示的对象。在这个教程中,我们将载入这个World节点。World节点是JSR184屏幕绘图的顶节点。它将处理各种类型的信息其中包括摄像机、灯光、背景、和一些贴图。在系列文章的以后部分将深入的讲解场景绘图和JSR184的其他接口,现在我们只需要知道的就是World类可以擦作一个整个的场景,和我们想做的额外的事情。请看下面的代码,这段代码的作用是从一个M3G文件中导入一个World节点。

 

** Loads our world */
    private void loadWorld()
    {
        try
        {
            // Loading the world is very simple. Note that I like to use a
            // res-folder that I keep all files in. If you normally just put your
            // resources in the project root, then load it from the root.
            Object3D[] buffer = Loader.load("/res/map.M3G");
           
            // Find the world node, best to do it the "safe" way
            for(int i = 0; i < buffer.length; i++)
            {
                if(buffer[i] instanceof World)
                {
                    world = (World)buffer[i];
                    break;
                }
            }
           
            // Clean objects
            buffer = null;
        }
        catch(Exception e)
        {
            // ERROR!
            System.out.println("Loading error!");
            reportException(e);
        }
    }

 

    正如你所看见的,我们使用Loader类载入Object3D数组以后,我们只需要简单地遍历整个数组并且找到World节点。这是查找一个World节点的最安全的方法。当我们发现我们的World节点以后我们应当中断我们的循环并且清除我们的缓冲区(既然不需要了,就应当删除了,虽然离开方法以后会自动删除,但养成好的习惯)。

 

    好了,新在我们已经载入了我们的World节点,也就是我们已经知道了一个场景的最高节点,和操作所有节点的信息。在向你展示简单的显示他们之前,让我们首先来展示一下我们如何移动一个摄像机在我们刚刚载入的文件中。

 

    摄像机操作:

 

    我们已经有一个准备绘制的World节点那么我们需要一个摄像机,这个摄像机的作用是使我们能够在整个世界中移动。如果你回忆一下你会发现我们已经准备好了一个我们能够使用的摄像机信息。所以我们应当从World中拾取出摄像机,并且操作它。

 

    JSR184中一个摄像机通过Camera类来描述。这个类使得我们在我们的3D程序中更容易的操作摄像机完成简单地定位和旋转方法。我们在例子中只使用了两个方法分别是translate(float, float, float)setOrientation(float, float, float, float)。第一种方法是在3D空间中移动偏移为XYZ。举个例子,如果你想移动摄像机到X3个单位和Z3个单位,你应当做如下处理:

Camera cam = new Camera();  // This is our camera

 

//移动摄像机偏移   X  Y  Z
cam.translate(3.0f, 0.0f, 3.0f);

 

       小菜一碟,每个方法调用移动摄像机是在以前的基础上操作,也就是入阁我们调用了两次以上的方法那么实际上我们是在X轴上移动了6个单位、在Z轴上也移动了6个单位。旋转就象移动一样简单,但是我们首先还要解释一下这个方法。它看起来十分象3DAPI旋转的方法。我们需要四个参数,第一个参数是旋转的实际角度,其他三个参数是一个要旋转的方向向量(X轴,Y轴,Z轴)。转向和方向向量将在以后讲到,现在,我们只需要知道以下的就可以了:

 

//沿着X轴旋转30
cam.setOrientation(30.0f, 1.0f, 0.0f, 0.0f);

 

//沿着Y轴旋转30
cam.setOrientation(30.0f, 0.0f, 1.0f, 0.0f);

 

//沿着Z轴旋转30
cam.setOrientation(30.0f, 0.0f, 0.0f, 1.0f);

 

       注意这个方法的名称是setOrientation,这意味着将清除摄像机以前的的所有旋转操作。我假设你已经知道了沿着一个轴旋转的意义是什么,我们将不再这里讨论这个问题。

       既然你已经知道了操作一个摄像机移动和旋转的方法,我们将展示如何从一个World中提取一个摄像机。

/** 载入我们的摄像机 */
    private void loadCamera()
    {
        // BAD!
        if(world == null)
            return;
       
        //
从世界中得到当前活动的摄像机

        cam = world.getActiveCamera();
       
        //
创建一个灯光
        Light l = new Light();
       
        //
确认灯光是 AMBIENT
        l.setMode(Light.AMBIENT);
       
        //
我们设置一个较亮的亮度

        l.setIntensity(3.0f);
       
        //
把它添加到世界中
        world.addChild(l);
    }

 

    这个容易吗?它很容易。我们只是使用getActiveCamera方法从World中提取当前活动的摄像机。这是我们世界被导出的时候已经带的摄像机。通过以上的方法我们得到了一个我们可以根据我们的想法来操作的摄像机。然而这个函数中也做了其他们功能,这里添加了一个灯。我们将在以后的章节中深入的讲述灯光,但是这里我们看见在世界中添加一个灯光是如此的容易。我们创建了一个环境灯光(如果你不知道,那么环境灯光就是所有的表面都被灯光从每个方向照亮。)并且添加到世界中。这个方法是我们得到了一个真实的世界,正如我在以前对你们讲到的,World节点能够操作所有类型的节点信息,包括灯光,所以我们只需要在我们的世界中添加一次灯光,JSR184将为我们操作。难道还手动的设置吗?我们在开始下一个部分:显示之前,让我们来移动摄像机。我们已经在以前提到了存储键盘信息的布尔型的数组,所以我们处理数组和操作我们的摄像机行为。首先我们需要一些使我们摄像机运动的变量。

// 摄像机旋转
float camRot = 0.0f;
double camSine = 0.0f;
double camCosine = 0.0f;
   
//
头部振动
float headDeg = 0.0f;

抱歉!评论已关闭.