| 方法 | 用途 |
setCell(col, row, tileIndex) | 設定單元格圖塊索引(0=透明) |
setPosition(x, y) | 整層平移,配合 paint(g) 實現硬體加速捲動 |
paint(Graphics g) | 一次繪製整層可見區域,內部優化裁剪 |
關鍵洞見: 不要逐格 drawImage,改用 setCell 修改邏輯矩陣 + setPosition 整層位移,效能提升 10 倍以上。
Terrain 類別架構
public class Terrain extends TiledLayer {
// 參數
public int milemeter = 0; // 飛行距離(分數)
public int speed = 0; // 捲動速度(幀間隔),主選單可調
private int timestamp = 0; // 幀計數器
private int tileW, tileH; // 圖塊尺寸
private int[][] gameMap; // 邏輯地圖 [Rows][Cols]
private int[] indexMap; // 行映射:實現 O(1) 捲動
private Random random;
private Helicopter player; // 碰撞檢測參考
// 地形狀態
private int upBound, lowBound; // 上下邊框邊界(列索引)
private int wallHeight; // 障礙高度(格數)
private int wallPos; // 障礙水平位置
private boolean up_down; // 地形上升/下降
private int alterSpeed, alterExtent; // 變化速度/持續格數
private int layerTop; // 視覺平滑捲動偏移
}
構造函數:初始化邏輯矩陣與行映射
public Terrain(int cols, int rows, Image img, int tileW, int tileH, Helicopter player) {
super(cols, rows, img, tileW, tileH); // 父類建構:建立圖塊集
this.player = player; this.tileW = tileW; this.tileH = tileH;
this.screenW = getWidth(); this.screenH = getHeight();
this.Rows = rows; this.Cols = cols;
random = new Random(); layerTop = -tileH;
// 1. 行映射陣列:indexMap[i] = 邏輯行號 → 物理行 i 顯示哪一行邏輯資料
indexMap = new int[Rows];
for (int i=0; i= Cols - Cols/7) gameMap[r][c] = 1; // 磚塊索引 1
// 3. 同步到 TiledLayer
for (int r=0; r
行映射技巧:O(1) 捲動的核心
傳統做法:每幀將 gameMap 所有行向上搬移一格 → O(Rows×Cols)。
indexMap 做法:只搬移索引陣列 → O(Rows)。
// 每幀更新時
for (int i=0; i
核心更新邏輯 update():地形演化與障礙生成
public void update() {
if (++timestamp > speed) { // 速度控制:每 speed 幀更新一次
timestamp = 0;
if (layerTop == 0) { // 整格捲動完成,處理邏輯
milemeter++; // 分數+1
// 1. 清空最新行(即將進入畫面的行)中間區域
for (int c=upBound+1; c 0) { // 正在變化中
if (up_down) { upBound -= alterSpeed; lowBound -= alterSpeed; } // 上升
else { upBound += alterSpeed; lowBound += alterSpeed; } // 下降
// 邊界夾擠保護
if (upBound < 0) { upBound += alterSpeed; lowBound += alterSpeed; }
if (lowBound >= Cols-1) { upBound -= alterSpeed; lowBound -= alterSpeed; }
// 依新邊界填充牆壁
for (int c=0; c= lowBound) gameMap[indexMap[Rows-1]][c] = 1;
alterExtent--;
} else { // 產生新變化
alterExtent = Math.abs(random.nextInt() % 10);
if (alterExtent == 0) alterExtent = 5; // 避免 0
up_down = (random.nextInt() % 2 == 0); // 隨機上升/下降
alterSpeed = Math.abs(random.nextInt()) % 3 + 1; // 1~3 格/步
// 注意:此處 return,新變化下一幀才開始生效
return;
}
// 3. 定期生成障礙(每 10 米一個)
if (milemeter % 10 == 0) {
wallPos = Math.abs(random.nextInt()) % (Cols - wallHeight);
for (int c=0; c
演算法關鍵點解析
- 雙層速度控制:
speed 控制邏輯更新頻率(難度),layerTop++ 控制視覺平滑度(每幀 1 像素)。
- 地形變化狀態機:
alterExtent 剩餘步數,up_down 方向,alterSpeed 步長。邊界夾擠防止超出螢幕。
- 障礙生成規則: 固定間距(10 米)、隨機水平位置、固定高度,確保可通過縫隙存在。
- 只更新可見區:
setCell 僅針對物理第 0 行,其餘行靠 setPosition 整層位移,極致效能。
碰撞檢測:矩形與網格的高效交集
public boolean collides() {
Rect pr = player.getPlayerRect(); // 像素座標
// 轉換為網格座標範圍
int r1 = pr.top / tileH, r2 = pr.bottom / tileH;
int c1 = pr.left / tileW, c2 = pr.right / tileW;
for (int r=r1; r<=r2; r++)
for (int c=c1; c<=c2; c++)
if (gameMap[indexMap[r]][c] == 1) return true; // 撞到磚塊
return false;
}
- 僅檢測直升機包圍盒覆蓋的格子(最多 3×4 格),O(1)。
- 利用
indexMap 直接查詢邏輯地圖,無需反推視覺座標。
效能與記憶體分析
| 指標 | 數值 | 說明 |
| 邏輯地圖大小 | Rows × Cols × 4 bytes | 例:10×20 = 800 bytes |
| 索引映射 | Rows × 4 bytes | 40 bytes |
| 每幀 setCell 次數 | Cols | 僅更新 1 行,極省 CPU |
| 碰撞檢測複雜度 | O(覆蓋格數) | 常數級 |
擴展建議:從硬編碼到數據驅動
// 難度曲線數據化
class LevelConfig {
int baseSpeed; // 捲動速度
int terrainVolatility; // alterExtent 範圍
int obstacleFrequency; // 米數間隔
int obstacleHeight; // 格數
int gapMinWidth; // 最小通道寬度
}
// Terrain 持有 LevelConfig,關卡切換只需替換配置物件
總結
Terrain 展示了在 J2ME 環境下,如何以 TiledLayer + 環狀索引映射 + 狀態機地形演化 實現商業級無限捲動場景。其核心智慧在於:
- 邏輯與視覺解耦:
gameMap 存邏輯,indexMap 控捲動,layerTop 控平滑。
- 最小化繪圖指令: 單行
setCell + 整層 setPosition,零 drawImage 迴圈。
- 程序化內容可控: 參數化地形波動、障礙頻率、通道寬度,支援難度曲線設計。
此架構可直接移植至現代引擎:Tilemap (Unity/Godot) + Circular Buffer 行索引 + Coroutine 地形生成器,思維完全通用。
- 上一篇文章: J2ME 精靈系統深度封裝:從 Sprite 到 GameObject 再到 Helicopter 組合模式
- 下一篇文章: J2ME RMS 從入門到精通(一):RecordStore 核心機制、存儲限制與實戰測試工具
Copyright ©
程式碼之家 All Rights Reserved