MIDP Graphics.translate(x, y) 實現坐標系平移,配合 getTranslateX/Y() 獲取當前偏移,是實現大地圖滾動、攝像機跟隨、UI 震動等效果的基礎。本文解析標準平移模式與無限循環滾動背景實現。
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);
}
}
| 操作 | translate 變化 | 視覺效果 | 邊界循環條件 |
|---|---|---|---|
| UP (向上移動視野) | translateY -= tileH | 背景向下滾動 | translateY < -canvasH → 歸零 |
| DOWN | translateY += tileH | 背景向上滾動 | translateY > 0 → -canvasH |
| LEFT | translateX -= tileW | 背景向右滾動 | translateX < -canvasW → 歸零 |
| RIGHT | translateX += tileW | 背景向左滾動 | translateX > 0 → -canvasW |