現在,移動遊戲和移動應用開發極爲熱門!遊戲中需要有時髦漂亮的圖形,其設計標準比以前任何時候都要高。本文將告訴你怎樣用酷斃的移動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類
類 | 描述 |
AnimationController | 控制動畫順序。 |
AnimationTrack | 把一個KeyframeSequence同一個AnimationController相關聯。 |
Appearance | 定義一個網眼(Mesh)或一個Spring3D的着色屬性的一組對象。 |
Background | 定義視圖是怎樣被清除的。 |
Camera | 一個場景圖頂點,它定義了場景中觀察者的位置以及從3D到2D的投影。 |
CompositingMode | 一個Appearance類,它封裝了每一個像素的合成屬性。 |
Fog | 一個Appearance類,它包含了霧化的有關屬性。 |
Graphics3D | 一個單獨的3D圖形上下文。所有的着色操作都是在該類中的render()方法中實現的。 |
Group | 一個場景圖形結點,它存儲了一個無序的結點集作爲它的子結點。 |
Image2D | 一個二維圖像,可用於紋理,背景,或者精靈圖像。 |
IndexBuffer | 該類定義瞭如何把頂點連接起來以形成一個幾何體。 |
KeyframeSequence | 封裝了一系列的具有時間戳和矢量值的關鍵幀的動畫數據。 |
Light | 描述了不同類型的光源。 |
Loader | 下載和反串行化圖形結點及結點成分,以及整個場景圖形。 |
Material | 封裝了進行光學計算的材質屬性。 |
Mesh | 描述了一個3D對象,它是用多邊形面定義的。 |
MorphingMesh | 描述了一個頂點-變形的多邊形網眼。 |
Node | 所有場景圖形結點的抽象基類。其五個具體子類是:Camera,Mesh,Sprite3D,Light和Group。 |
Object3D | 所有可以成爲3D世界中組成部分的對象的抽象基類。 |
PolygonMode | 封裝了多邊形級別屬性。 |
RayIntersection | 存儲了對於分割的Mesh或Sprite3D的引用,以及有關分割點的信息。 |
SkinnedMesh | 描述了一個框架動畫的多邊形網眼。 |
Sprite3D | 用3D位置來描述一個2D圖像。 |
Texture2D | 封裝了一個2D紋理圖像和一個屬性集合,這些屬性指出該圖像是如何應用到子網眼上的。 |
Transform | 一個通用的4x4的浮點數矩陣,用來描述一個變換。 |
Transformable | Node和Texture2D類的抽象基類。 |
TriangleStripArray | 定義了一個三角形帶數組。 |
VertexArray | 一個整型矢量數組,描述了頂點位置,法線,顏色或者紋理座標。 |
VertexBuffer | 存儲對於VertexArrays的引用,它包含了一個頂點集的位置,顏色,法線,以及紋理座標。 |
World | 一個特別的Group結點,它作爲場景圖最頂層的容器。 |
舉例
我們將開發一個簡單的旋轉一個多邊形的3D應用程序爲例。該多邊形是一個立方體,它的紋理是一張舊汽車相片。列表1展示了例程midlet的主要類-應用程序的中心類。該類負責創建應用程序並建立起運行MyCanvas的計時器。
列表1. MIDletMain類
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(); } } } } |
列表2顯示了MyCanvas類,該類包含了應用程序的所有圖形邏輯。init()方法負責結點的創建,紋理文件的裝載並設置紋理,外觀和背景也被一起設置。paint()方法負責着色並旋轉立方體。圖1展示了正在一個模擬器中運行的實際程序。
列表2. MyCanvas類
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(); } } |
圖1 正在一個模擬器中運行的應用程序 |
小結
JSR 184對於可以運行MIDP 2.0的設備來說,是一個節省時間和空間的可選的軟件開發包。它允許開發者使用兩種圖形方式-保留模式和立即模式-來產生3D圖形。本文集中講述了立即模式,並給出一個例子程序來說明怎樣使用3D API。