首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 多執行緒控制圖像分割重組:實現拼圖遊戲核心邏輯

     編輯:Java 程式設計入門

前言:Java 圖形處理與多執行緒的結合

Java AWT/Swing 提供了豐富的圖像處理 API (Image, Graphics, MediaTracker) 與內建多執行緒支援。結合兩者,可實現**靜態圖像動態化**效果:將一張完整圖片分割為多塊拼圖,利用執行緒控制動畫幀率、處理用戶交互 (拖拽、點擊)、實現拼圖遊戲邏輯。本文深度解析圖像分割算法、MediaTracker 異步載入監控、雙緩衝繪圖、遊戲主迴路執行緒模型、交互事件處理等核心技術。

一、圖像分割核心算法

1.1 參數定義


final int ROWS = 4;       // 行數
final int COLS = 5;       // 列數
final int TOTAL = ROWS * COLS; // 20 片
final int EMPTY_INDEX = TOTAL - 1; // 最後一片設為空白 (用於移動空位)

1.2 拼圖片段資料結構


class Piece {
    Image image;      // 該片段圖像
    int startX, startY; // 原始正確位置 (網格座標)
    int currX, currY;   // 當前顯示位置 (像素座標)
    int index;          // 0 ~ TOTAL-1

    Piece(Image img, int x, int y, int idx) {
        this.image = img;
        this.startX = x; this.startY = y;
        this.currX = x; this.currY = y; // 初始位置 = 正確位置 (或打亂)
        this.index = idx;
    }
    
    boolean isInCorrectPosition() {
        return currX == startX && currY == startY;
    }
}

1.3 圖像分割實現


// 假設 sourceImage 已載入完整圖片
int pieceW = sourceImage.getWidth(null) / COLS;
int pieceH = sourceImage.getHeight(null) / ROWS;

Piece[] pieces = new Piece[TOTAL];
for (int row = 0; row < ROWS; row++) {
    for (int col = 0; col < COLS; col++) {
        int idx = row * COLS + col;
        // 使用 Graphics 繪製子區域到新 Image (或用 PixelGrabber/CropImageFilter)
        Image pieceImg = createImage(new FilteredImageSource(
            sourceImage.getSource(),
            new CropImageFilter(col * pieceW, row * pieceH, pieceW, pieceH)
        ));
        int x = col * pieceW;
        int y = row * pieceH;
        pieces[idx] = new Piece(pieceImg, x, y, idx);
    }
}
// 將最後一片設為空白 (可創建透明圖像或標記)
pieces[EMPTY_INDEX].image = null; // 或透明圖

二、MediaTracker:異步圖像載入監控

圖像載入 (網路/磁碟) 是異步的。MediaTracker 確保繪圖前所有片段**完全載入**:


MediaTracker tracker = new MediaTracker(this); // Component 參數
for (int i = 0; i < TOTAL; i++) {
    if (pieces[i].image != null) {
        tracker.addImage(pieces[i].image, i); // ID = 索引
    }
}
try {
    tracker.waitForAll(); // 阻塞直到全部載入完成或出錯
    // 檢查錯誤
    for (int i = 0; i < TOTAL; i++) {
        if (tracker.isErrorID(i)) throw new RuntimeException("圖像 " + i + " 載入失敗");
    }
} catch (InterruptedException e) { Thread.currentThread().interrupt(); }

三、遊戲主迴路執行緒:固定幀率與雙緩衝


class GameThread extends Thread {
    private volatile boolean running = true;
    private final int TARGET_FPS = 30;
    private final long FRAME_TIME = 1000 / TARGET_FPS;

    @Override
    public void run() {
        while (running) {
            long start = System.currentTimeMillis();
            
            // 1. 更新遊戲狀態 (動畫、自動移動等)
            updateGameState();
            
            // 2. 繪圖 (雙緩衝)
            renderOffscreen();
            
            // 3. 翻頁顯示
            repaint(); // 觸發 paint() -> 繪製 offscreen 圖像
            
            // 4. 幀率控制
            long elapsed = System.currentTimeMillis() - start;
            long sleep = FRAME_TIME - elapsed;
            if (sleep > 0) {
                try { Thread.sleep(sleep); } catch (InterruptedException e) { running = false; }
            }
        }
    }
    
    void stopGame() { running = false; }
}

3.1 雙緩衝繪圖實現 (覆寫 paint/update)


private Image offscreenImage;
private Graphics offscreenGraphics;

public void update(Graphics g) { // 避免預設 update 清屏導致閃爍
    paint(g);
}

public void paint(Graphics g) {
    if (offscreenImage == null) {
        offscreenImage = createImage(getWidth(), getHeight());
        offscreenGraphics = offscreenImage.getGraphics();
    }
    
    // 1. 離屏清屏
    offscreenGraphics.setColor(getBackground());
    offscreenGraphics.fillRect(0, 0, getWidth(), getHeight());
    
    // 2. 繪製所有拼圖片段
    for (Piece p : pieces) {
        if (p.image != null) {
            offscreenGraphics.drawImage(p.image, p.currX, p.currY, this);
        }
    }
    
    // 3. 繪製網格線、提示文字等 UI
    drawGrid(offscreenGraphics);
    drawUI(offscreenGraphics);
    
    // 4. 一次性繪製到螢幕
    g.drawImage(offscreenImage, 0, 0, this);
}

四、用戶交互:滑鼠事件驅動拼圖移動


addMouseListener(new MouseAdapter() {
    @Override
    public void mousePressed(MouseEvent e) {
        int clickX = e.getX();
        int clickY = e.getY();
        
        // 找到點擊位置的片段 (非空白)
        for (Piece p : pieces) {
            if (p.image != null && 
                clickX >= p.currX && clickX < p.currX + pieceW &&
                clickY >= p.currY && clickY < p.currY + pieceH) {
                
                // 檢查是否與空白片相鄰 (上下左右)
                Piece empty = pieces[EMPTY_INDEX];
                if (isAdjacent(p, empty)) {
                    // 交換位置 (動畫或瞬間)
                    animateSwap(p, empty); // 或直接 swapPosition(p, empty)
                    repaint();
                }
                break;
            }
        }
    }
});

boolean isAdjacent(Piece a, Piece b) {
    return (Math.abs(a.currX - b.currX) == pieceW && a.currY == b.currY) ||
           (Math.abs(a.currY - b.currY) == pieceH && a.currX == b.currX);
}

void swapPosition(Piece a, Piece b) {
    int tx = a.currX, ty = a.currY;
    a.currX = b.currX; a.currY = b.currY;
    b.currX = tx; b.currY = ty;
}

五、勝利判定與遊戲狀態管理


boolean checkWin() {
    for (Piece p : pieces) {
        if (p.image != null && !p.isInCorrectPosition()) return false;
    }
    return true; // 所有非空白片段均在正確位置
}

// 在 updateGameState 或交互後調用
if (checkWin()) {
    gameThread.stopGame();
    showVictoryDialog(); // 彈窗、計時、重新開始按鈕
}

六、完整類別架構


public class PuzzleGame extends Frame implements Runnable {
    // 常數、Piece 內部類、pieces 陣列、MediaTracker
    // 雙緩衝 offscreenImage/Graphics
    // Thread gameThread
    
    public PuzzleGame() {
        // 載入圖片 -> 分割 -> MediaTracker 等待 -> 打亂順序 -> 啟動 gameThread
    }
    
    // run() 遊戲主迴路
    // update/paint 雙緩衝繪圖
    // mousePressed 交互邏輯
    // checkWin 勝利判定
    // main() 啟動
}

七、效能優化與擴展方向

  1. 移動動畫:animateSwap 使用補間算法 (線性/緩動) 在數幀內平滑過渡位置。
  2. 打亂算法:從終態隨機執行 N 步合法移動,保證可解 (拼圖可解性判定:逆序數奇偶性)。
  3. 圖片預處理:啟動時一次性分割並快取 BufferedImage 片段,避免運行時 CropImageFilter 開銷。
  4. Swing 遷移:繼承 JPanel、使用 SwingWorker 載入、Timer 驅動幀、paintComponent 繪圖、MouseListener 交互。
  5. JavaFX 版本:ImageView + TranslateTransition 動畫、AnimationTimer 遊戲迴路、CSS 樣式化。

結語

Java 多執行緒控制圖像分割重組,展示了 **AWT 圖形 API、MediaTracker 異步載入、雙緩衝繪圖、固定幀率遊戲迴路、事件驅動交互** 等核心客戶端技術的有機結合。從拼圖遊戲這一經典案例出發,掌握的圖像分割算法、座標映射、碰撞檢測、狀態機設計、動畫插值等技能,可直接應用於圖片編輯器、視覺化儀表板、教學軟體、休閒遊戲等廣泛領域。隨著 JavaFX、Swing、甚至 Web 技術 (Canvas/WebGL) 的發展,核心邏輯 (分割、狀態、渲染、交互) 永遠是圖形應用開發的不變基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved