專案結構
com.beancore
├── ui
│ ├── MainFrame.java # 主視窗、狀態機 (菜單/載入/遊戲/結束)
│ ├── GameLoadingPanel.java # 載入動畫面板
│ ├── GamePlayingPanel.java # 核心遊戲邏輯面板
│ ├── PopupMenuPanel.java # 開始菜單
│ ├── Top10ScorePanel.java # 排行榜
│ └── HelpDialog.java # 說明對話框
├── entity
│ ├── MyPlane.java # 玩家飛機
│ ├── EnemyPlane.java # 敵機 (小/中/大/BOSS)
│ ├── Bullet.java # 子彈
│ └── Score.java # 積分實體 (實現 Comparable)
├── util
│ ├── ImageLoader.java # 從大圖切割子圖
│ ├── FileUtil.java # 積分檔案讀寫
│ └── SoundPlayer.java # 音效播放 (javax.sound.sampled)
└── config
├── Config.java # 遊戲常數 (尺寸、速度、路徑)
└── ImageConstants.java # 圖集座標常量
核心類關鍵點
MainFrame - 狀態機
public class MainFrame extends JFrame implements ActionListener {
// 面板切換
private void popupMenuPanel() { switchPanel(popupMenuPanel); }
private void loadGame() { switchPanel(gameLoadingPanel); gameLoadingPanel.loadingGame(); }
private void startGame() {
switchPanel(gamePlayingPanel);
gamePlayingPanel.startGame();
// 遊戲主循環在 EDT 外執行
new Thread(() -> {
while (gamePlayingPanel.getMyPlane().isAlive()) {
try { Thread.sleep(Config.GAME_PANEL_REPAINT_INTERVAL); }
catch (InterruptedException e) { e.printStackTrace(); }
}
SwingUtilities.invokeLater(this::gameOver);
}).start();
}
}
GamePlayingPanel - 遊戲循環
public class GamePlayingPanel extends JPanel implements Runnable {
private MyPlane myPlane;
private List enemies = new CopyOnWriteArrayList<>();
private List bullets = new CopyOnWriteArrayList<>();
private int score = 0;
@Override public void run() {
while (myPlane.isAlive()) {
updateLogic(); // 移動、發射、碰撞、生成敵機
repaint(); // 觸發 paintComponent
try { Thread.sleep(Config.GAME_PANEL_REPAINT_INTERVAL); }
catch (InterruptedException e) { break; }
}
}
@Override protected void paintComponent(Graphics g) {
super.paintComponent(g);
// 繪製背景、玩家、敵機、子彈、分數、道具
g.drawImage(Images.SHOOT_BACKGROUND_IMG, 0, 0, null);
myPlane.draw(g);
enemies.forEach(e -> e.draw(g));
bullets.forEach(b -> b.draw(g));
drawScore(g);
}
private void updateLogic() {
// 鍵盤移動 myPlane
// 子彈移動、越界移除
// 敵機移動、越界移除、隨機生成
// 碰撞檢測: 矩形相交
// 道具拾取、BOSS 血量判定
}
}
ImageLoader - 圖集切割
public class ImageLoader {
private final BufferedImage source;
public ImageLoader(String path) throws IOException {
source = ImageIO.read(new File(path));
}
public BufferedImage getImage(int x, int y, int w, int h) {
return source.getSubimage(x, y, w, h);
}
}
// 使用: 從 shoot.png 切割飛機、子彈、爆炸等所有素材
積分持久化
public class FileUtil {
public static List readScore(String file) { /* 反序列化 */ }
public static void writeScore(List list, String file) { /* 序列化 */ }
}
// Score 實現 Comparable,按分數降序、時間升序排序
資源需求
shoot_background.png - 背景與載入動畫圖集
shoot.png - 遊戲角色圖集 (飛機、子彈、爆炸、道具)
font.png - 數字字體圖集
achievement.wav - 得分音效
編譯運行
# 需 JDK 8+
javac -d out -sourcepath src src/com/beancore/ui/MainFrame.java
java -cp out com.beancore.ui.MainFrame
擴展建議
- 引入
javafx.animation.AnimationTimer 替代手動循環,幀率更穩定。
- 使用實體組件系統 (ECS) 重構實體管理,支援更多敵機類型。
- 加入網路對戰:WebSocket 同步狀態。
- 關卡編輯器:JSON 定義敵機波次、BOSS 行為。