首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> J2ME

J2ME 3D 程式設計實戰:第一個 M3G 程式完整原始碼解析

     編輯:J2ME

專案目標與執行環境

  • 參考 WTK 2.2 Demo3D 範例改寫
  • 目標:載入 swerve.m3g 場景並播放內建動畫
  • 編譯環境:J2ME WTK 2.2
  • 專案主類別:first3d.First3DMIDlet
  • 需啟用 API:Mobile 3D Graphics (JSR 184),Profile: MIDP 2.0,Configuration: CLDC 1.1

資源檔案準備

將 WTK 安裝目錄下的場景檔複製至專案 res/ 目錄:


WTK2.2/apps/Demo3D/res/com/superscape/m3g/wtksamples/retainedmode/content/swerve.m3g
→ 專案/res/swerve.m3g

完整原始碼 (兩個檔案)

First3DMIDlet.java


package first3d;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class First3DMIDlet extends MIDlet {
    private First3DCanvas displayable = new First3DCanvas();

    public void startApp() {
        Display.getDisplay(this).setCurrent(displayable);
    }

    public void pauseApp() {}
    public void destroyApp(boolean unconditional) {}
}

First3DCanvas.java (核心邏輯)


package first3d;

import javax.microedition.lcdui.*;
import javax.microedition.m3g.*;
import java.util.*;

public class First3DCanvas extends Canvas implements Runnable {
    private World myWorld = null;
    private Graphics3D g3d = Graphics3D.getInstance();
    private Camera cam = null;
    
    private int viewport_x, viewport_y, viewport_width, viewport_height;
    private long worldStartTime = 0;
    private int validity = 0;

    public First3DCanvas() {
        Thread thread = new Thread(this);
        thread.start();

        try {
            // 1. 載入 .m3g 場景
            myWorld = (World) Loader.load("/swerve.m3g")[0];
            
            // 2. 視口初始化
            viewport_x = 0;
            viewport_y = 0;
            viewport_width = getWidth();
            viewport_height = getHeight();
            
            // 3. 取得場景中定義的 Camera
            cam = myWorld.getActiveCamera();
            
            // 4. 依螢幕長寬比調整視口 (保持投影比例)
            float[] params = new float[4];
            int type = cam.getProjection(params);
            if (type != Camera.GENERIC) {
                float wAspect = (float) viewport_width / viewport_height;
                if (wAspect < params[1]) { // params[1] = 投影高度比例
                    float height = viewport_width / params[1];
                    viewport_height = (int) height;
                    viewport_y = (getHeight() - viewport_height) / 2;
                } else {
                    float width = viewport_height * params[1];
                    viewport_width = (int) width;
                    viewport_x = (getWidth() - viewport_width) / 2;
                }
            }
            
            worldStartTime = System.currentTimeMillis();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    protected void paint(Graphics g) {
        // 清除背景
        g.setColor(0x000000);
        g.fillRect(0, 0, getWidth(), getHeight());

        // 綁定 3D 渲染目標
        g3d.bindTarget(g);
        g3d.setViewport(viewport_x, viewport_y, viewport_width, viewport_height);

        // 更新動畫時間並取得有效性
        long startTime = System.currentTimeMillis() - worldStartTime;
        validity = myWorld.animate((int) startTime);

        try {
            g3d.render(myWorld);
        } finally {
            g3d.releaseTarget();
        }
    }

    public void run() {
        try {
            while (true) {
                // 重繪視口區域
                repaint(viewport_x, viewport_y, viewport_width, viewport_height);
                Thread.sleep(30); // 限制幀率 ~33 FPS
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

關鍵技術點解析

1. 場景載入 Loader.load()

  • 回傳 Object[],第一個元素通常是 World
  • 場景包含幾何體、材質、貼圖、攝影機、燈光、動畫軌跡
  • 檔案放在 res/ 目錄,打包進 JAR 根目錄

2. 視口與投影比例同步

程式碼中 cam.getProjection(params) 取得投影參數,params[1] 代表投影的高寬比。根據螢幕實際長寬比調整 viewport_width/height,避免畫面拉伸變形。這是保留模式場景在不同解析度裝置上正確顯示的關鍵。

3. 動畫驅動 World.animate(time)

  • 傳入自場景啟動以來的毫秒數
  • 引擎內部根據動畫軌跡插值更新節點變換 (位移、旋轉、縮放)
  • 回傳 validity 指示場景圖是否有變更 (可用於優化)

4. 渲染迴路與執行緒分離

  • 建構子啟動獨立執行緒 run() 持續呼叫 repaint()
  • paint() 中完成 bindTarget → render → releaseTarget 標準流程
  • 使用 repaint(x,y,w,h) 僅重繪視口區域,提升效能

5. 例外處理與資源釋放

finally { g3d.releaseTarget(); } 確保渲染目標釋放,避免記憶體洩漏或繪圖鎖死。

常見問題與除錯

現象可能原因解決方案
黑畫面、無畫面視口設定錯誤、Camera 未啟用、模型在視野外檢查 setViewport 參數、world.setActiveCamera、模型座標
畫面撕裂、閃爍未雙緩衝、repaint 過於頻繁加入 Thread.sleep、使用 flushGraphics() (若繼承 GameCanvas)
載入失敗 NullPointerExceptionswerve.m3g 未打包進 JAR、路徑錯誤確認 res/swerve.m3g 存在、build.xml 包含資源
動畫靜止未呼叫 animate() 或時間參數錯誤確認執行緒正常運行、worldStartTime 正確初始化

擴充練習建議

  • 加入鍵盤控制:keyPressed 中調整 cam 位置/旋轉
  • 切換場景:準備多個 .m3g,選單切換時重新 Loader.load
  • 混合模式:在保留模式場景上疊加立即模式粒子效果
  • 效能分析:記錄 render 耗時,針對低端裝置簡化模型

此範例展示了 M3G 保留模式的核心工作流程:載入場景 → 同步視口 → 以時間驅動動畫 → 持續渲染。掌握此流程即可開發豐富的 3D 行動應用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved