Java 遊戲開發最大敵人是 GC 停頓、繪圖效率、記憶體佔用。本文提煉 10 條必遵鐵律,涵蓋渲染架構、資源管理、遊戲循環、並發模型,助你用 Java 寫出媲美 C++/Unity 的流暢體驗。
CardLayout 切換 Panel、add/remove Canvas 實現場景轉換 → 閃爍、延遲、記憶體抖動。paintComponent 統一繪製當前幀。// 遊戲主面板
public class GameCanvas extends JPanel {
private GameState currentState; // MENU, PLAYING, PAUSE, GAME_OVER
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
// 委派給當前狀態的渲染器
if (currentState != null) currentState.render(g2d);
}
public void changeState(GameState newState) {
this.currentState = newState;
repaint(); // 單一入口觸發重繪
}
}
Renderer (繪圖器) 和 InputHandler (監聽器) 介面。Renderer + InputHandler 實例,零閃爍、零冗餘代碼。public interface Renderer { void render(Graphics2D g, GameContext ctx); }
public interface InputHandler { void handleInput(KeyEvent e, GameContext ctx); }
// 具體場景實現
public class MenuRenderer implements Renderer { ... }
public class BattleRenderer implements Renderer { ... }
// 切換極簡
canvas.setRenderer(new BattleRenderer());
canvas.setInputHandler(new BattleInputHandler());
static final 載入,避免重複 new Image()。WeakHashMap 或 Caffeine 緩存,離開關卡自動回收。Image.flush()、AudioClip.close()、BufferStrategy.dispose(),離開場景顯式調用。不要用 Swing Timer 或無限 while(true) 佔死 CPU。
public class GameLoop implements Runnable {
private static final int TARGET_FPS = 60;
private static final long NANO_PER_FRAME = 1_000_000_000L / TARGET_FPS;
private volatile boolean running = false;
@Override
public void run() {
long lastTime = System.nanoTime();
double delta = 0;
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / (double) NANO_PER_FRAME;
lastTime = now;
if (delta >= 1) {
update(); // 邏輯更新 (固定步長)
repaint(); // 請求繪製 (由 EDT 執行)
delta--;
}
// 睡眠讓出 CPU,避免 100% 佔用
try { Thread.sleep(1); } catch (InterruptedException ignored) {}
}
}
private void update() { /* 物理、AI、碰撞、狀態機 */ }
}
關鍵:邏輯更新 (Fixed Step) 與渲染 (Variable Step) 解耦,支援插幀插值平滑畫面。
drawPolygon、drawArc、漸變、濾鏡極耗 CPU。BufferedImage / Sprite Sheet,運行時僅 drawImage。repaint(x,y,w,h) 精準失效repaint() 觸發完整 paintComponent,大尺寸高解析度極慢。repaint(bounds)。BufferStrategy 雙緩衝,單幀繪製開銷降至毫秒級。// JPanel 內啟用
createBufferStrategy(2); // 雙緩衝
// 或 3 (三緩衝,減少等待 GPU 顯示)
public void renderFrame() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) { createBufferStrategy(3); return; }
Graphics2D g = (Graphics2D) bs.getDrawGraphics();
try {
// 1. 清屏
g.setColor(Color.BLACK); g.fillRect(0,0,getWidth(),getHeight());
// 2. 繪製遊戲內容
currentState.render(g);
// 3. 繪製 HUD/FPS
drawDebugInfo(g);
} finally {
g.dispose(); // 原則 9:必須 dispose
}
bs.show(); // 翻轉緩衝區顯示
// Toolkit.getDefaultToolkit().sync(); // 可選:強制同步顯示管線
}
| 特性 | Swing (JFC) | AWT / SWT |
|---|---|---|
| 繪製基礎 | Java2D 軟體繪製 (CPU) | 原生 OS 繪製管線 (GPU 加速機會大) |
| 組件重量 | 重量級、邊框、Look&Feel 開銷 | 輕量級、直接對應原生 Widget |
| 遊戲適配性 | 需關閉雙緩衝、禁用重繪優化、繞過 UIManager | 原生 Canvas/Shell 即是遊戲畫布 |
推薦:LWJGL 3 / LibGDX / JavaFX (Canvas) 直接綁定 OpenGL/Vulkan/Metal,繞過 Java2D 瓶頸。純 Java2D 必用 Canvas (AWT) 而非 JPanel (Swing)。
Graphics 持有原生設備上下文 (HDC/GDI+/CGContext),不受 GC 管理。createBufferStrategy 取得的 Graphics、BufferedImage.createGraphics()、VolatileImage.getGraphics() 均需 finally { g.dispose(); }。OutOfMemoryError: Native memory 或繪圖凍結。Vector2、Rectangle、Particle,消除臨時物件分配。VarHandle / Unsafe (內部 API) 做無鎖狀態更新 (極少數極端場景)。| 框架 | 渲染後端 | 優勢 | 學習曲線 |
|---|---|---|---|
| LibGDX | OpenGL (LWJGL 3) | 跨平台(桌面/Android/Web/iOS)、生態豐富、2D/3D 通吃、箱庭/Spine 整合 | 中 |
| FXGL | JavaFX (Prism/Glass) | 純 Java、模組化、Entity-Component 系統、支援 GraalVM Native | 低 |
| LWJGL 3 | OpenGL/Vulkan/Metal/OpenAL | 最薄封裝、完全控制、高性能 3D/計算著色器 | 高 |
| jMonkeyEngine | OpenGL (LWJGL) | 3D 場景圖、物理、動畫、編輯器 SDK | 中高 |
結語:堅持這 10 條鐵律,配合 LibGDX/FXGL 等現代框架,Java 完全能支撐商業級 2D/2.5D 遊戲。3A 級 3D 仍建議 C++/Unreal/Unity,但 Java 在獨立遊戲、教學原型、伺服器端遊戲邏輯、工具鏈開發上擁有不可替代的生產力優勢。