首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Java 接蘋果小遊戲:雙執行緒協作模式解決球籃同步繪製衝突

     編輯:程式設計綜合問答

問題核心

單執行緒 `paintComponent` 同時負責:

  1. 定時器驅動蘋果下落 (`repaint` 觸發重繪)。
  2. 鍵盤/按鈕驅動籃子移動 (`repaint` 觸發重繪)。

**衝突**:籃子移動頻繁 `repaint` 會打斷蘋果下落的固定時間間隔,導致蘋果速度忽快忽慢。

解決架構:模型-視圖分離 + 雙執行緒

┌─────────────────────────────────────┐
│         Game Model (非 GUI)         │
│  - Apple: x, y, vy, active          │
│  - Basket: x, width                 │
│  - score, state                     │
│  - update(deltaTime)  ← 邏輯更新    │
└──────────────┬──────────────────────┘
               │ 狀態查詢 (線程安全)
               ▼
┌─────────────────────────────────────┐
│         View (JPanel)               │
│  - paintComponent(g)  ← 只讀模型繪製 │
│  - 固定幀率 ~60FPS 重繪             │
└─────────────────────────────────────┘

核心代碼結構


// 1. 遊戲模型 (獨立類,無 Swing 依賴)
class GameModel {
    Apple apple = new Apple();
    Basket basket = new Basket();
    int score = 0;
    final Object lock = new Object(); // 簡單同步
    
    void update(double dt) {
        synchronized(lock) {
            apple.fall(dt);
            if (basket.catches(apple)) {
                score++;
                apple.reset();
            }
        }
    }
    
    // 給 View 讀取的快照方法
    Apple getApple() { synchronized(lock) { return apple.copy(); } }
    Basket getBasket() { synchronized(lock) { return basket.copy(); } }
    int getScore() { synchronized(lock) { return score; } }
}

// 2. 邏輯執行緒 (固定時間步)
class GameLoop implements Runnable {
    GameModel model;
    volatile boolean running = true;
    public void run() {
        long last = System.nanoTime();
        while (running) {
            long now = System.nanoTime();
            double dt = (now - last) / 1e9;
            last = now;
            model.update(dt);
            try { Thread.sleep(16); } catch (InterruptedException ignored) {}
        }
    }
}

// 3. 視圖面板 (只讀模型繪製)
class GamePanel extends JPanel {
    GameModel model;
    public GamePanel(GameModel m) { this.model = m; }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Apple a = model.getApple();      // 獲取快照
        Basket b = model.getBasket();
        g.setColor(Color.RED);
        g.fillOval(a.x, a.y, a.size, a.size);
        g.setColor(Color.BLUE);
        g.fillRect(b.x, getHeight()-50, b.width, 30);
        g.drawString("Score: " + model.getScore(), 10, 20);
    }
}

// 4. 主程式啟動
public class AppleGame {
    public static void main(String[] args) {
        GameModel model = new GameModel();
        GamePanel panel = new GamePanel(model);
        JFrame frame = new JFrame("Catch Apple");
        frame.add(panel);
        frame.setSize(400, 600);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        
        // 啟動邏輯執行緒
        new Thread(new GameLoop(model)).start();
        
        // 視圖固定幀率重繪 (Swing Timer 在 EDT 執行)
        new Timer(16, e -> panel.repaint()).start();
        
        // 鍵盤控制籃子 (更新模型)
        frame.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_LEFT) model.getBasket().moveLeft();
                if (e.getKeyCode() == KeyEvent.VK_RIGHT) model.getBasket().moveRight();
            }
        });
    }
}

關鍵設計要點

要點說明
模型無 Swing 依賴可單元測試、可移植、職責單一
固定時間步 `dt`物理模擬穩定,不受幀率波動影響
View 只讀模型快照避免併發修改異常,`synchronized` 或 `copy()` 保證一致性
Swing Timer 驅動重繪在 EDT 執行,符合 Swing 線程規則
輸入事件更新模型鍵盤/滑鼠盡快更新模型,視圖下一幀自動反映

進階優化

  • 使用 `volatile` 或 `AtomicReference` 替代 `synchronized` 降低鎖開銷。
  • 引入雙緩衝 `BufferStrategy` 消除閃爍 (JPanel 預設雙緩衝)。
  • 遊戲狀態機 (菜單/運行/暫停/結束) 管理生命週期。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved