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

J2ME 2D 繪圖坐標系統變換:translate 平移與滾動背景實現

     編輯:J2ME

前言:坐標變換是 2D 遊戲引擎的核心

MIDP Graphics.translate(x, y) 實現坐標系平移,配合 getTranslateX/Y() 獲取當前偏移,是實現大地圖滾動、攝像機跟隨、UI 震動等效果的基礎。本文解析標準平移模式與無限循環滾動背景實現。

核心 API

  • g.translate(dx, dy):將坐標原點平移 (dx, dy),後續繪製坐標相對新原點
  • g.getTranslateX()、g.getTranslateY():獲取當前累積平移量
  • g.drawImage(img, x, y, anchor):錨點基於當前坐標系計算

標準平移繪製模式


public void paint(Graphics g) {
    // 1. 保存當前平移 (可選,若需恢復)
    int savedTx = g.getTranslateX();
    int savedTy = g.getTranslateY();
    
    // 2. 應用攝像機偏移
    g.translate(-cameraX, -cameraY); // 世界座標 → 屏幕座標
    
    // 3. 繪製世界對象 (使用世界座標)
    g.drawImage(mapImage, 0, 0, Graphics.TOP | Graphics.LEFT);
    g.drawImage(playerImg, playerX, playerY, Graphics.TOP | Graphics.LEFT);
    for (Enemy e : enemies) {
        g.drawImage(enemyImg, e.x, e.y, Graphics.TOP | Graphics.LEFT);
    }
    
    // 4. 恢復平移 (繪製 UI/HUD 使用屏幕座標)
    g.translate(savedTx, savedTy); // 或 g.translate(-cameraX, -cameraY) 逆操作
    
    // 5. 繪製 HUD (分數、血條等,屏幕座標)
    g.drawString("Score: " + score, 5, 5, Graphics.TOP | Graphics.LEFT);
}

無限循環滾動背景實現 (核心代碼)


class ScrollingCanvas extends Canvas implements CommandListener {
    private Image bgTile;           // 單個背景瓦片
    private int translateX = 0, translateY = 0; // 當前平移量
    private int tileW, tileH;       // 瓦片尺寸
    private int canvasW, canvasH;   // 畫布尺寸
    private Command exitCmd;

    public ScrollingCanvas(MIDlet midlet) {
        try { bgTile = Image.createImage("/tile.png"); } catch (IOException e) {}
        tileW = bgTile.getWidth(); tileH = bgTile.getHeight();
        canvasW = getWidth(); canvasH = getHeight();
        exitCmd = new Command("Exit", Command.EXIT, 1);
        addCommand(exitCmd); setCommandListener(this);
    }

    protected void paint(Graphics g) {
        if (bgTile == null) return;
        // 清屏
        g.setColor(0xFFFFFF); g.fillRect(0, 0, canvasW, canvasH);
        // 應用平移
        g.translate(translateX, translateY);
        // 繪製瓦片覆蓋整個可視區 (含平移後超出部分)
        // 計算需要繪製的瓦片範圍
        int startX = -translateX / tileW;
        int endX = startX + canvasW / tileW + 2;
        int startY = -translateY / tileH;
        int endY = startY + canvasH / tileH + 2;
        
        for (int tx = startX; tx < endX; tx++) {
            for (int ty = startY; ty < endY; ty++) {
                g.drawImage(bgTile, tx * tileW, ty * tileH, Graphics.TOP | Graphics.LEFT);
            }
        }
    }

    protected void keyPressed(int keyCode) {
        int action = getGameAction(keyCode);
        switch (action) {
            case UP:
                // 向上滾動:translateY 減少 (內容向下移動)
                if (translateY - tileH < -canvasH) // 超出一屏高度,循環
                    translateY = 0;
                else
                    translateY -= tileH;
                break;
            case DOWN:
                if (translateY + tileH > 0)
                    translateY = -canvasH;
                else
                    translateY += tileH;
                break;
            case LEFT:
                if (translateX - tileW < -canvasW)
                    translateX = 0;
                else
                    translateX -= tileW;
                break;
            case RIGHT:
                if (translateX + tileW > 0)
                    translateX = -canvasW;
                else
                    translateX += tileW;
                break;
        }
        repaint(); // 觸發重繪
    }

    public void commandAction(Command c, Displayable d) {
        if (c == exitCmd) midlet.destroyApp(true);
    }
}

關鍵邏輯解析

進階:攝像機跟隨玩家


public void updateCamera() {
    // 目標:玩家居中
    int targetTx = canvasW / 2 - playerX;
    int targetTy = canvasH / 2 - playerY;
    
    // 邊界限制:地圖範圍 [0, mapW] × [0, mapH]
    targetTx = Math.max(Math.min(targetTx, 0), canvasW - mapW);
    targetTy = Math.max(Math.min(targetTy, 0), canvasH - mapH);
    
    // 平滑插值 (可選)
    translateX += (targetTx - translateX) * 0.2;
    translateY += (targetTy - translateY) * 0.2;
}

性能優化建議

  1. 只繪製可見瓦片:計算 startX/endX 範圍,避免全圖遍歷
  2. 瓦片圖集:多張小圖合併為一張大圖,用 drawRegion() 繪製,減少 drawImage 調用
  3. 避免頻繁 translate:邏輯層維護 cameraX/Y,paint() 中一次性 translate(-cameraX, -cameraY)
  4. 整數坐標:所有平移量保持整數,避免子像素渲染開銷

總結

掌握 translate 與 getTranslateX/Y 的配合,構建「世界座標 → 攝像機 → 屏幕座標」渲染管線,是從「單屏遊戲」進化為「大世界遊戲」的關鍵技術門檻。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
操作translate 變化視覺效果邊界循環條件
UP (向上移動視野)translateY -= tileH背景向下滾動translateY < -canvasH → 歸零
DOWNtranslateY += tileH背景向上滾動translateY > 0 → -canvasH
LEFTtranslateX -= tileW背景向右滾動translateX < -canvasW → 歸零
RIGHTtranslateX += tileW背景向左滾動translateX > 0 → -canvasW