透過一個「行星繞太陽公轉」的 Swing 小遊戲,示範封裝、繼承、多型、工具類抽取、遊戲迴圈等核心 OOP 概念在實際專案中的落地。本文附完整原始碼結構、關鍵類別解析、執行緒動畫原理與擴充指南。
src/
├── util/ # 通用基礎設施層
│ ├── Constant.java # 遊戲常數 (寬/高)
│ ├── GameUtil.java # 圖片載入工具 (單例模式)
│ └── MyFrame.java # 視窗框架基類 (含重繪執行緒)
└── solar/ # 業務邏輯層
├── Star.java # 恆星/天體基類 (座標、圖片、繪製)
├── Planet.java # 行星類 (橢圓軌道、公轉邏輯)
├── SolarFrame.java # 主遊戲面板 (組合太陽、地球、火星)
└── Test.java # 啟動入口
Constant.java — 集中管理常數,避免魔術數字散落。
package util;
public class Constant {
public static final int GAME_WIDTH = 800;
public static final int GAME_HEIGHT = 600;
}
GameUtil.java — 統一圖片載入,支援 classpath 資源,避免重複 ImageIO.read 邏輯。
package util;
public class GameUtil {
private GameUtil() {} // 工具類私有建構子
public static Image getImage(String path) {
URL u = GameUtil.class.getClassLoader().getResource(path);
try { return ImageIO.read(u); } catch (IOException e) { e.printStackTrace(); return null; }
}
}
MyFrame.java — 封裝 JFrame 建立、置中、置頂、關閉操作,**核心是內部類 PaintThread 實現定時重繪 (約 25 FPS)**。
class PaintThread extends Thread {
public void run() {
while (true) {
repaint(); // 觸發 paint(Graphics)
try { Thread.sleep(40); } catch (InterruptedException e) { e.printStackTrace(); }
}
}
}
Star.java — 所有天體的父類,封裝座標 (x,y)、圖片 img、寬高 w/h、通用 draw(Graphics)。
Planet.java extends Star — 新增橢圓軌道參數:
longAxis、shortAxis:長/短半軸。speed:角速度 (弧度/幀)。degree:當前角度。center:被環繞的恆星參考點。關鍵方法:
public void move() {
x = center.x + longAxis * Math.cos(degree);
y = center.y + shortAxis * Math.sin(degree);
degree += speed;
}
public void drawTrace(Graphics g) { // 畫軌道橢圓
g.drawOval((int)(center.x - longAxis), (int)(center.y - shortAxis),
(int)(2*longAxis), (int)(2*shortAxis));
}
@Override public void draw(Graphics g) {
super.draw(g); // 畫行星本體
drawTrace(g); // 畫軌道
move(); // 更新位置
}
SolarFrame.java extends MyFrame — 組合場景:
public class SolarFrame extends MyFrame {
Star sun = new Star("images/sun.jpg", width, height);
Planet earth = new Planet(sun, "images/earth.png", 100, 60, 0.1);
Planet mars = new Planet(sun, "images/mars.png", 180, 100, 0.15);
@Override public void paint(Graphics g) {
g.drawImage(bg, 0, 0, null);
sun.draw(g);
earth.draw(g);
mars.draw(g);
}
}
PaintThread 週期性呼叫 repaint(),而非依賴系統事件佇列,掌控幀率。JPanel 預設開啟雙重緩衝,減少閃爍。move() 更新狀態,draw() 只負責繪製,便於未來引入固定時間步長。move() 目前在 draw() 內呼叫,教學簡化;生產建議獨立 update() 並在固定時間步長執行。GameUtil.getImage 每次載入都讀磁碟,建議改用 Map<String, Image> 快取。assets/images/ 放入行星圖片。SolarFrame 增加一行:Planet venus = new Planet(sun, "images/venus.png", 140, 80, 0.08);paint() 增加:venus.draw(g);得益於「父類封裝通用屬性/方法、子類只關心差異參數」的設計,**零修改現有類別**即可擴充。
| 問題 | 改進方案 |
|---|---|
| 圖片路徑硬編碼、打包後找不到 | 改用 getClass().getResource("/images/xxx.png") 載入 classpath 資源 |
| 幀率不穩、CPU 佔用高 | 引入 BufferStrategy + 固定時間步長遊戲迴圈 |
| 無法暫停/加速/儲存狀態 | 抽取 GameLoop 介面,狀態序列化至 JSON |
| 只有圓/橢圓軌道,無萬有引力模擬 | 引入物理引擎 或自行實作牛頓萬有引力 |
這個小遊戲雖簡單,却完整展示了 **「高內聚、低耦合、單一職責、開放封閉」** 的 OOP 精髓:
util 層屏蔽平台細節 (視窗、圖片、常數)。solar 層專注領域模型 (天體、軌道、場景組合)。Star → Planet,多型體現在 draw() 統一介面。以此為起點,逐步引入狀態機、物理引擎、場景管理、資源池,即可演進為完整 2D 遊戲引擎雛形。