「框架是汽車,引擎是引擎」。框架決定平台邊界(如 MIDP 1.0/2.0、螢幕尺寸),引擎提供可復用的核心功能(資源管理、渲染、腳本)。引擎需適配框架,不可混淆。抽象設計與具體類別結構不一一對應,關鍵在於職責分離。
在有限 Heap(如 100 KB)下運行大型遊戲(細胞分裂、古墓奇兵),資源調度是生死線。管理器需解決:
反編譯發現其資源包採用 索引表 + 連續資料區 格式:
// 索引表結構(包頭)
struct IndexEntry {
int offset; // 資料在包中的絕對偏移量
int size; // 資料長度
}
// 包檔案佈局:
// [檔案數: 2 bytes][IndexEntry × N][資料區...]
// 載入索引表到記憶體(僅一次)
private boolean loadIndex(String resName) {
try {
InputStream is = getClass().getResourceAsStream(resName);
int count = (is.read() & 0xFF) | ((is.read() & 0xFF) << 8); // 檔案數
offsets = new int[count];
sizes = new int[count];
int headerSize = 2 + count * 4; // 索引表大小
for (int i = 0; i < count; i++) {
// 每個 offset 佔 4 bytes (big-endian)
offsets[i] = (is.read() << 24) | ((is.read() & 0xFF) << 16) |
((is.read() & 0xFF) << 8) | (is.read() & 0xFF);
if (i > 0) sizes[i-1] = offsets[i] - offsets[i-1]; // 差值即長度
}
sizes[count-1] = (int)(is.available() - (offsets[count-1] - headerSize));
is.close();
return true;
} catch (Exception e) { return false; }
}
// 按需讀取單一資源
private byte[] loadResource(int index) {
if (index < 0 || index >= offsets.length) return null;
try {
InputStream is = getClass().getResourceAsStream(resName);
is.skip(offsets[index] + headerSize); // 直接跳轉
byte[] data = new byte[sizes[index]];
is.read(data);
is.close();
return data;
} catch (Exception e) { return null; }
}
頻繁 skip/read 會殺死 CPU。改進:loadResources(int[] indices) 一次讀取連續區塊,拆分後快取。若 Heap 允許,關鍵資源(主角、常用 UI)預載常駐;大型資源(地圖、過場動畫)按關卡載入、離開即釋放。
System.gc()。最可靠、效能最好,雖違背「自動化」理想。load(index)、loadBatch(indices)、release(index)、releaseAll()。場景管理器雖名為「管理」,實為「資源調度器」。在 J2ME 受限環境下,精確控制 I/O 與記憶體 遠比抽象的管理介面重要。下章將探討 Camera 與渲染器的輕量實作。