当前页面: 开发资料首页 → J2ME 专题 → 为J2ME开发移动3D游戏之立即模式
为J2ME开发移动3D游戏之立即模式
摘要: 为J2ME开发移动3D游戏之立即模式
作者:朱先忠编 文章来源:Yesky
一、简述
现在,移动游戏和移动应用开发极为热门!游戏中需要有时髦漂亮的图形,其设计标准比以前任何时候都要高。本文将告诉你怎样用酷毙的移动3D图形API为J2ME设备开发3D图形游戏。
如果你在用MIDP1.0进行用户接口编程,那么有两条路你可以选择:使用高级的UI类或者一切由你自己从头开始。作为游戏开发者,第一种选择往往是不可能的;这是为什么游戏开发者不得不为他们的高级游戏开发自己的3D引擎的原因。无疑,这需要付出大量的时间和努力,而缺乏浮点数支持的CLDC 1.0(MIDP 1.0正是建于其上)对问题的解决没有多大帮助。
在MIDP 2.0中,有一个可选的叫移动3D图形API的软件包,或者叫JSR 184。该API是第一个基于Java标准开发的移动设备上的三维图形软件包。该API既有高级又有低级图形特征;其中,高级特征称为保留模式,低级特征称为立即模式。保留模式使得开发者有可能使用场景图形并使场景中的物体根据虚拟相机和灯光的位置进行自身的着色。立即模式能够允许应用程序直接进行物体绘制。如果需要,可以在同一个应用程序中使用这两种模式。
本文着重介绍立即模式。
二、3D API
让我们以列举和解释该3D API中的类作为开始。除了这些API外,JSR 184还包含了一个场景图形结构和一个相应的文件格式以有效地管理和配置3D内容。该文件格式定义了一种m3g文件,这种文件典型地从3D建模文件应用程序中转换而来。
表1.3D API类
<table cellspacing="0" cellpadding="2" width="90%" align="center" border="1">
<tr><td>类 </td><td>描述</td></tr><tr><td>AnimationController</td><td>控制动画顺序。</td></tr><tr><td>AnimationTrack</td><td>把一个KeyframeSequence同一个AnimationController相关联。</td></tr><tr><td>Appearance </td><td>定义一个网眼(Mesh)或一个Spring3D的着色属性的一组对象。</td></tr><tr><td>Background </td><td>定义视图是怎样被清除的。</td></tr><tr><td>Camera </td><td>一个场景图顶点,它定义了场景中观察者的位置以及从3D到2D的投影。</td></tr><tr><td>CompositingMode </td><td>一个Appearance类,它封装了每一个像素的合成属性。</td></tr><tr><td>Fog</td><td>一个Appearance类,它包含了雾化的有关属性。</td></tr><tr><td>Graphics3D </td><td>一个单独的3D图形上下文。所有的着色操作都是在该类中的render()方法中实现的。</td></tr><tr><td>Group </td><td>一个场景图形结点,它存储了一个无序的结点集作为它的子结点。</td></tr><tr><td>Image2D</td><td>一个二维图像,可用于纹理,背景,或者精灵图像。</td></tr><tr><td>IndexBuffer </td><td>该类定义了如何把顶点连接起来以形成一个几何体。</td></tr><tr><td>KeyframeSequence </td><td>封装了一系列的具有时间戳和矢量值的关键帧的动画数据。</td></tr><tr><td>Light </td><td>描述了不同类型的光源。</td></tr><tr><td>Loader </td><td>下载和反串行化图形结点及结点成分,以及整个场景图形。</td></tr><tr><td>Material </td><td>封装了进行光学计算的材质属性。 </td></tr><tr><td>Mesh </td><td>描述了一个3D对象,它是用多边形面定义的。</td></tr><tr><td>MorphingMesh </td><td>描述了一个顶点-变形的多边形网眼。</td></tr><tr><td>Node </td><td>所有场景图形结点的抽象基类。其五个具体子类是:Camera,Mesh,Sprite3D,Light和Group。</td></tr><tr><td>Object3D </td><td>所有可以成为3D世界中组成部分的对象的抽象基类。</td></tr><tr><td>PolygonMode </td><td>封装了多边形级别属性。</td></tr><tr><td>RayIntersection</td><td>存储了对于分割的Mesh或Sprite3D的引用,以及有关分割点的信息。</td></tr><tr><td>SkinnedMesh </td><td>描述了一个框架动画的多边形网眼。</td></tr><tr><td>Sprite3D </td><td>用3D位置来描述一个2D图像。</td></tr><tr><td>Texture2D </td><td>封装了一个2D纹理图像和一个属性集合,这些属性指出该图像是如何应用到子网眼上的。</td></tr><tr><td>Transform </td><td>一个通用的4x4的浮点数矩阵,用来描述一个变换。</td></tr><tr><td>Transformable </td><td>Node和Texture2D类的抽象基类。</td></tr><tr><td>TriangleStripArray </td><td>定义了一个三角形带数组。</td></tr><tr><td>VertexArray</td><td>一个整型矢量数组,描述了顶点位置,法线,颜色或者纹理坐标。</td></tr><tr><td>VertexBuffer </td><td>存储对于VertexArrays的引用,它包含了一个顶点集的位置,颜色,法线,以及纹理坐标。</td></tr><tr><td>World </td><td>一个特别的Group结点,它作为场景图最顶层的容器。</td></tr></table>
三、举例
我们将开发一个简单的旋转一个多边形的3D应用程序为例。该多边形是一个立方体,它的纹理是一张旧汽车相片。列表1展示了例程midlet的主要类-应用程序的中心类。该类负责创建应用程序并建立起运行MyCanvas的计时器。
列表1. MIDletMain类
<table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e3e3e3" border="1">
<tr><td>import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;
public class MIDletMain extends MIDlet {
static MIDletMain midlet;
MyCanvas d = new MyCanvas();
Timer iTimer = new Timer();
public MIDletMain() {
this.midlet = this;
}
public void startApp() {
Display.getDisplay(this).setCurrent(d);
iTimer.schedule( new MyTimerTask(), 0, 40 );
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
public static void quitApp() {
midlet.destroyApp(true);
midlet.notifyDestroyed();
midlet = null;
}
class MyTimerTask extends TimerTask {
public void run() {
if( d != null ) {
d.repaint();
}
}
}
}</td></tr></table>
列表2显示了MyCanvas类,该类包含了应用程序的所有图形逻辑。init()方法负责结点的创建,纹理文件的装载并设置纹理,外观和背景也被一起设置。paint()方法负责着色并旋转立方体。图1展示了正在一个模拟器中运行的实际程序。
列表2. MyCanvas类
<table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e3e3e3" border="1">
<tr><td>import javax.microedition.lcdui.*;
import javax.microedition.m3g.*;
public class MyCanvas extends Canvas {
private Graphics3D graphics3d;
private Camera camera;
private Light light;
private float angle = 0.0f;
private Transform transform = new Transform();
private Background background = new Background();
private VertexBuffer vbuffer;
private IndexBuffer indexbuffer;
private Appearance appearance;
private Material material = new Material();
private Image image;
public MyCanvas() {
// 创建Displayable对象以探听命令事件
setCommandListener(new CommandListener() {
public void commandAction(Command c, Displayable d) {
if (c.getCommandType() == Command.EXIT) {
MIDletMain.quitApp();}}
});
try { init();}
catch(Exception e) { e.printStackTrace();}
}
/**
* 组件的初始化
*/
private void init() throws Exception {
addCommand(new Command("Exit", Command.EXIT, 1));
graphics3d = Graphics3D.getInstance();
camera = new Camera();
camera.setPerspective( 60.0f,(float)getWidth()/ (float)getHeight(),1.0f,1000.0f );
light = new Light();
light.setColor(0xffffff);
light.setIntensity(1.25f);
short[] vert = {
5, 5, 5, -5, 5, 5, 5,-5, 5, -5,-5, 5,
-5, 5,-5, 5, 5,-5, -5,-5,-5, 5,-5,-5,
-5, 5, 5, -5, 5,-5, -5,-5, 5, -5,-5,-5,
5, 5,-5, 5, 5, 5, 5,-5,-5, 5,-5, 5,
5, 5,-5, -5, 5,-5, 5, 5, 5, -5, 5, 5,
5,-5, 5, -5,-5, 5, 5,-5,-5, -5,-5,-5 };
VertexArray vertArray = new VertexArray(vert.length / 3, 3, 2);
vertArray.set(0, vert.length/3, vert);
//立方体的各个结点法线
byte[] norm = {
0, 0, 127, 0, 0, 127, 0, 0, 127, 0, 0, 127,
0, 0,-127, 0, 0,-127, 0, 0,-127, 0, 0,-127,
-127, 0, 0, -127, 0, 0, -127, 0, 0, -127, 0, 0,
127, 0, 0, 127, 0, 0, 127, 0, 0, 127, 0, 0,
0, 127, 0, 0, 127, 0, 0, 127, 0, 0, 127, 0,
0,-127, 0, 0,-127, 0, 0,-127, 0, 0,-127, 0 };
VertexArray normArray = new VertexArray(norm.length / 3, 3, 1);
normArray.set(0, norm.length/3, norm);
//各个结点的纹理坐标
short[] tex = {
1, 0, 0, 0, 1, 1, 0, 1,
1, 0, 0, 0, 1, 1, 0, 1,
1, 0, 0, 0, 1, 1, 0, 1,
1, 0, 0, 0, 1, 1, 0, 1,
1, 0, 0, 0, 1, 1, 0, 1,
1, 0, 0, 0, 1, 1, 0, 1 };
VertexArray texArray = new VertexArray(tex.length / 2, 2, 2);
texArray.set(0, tex.length/2, tex);
int[] stripLen = { 4, 4, 4, 4, 4, 4 };
// 对象的VertexBuffer
VertexBuffer vb = vbuffer = new VertexBuffer();
vb.setPositions(vertArray, 1.0f, null);
vb.setNormals(normArray);
vb.setTexCoords(0, texArray, 1.0f, null);
indexbuffer = new TriangleStripArray( 0, stripLen );
//纹理图像
image = Image.createImage( "/pic1.png" );
Image2D image2D = new Image2D( Image2D.RGB, image );
Texture2D texture = new Texture2D( image2D );
texture.setFiltering(Texture2D.FILTER_NEAREST,Texture2D.FILTER_NEAREST);
texture.setWrapping(Texture2D.WRAP_CLAMP,Texture2D.WRAP_CLAMP);
texture.setBlending(Texture2D.FUNC_MODULATE);
// 创建外观(Appearance)对象
appearance = new Appearance();
appearance.setTexture(0, texture);
appearance.setMaterial(material);
material.setColor(Material.DIFFUSE, 0xFFFFFFFF);
material.setColor(Material.SPECULAR, 0xFFFFFFFF);
material.setShininess(100.0f);
background.setColor(0xffffcc);
}
protected void paint(Graphics g) {
graphics3d.bindTarget(g, true,
Graphics3D.DITHER | Graphics3D.TRUE_COLOR);
graphics3d.clear(background);
//设置照相机
Transform transform = new Transform();
transform.postTranslate(0.0f, 0.0f, 30.0f);
graphics3d.setCamera(camera, transform);
//设置灯光
graphics3d.resetLights();
graphics3d.addLight(light, transform);
//设置旋转
angle += 1.0f;
transform.setIdentity();
transform.postRotate(angle, 1.0f, 1.0f, 1.0f);
graphics3d.render(vbuffer, indexbuffer, appearance, transform);
graphics3d.releaseTarget();
}
}</td></tr></table>
<table width="90%" align="center" border="0">
<tr><td>
图1 正在一个模拟器中运行的应用程序</td></tr></table>
四、小结
JSR 184对于可以运行MIDP 2.0的设备来说,是一个节省时间和空间的可选的软件开发包。它允许开发者使用两种图形方式-保留模式和立即模式-来产生3D图形。本文集中讲述了立即模式,并给出一个例子程序来说明怎样使用3D。