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

J2ME 雙人掃雷遊戲完整實作(二):核心邏輯與界面繪製

     編輯:J2ME

前言

承接上一篇,本文將深入解析《雙人掃雷 1.0》的核心程式碼實作。我們將涵蓋幫助界面、主遊戲畫面繪製、關鍵遊戲邏輯(佈雷、遞歸展開、勝負判定)、遊戲中選單,以及最終的程式碼整合。這是一個基於 MIDP 1.0 的完整雙人對戰掃雷範例,適合初學者研究 J2ME 遊戲開發的基礎架構。

一、幫助界面實作

幫助界面採用簡單的文字陣列繪製方式。將說明文字預先存入 String[],在 paintHelpScreen() 中逐行繪製。若內容超出螢幕,可透過按鍵翻頁(實際上只是重繪陣列的不同區段)。

// MiningCanvas.java 成員變數
final String[] strGamehelp = new String[10];

public MiningCanvas(MiningMIDlet miningMIDlet) {
    // ...
    strGamehelp[0] = "此遊戲為雙人對戰遊戲";
    strGamehelp[1] = "與經典掃雷不同,這";
    strGamehelp[2] = "個遊戲中,我們要挖出";
    strGamehelp[3] = "雷,挖錯雷則交換玩家。";
    strGamehelp[4] = "一共有 52 顆雷,256 個格";
    strGamehelp[5] = "子,最先挖到 27 顆雷者";
    strGamehelp[6] = "獲勝。1. 上、下、左、右分";
    strGamehelp[7] = "別為數字鍵 2、8、4、6,挖";
    strGamehelp[8] = "雷為 5;2. 螢幕外框的顏";
    strGamehelp[9] = "色為當前玩家;";
}

private void paintHelpScreen(Graphics g) {
    g.setColor(0x00FFFFFF);
    g.fillRect(0, 0, canvasW, canvasH);
    g.setFont(lowFont);
    g.setColor(0x00000000);
    for (int i = 0; i < strGamehelp.length; i++) {
        g.drawString(strGamehelp[i], 5, 5 + (lowFont.getHeight() + 3) * i, Graphics.TOP | Graphics.LEFT);
    }
}

// keyPressed 中處理幫助狀態
case GAMESTATE_HELP:
    gamestate = GAMESTATE_MENU; // 任意鍵返回主選單
    break;

二、主遊戲界面繪製架構

主畫面分為四大區塊:

  1. 雷區棋盤:根據 Bomb[][] 陣列狀態繪製(未翻開、已翻開數字、玩家 1/2 的雷)。
  2. 玩家選擇框:標示目前游標位置。
  3. 資訊欄:顯示雙方分數、剩餘雷數、小地圖。
  4. 回合提示:外框顏色與旗幟圖示指示當前玩家。

關鍵座標與捲軸機制

  • 完整雷區 16×16,螢幕僅顯示 10×12,透過 paintX、paintY 控制可視區域左上角在雷區中的座標。
  • 選擇框座標 (selectedX, selectedY) 若超出可視範圍,即調整 paintX/Y 使其回到視野內。
  • 注意:程式中座標 (x, y) 對應陣列 bombs[y][x],讀寫時務必留意行列互換。

邊界保護陣列

bombs 宣告為 new Bomb[18][18](miningMapGrid+2),真實雷區位於索引 1~16,外圍一圈作為哨兵,避免周邊檢查時發生 ArrayIndexOutOfBoundsException。

核心繪製程式碼節選

// 遊戲主畫面繪製入口
private void paintGameScreen(Graphics g) {
    paintPlayer(g, isPlayer1);   // 外框與旗幟
    paintMiningMap(g);           // 雷區
    paintInfo(g);                // 資訊欄
    paintSelected(g);            // 選擇框
}

// 繪製雷區核心邏輯
public void paintMiningMap(Graphics g) {
    for (int i = 0; i < bombMapH; i++) {
        for (int j = 0; j < bombMapW; j++) {
            Bomb b = bombs[i + paintY + 1][j + paintX + 1];
            int dx = miningMapX + j * miningGridLength;
            int dy = miningMapY + i * miningGridLength;
            if (!b.hasFound) {
                g.drawImage(unFoundGroundImg, dx, dy, Graphics.TOP | Graphics.LEFT);
            } else if (!b.isBomb) {
                // 依周圍雷數繪製 0~8 圖片
                switch (b.bombaround) {
                    case 0: g.drawImage(foundGroundImg, dx, dy, Graphics.TOP | Graphics.LEFT); break;
                    case 1: g.drawImage(bomb1Img, dx, dy, Graphics.TOP | Graphics.LEFT); break;
                    // ... case 2~7 同理
                    default: g.drawImage(foundGroundImg, dx, dy, Graphics.TOP | Graphics.LEFT); break;
                }
            } else {
                // 已翻開且是雷:顯示屬於哪位玩家
                g.drawImage(b.isPlayer1 ? player1BombImg : player2BombImg, dx, dy, Graphics.TOP | Graphics.LEFT);
            }
        }
    }
}

三、遊戲核心邏輯

1. 佈雷初始化 bombInit()

建立 18×18 的 Bomb 物件陣列,隨機在 1~16 範圍內放置 52 顆雷,並同步更新周圍 8 格的 bombaround 計數器。

public void bombInit() {
    int bombindex = 0;
    Random random = new Random();
    // 初始化所有 Bomb 物件
    for (int i = 0; i < miningMapGrid + 2; i++)
        for (int j = 0; j < miningMapGrid + 2; j++)
            bombs[i][j] = new Bomb();

    while (bombindex < bombNum) {
        int x = Math.abs(random.nextInt() % 16) + 1;
        int y = Math.abs(random.nextInt() % 16) + 1;
        if (!bombs[x][y].isBomb) {
            try {
                bombs[x][y].isBomb = true;
                bombindex++;
                // 更新周圍 8 格計數
                bombs[x-1][y].bombaround++;
                bombs[x-1][y-1].bombaround++;
                bombs[x][y-1].bombaround++;
                bombs[x+1][y-1].bombaround++;
                bombs[x+1][y].bombaround++;
                bombs[x+1][y+1].bombaround++;
                bombs[x][y+1].bombaround++;
                bombs[x-1][y+1].bombaround++;
            } catch (ArrayIndexOutOfBoundsException e) {}
        }
    }
}

2. 遞歸展開 bombOut(x, y)

經典掃雷規則:點擊無雷且周圍無雷的格子,自動展開相連的安全區域及其邊緣一圈。

public void bombOut(int x, int y) {
    if (!bombs[x][y].hasFound && x >= 1 && y >= 1 && x <= miningMapGrid && y <= miningMapGrid) {
        bombs[x][y].hasFound = true;
        if (bombs[x][y].bombaround == 0) {
            bombOut(x-1, y);
            bombOut(x-1, y-1);
            bombOut(x, y-1);
            bombOut(x+1, y-1);
            bombOut(x+1, y);
            bombOut(x+1, y+1);
            bombOut(x, y+1);
            bombOut(x-1, y+1);
        }
    }
}

3. 勝負判定 checkWin()

任一方挖到 27 顆雷(超過一半)即獲勝,彈出 Alert 並自動重置遊戲。

public void checkWin() {
    if (player1Found > 26) { winString = "Player1 has won!"; sbWon = true; }
    if (player2Found > 26) { winString = "Player2 has won!"; sbWon = true; }
    if (sbWon) {
        winAlert = new Alert("", winString + "\nPlayer1 " + player1Found + " : " + player2Found + " Player2", null, AlertType.INFO);
        gameInit(); // 重置下一局
        winAlert.setTimeout(Alert.FOREVER);
        MiningMIDlet.display.setCurrent(winAlert);
    }
}

4. 遊戲重置 gameInit()

重置所有狀態變數、分數、選擇框位置、可視區域,並呼叫 bombInit() 重新佈雷。

四、按鍵處理與遊戲狀態機

遊戲採用狀態機設計:GAMESTATE_MENU、GAMESTATE_HELP、GAMESTATE_GAMEING、GAMESTATE_GAMEMENU 等。在 keyPressed() 的 switch(gamestate) 中分派處理。

遊戲中按鍵邏輯(GAMESTATE_GAMEING)

case GAMESTATE_GAMEING:
    if (keyCode == FullCanvas.KEY_SOFTKEY1) {
        gamestate = GAMESTATE_GAMEMENU;
    } else if (action == FullCanvas.LEFT) {
        selectedX = (--selectedX + miningMapGrid) % miningMapGrid;
    } else if (action == FullCanvas.RIGHT) {
        selectedX = (++selectedX) % miningMapGrid;
    } else if (action == FullCanvas.UP) {
        selectedY = (--selectedY + miningMapGrid) % miningMapGrid;
    } else if (action == FullCanvas.DOWN) {
        selectedY = (++selectedY) % miningMapGrid;
    } else if (action == FullCanvas.FIRE) {
        Bomb target = bombs[selectedY+1][selectedX+1];
        if (!target.hasFound) {
            if (target.isBomb) {
                // 挖到雷:得分、標記歸屬、切換玩家
                target.hasFound = true;
                target.isPlayer1 = isPlayer1;
                if (isPlayer1) player1Found++; else player2Found++;
                bombLeft--;
                checkWin();
            } else {
                // 未挖到雷:遞歸展開並交換玩家
                bombOut(selectedY+1, selectedX+1);
                isPlayer1 = !isPlayer1;
            }
        }
    }
    // 視野跟隨選擇框
    if ((selectedX - paintX) < 0) paintX = selectedX;
    else if ((selectedX - paintX) >= bombMapW) paintX = selectedX - bombMapW + 1;
    if ((selectedY - paintY) < 0) paintY = selectedY;
    else if ((selectedY - paintY) >= bombMapH) paintY = selectedY - bombMapH + 1;
    break;

五、遊戲中選單

提供「返回遊戲、重新開始、幫助、主選單、退出」五個選項。結構與主選單類似,此處略述。已知問題:從幫助界面返回會直接跳至主選單,可透過增加狀態變數修復。

六、總結與擴展方向

至此,雙人掃雷的完整邏輯已全部呈現。雖然核心簡單,但已具備完整遊戲循環。後續可擴展:

  • 加入音效(參考 Nokia Sound API 或 MMAPI)
  • 實作藍牙對戰(JSR-82)
  • 優化繪製效能(雙緩衝、髒矩形更新)
  • 增加 AI 單人模式

原作者 yinowl 於 2005 年發布,雖屬早期 J2ME 範例,但其狀態機、座標映射、遞歸展開等設計模式至今仍具參考價值。

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