鴻蒙掃雷游戲的核心架構設計
鴻蒙OS掃雷游戲采用了MVC(模型-視圖-控制器)的架構思想,將游戲邏輯與UI展示分離,使得代碼結構清晰且易于維護。整個游戲由以下幾個核心部分構成:
數據模型設計
游戲的基礎數據模型是Cell
類,它代表掃雷游戲中的每個方塊,包含位置信息、地雷狀態、相鄰地雷數量等關鍵屬性:
@ObservedV2
class Cell {row: number; // 行坐標column: number; // 列坐標hasMine: boolean; // 是否包含地雷neighborMines: number; // 相鄰地雷數量@Trace isFlag: boolean; // 是否已標記@Trace value: string; // 顯示值(數字或"雷")constructor(row: number, column: number) {this.row = row;this.column = column;this.value = '';}
}
@ObservedV2
裝飾器使Cell
類具備數據觀察能力,當屬性變化時能自動通知視圖更新,這是鴻蒙OS聲明式UI的核心特性之一。@Trace
裝飾器則用于追蹤屬性變化,幫助開發者調試數據流向。
游戲狀態管理
游戲的狀態管理集中在Index
組件中,通過@State
裝飾器管理界面相關狀態,確保數據變化時UI能自動刷新:
@Entry
@Component
struct Index {@State private gameBoard: Cell[][] = []; // 游戲面板@State private mineCount: number = 10; // 地雷數量@State private revealedCells: Set<string> = new Set(); // 已揭示方塊@State private flaggedCells: Set<string> = new Set(); // 標記方塊@State private cellSize: number = 60; // 方塊大小@State private isGameOver: boolean = false; // 游戲結束狀態// 其他狀態...
}
使用Set
數據結構存儲已揭示和標記的方塊,利用其快速查找特性(時間復雜度O(1))提升游戲性能。字符串鍵"row,column"
的設計巧妙地將二維坐標轉換為一維鍵,便于狀態管理。
雙向綁定與視圖更新
鴻蒙OS的聲明式UI特性在掃雷游戲中體現為數據與視圖的雙向綁定,當gameBoard
或狀態集合發生變化時,build
方法會自動重新渲染相關組件:
Text(this.isShowValue(cell)).width(`${this.cellSize}lpx`).height(`${this.cellSize}lpx`).backgroundColor(this.revealedCells.has(`${rowIndex},${colIndex}`) ?(this.isShowValue(cell) === '雷' ? Color.Red : Color.White) : Color.Gray)
上述代碼中,背景顏色根據revealedCells
集合中的狀態動態變化,無需手動操作DOM,大大簡化了開發流程。
核心算法與游戲邏輯實現
掃雷游戲的核心在于地雷放置、相鄰計算和自動揭示算法,這些算法的效率直接影響游戲體驗。
地雷放置與數字計算
地雷放置采用隨機算法,確保在10x10的網格中均勻分布指定數量的地雷:
private placeMines() {let placed = 0;while (placed < this.mineCount) {let x = Math.floor(Math.random() * 10);let y = Math.floor(Math.random() * 10);if (!this.gameBoard[x][y].hasMine) {this.gameBoard[x][y].hasMine = true;placed++;}}
}
為避免地雷重疊,通過hasMine
屬性檢查方塊是否已放置地雷。這種實現方式簡單直觀,但在極端情況下(如剩余可放置位置較少時)可能出現性能問題,更優的實現可采用Fisher-Yates洗牌算法。
相鄰地雷計算是掃雷游戲的核心算法之一,通過遍歷當前方塊周圍8個方向的方塊來統計地雷數量:
private countNeighborMines(row: number, col: number): number {let count = 0;for (let dx = -1; dx <= 1; dx++) {for (let dy = -1; dy <= 1; dy++) {if (dx === 0 && dy === 0) continue;let newRow = row + dx, newCol = col + dy;if (newRow >= 0 && newRow < 10 && newCol >= 0 && newCol < 10 && this.gameBoard[newRow][newCol].hasMine) {count++;}}}return count;
}
該算法的時間復雜度為O(1),因為每個方塊最多檢查8個相鄰方塊,確保了游戲的流暢運行。
自動揭示與勝利判定
自動揭示算法是掃雷游戲的靈魂,當玩家點擊一個周圍沒有地雷的方塊時,需要遞歸揭示其周圍所有安全方塊:
private revealCell(row: number, col: number) {if (this.isGameOver || this.revealedCells.has(`${row},${col}`)) return;const key = `${row},${col}`;this.revealedCells.add(key);if (this.gameBoard[row][col].hasMine) {this.showGameOverDialog();} else {if (this.gameBoard[row][col].neighborMines === 0) {for (let dx = -1; dx <= 1; dx++) {for (let dy = -1; dy <= 1; dy++) {if (dx === 0 && dy === 0) continue;let newRow = row + dx, newCol = col + dy;if (newRow >= 0 && newRow < 10 && newCol >= 0 && newCol < 10) {this.revealCell(newRow, newCol);}}}}}if (this.isVictory()) {this.showVictoryDialog();}
}
遞歸實現的自動揭示算法簡潔明了,但在極端情況下(如點擊一個大面積的空白區域)可能導致棧溢出。鴻蒙OS的JavaScript引擎支持尾遞歸優化,但更安全的實現可采用迭代方式(如BFS或DFS)。
勝利判定算法通過統計已揭示的非地雷方塊數量來判斷游戲是否勝利:
private isVictory() {let revealedNonMineCount = 0;const totalNonMineCells = 10 * 10 - this.mineCount;for (let i = 0; i < 10; i++) {for (let j = 0; j < 10; j++) {if (this.revealedCells.has(`${i},${j}`)) {revealedNonMineCount++;}}}return revealedNonMineCount === totalNonMineCells;
}
性能優化策略
掃雷游戲在鴻蒙OS上的性能優化主要體現在以下幾個方面:
- 數據結構優化:使用
Set
存儲已揭示和標記的方塊,確保查找操作的高效性 - 避免重復計算:地雷相鄰數量在初始化時計算完成,游戲過程中無需重復計算
- 事件節流:雖然代碼中未顯式實現,但鴻蒙OS的手勢處理底層已做優化,避免高頻點擊導致的性能問題
- 組件復用:通過
ForEach
循環動態生成方塊組件,避免靜態創建100個組件的性能開銷
交互設計與用戶體驗優化
鴻蒙掃雷游戲在交互設計上充分考慮了移動端的操作特點,通過手勢識別和視覺反饋提升用戶體驗。
手勢交互實現
游戲支持兩種核心手勢:單擊揭示方塊和長按標記地雷,通過parallelGesture
實現手勢組合:
.parallelGesture(GestureGroup(GestureMode.Exclusive,TapGesture({ count: 1, fingers: 1 }).onAction(() => this.revealCell(rowIndex, colIndex)),LongPressGesture({ repeat: true }).onAction(() => cell.isFlag = true)
));
GestureMode.Exclusive
確保兩種手勢不會沖突,長按操作會優先于單擊操作。repeat: true
參數允許長按持續觸發標記狀態,提升操作流暢度。
視覺反饋設計
視覺反饋是掃雷游戲體驗的重要組成部分,鴻蒙游戲通過以下方式提供清晰的反饋:
- 顏色變化:已揭示的方塊背景色變為白色,地雷方塊顯示為紅色
- 數字標識:顯示相鄰地雷數量,數字顏色根據數量不同(如1為藍色,2為綠色)
- 標記旗幟:長按標記的方塊顯示"旗"字,幫助用戶記錄可疑地雷位置
- 游戲狀態提示:通過對話框提示游戲結束或勝利,并顯示用時
Text(this.isShowValue(cell)).backgroundColor(this.revealedCells.has(`${rowIndex},${colIndex}`) ?(this.isShowValue(cell) === '雷' ? Color.Red : Color.White) : Color.Gray).fontColor(!this.revealedCells.has(`${rowIndex},${colIndex}`) || this.isShowValue(cell) === '雷' ?Color.White : Color.Black)
響應式布局設計
游戲界面采用響應式設計,通過cellSize
狀態動態調整方塊大小,確保在不同尺寸的設備上都有良好的顯示效果:
// 游戲面板容器
Flex({ wrap: FlexWrap.Wrap }) {// 方塊生成邏輯...
}
.width(`${(this.cellSize + this.cellMargin * 2) * 10}lpx`);
這種設計使得游戲能夠自適應手機、平板等不同設備的屏幕尺寸,無需為每種設備單獨開發界面。
鴻蒙特性在游戲開發中的應用
掃雷游戲的鴻蒙實現充分利用了平臺的特有能力,展現了鴻蒙OS在應用開發中的優勢。
聲明式UI與狀態管理
鴻蒙OS的聲明式UI模型使得游戲界面與數據狀態緊密綁定,當gameBoard
或狀態集合變化時,UI會自動更新,大大簡化了開發流程:
// 狀態變化自動觸發UI更新
this.revealedCells.add(`${row},${col}`);// 視圖根據狀態動態渲染
Text(this.isShowValue(cell)).visibility(cell.isFlag && !this.isGameOver ? Visibility.Visible : Visibility.None)
這種數據驅動視圖的模式避免了傳統命令式UI中繁瑣的DOM操作,提高了代碼的可維護性。
組件化與復用
游戲中的每個方塊都是一個可復用的組件單元,通過ForEach
循環動態生成,這種組件化思想是鴻蒙應用開發的核心:
ForEach(this.gameBoard, (row: Cell[], rowIndex: number) => {ForEach(row, (cell: Cell, colIndex: number) => {Stack() {// 方塊UI組件...}});
});
組件化設計使得游戲界面結構清晰,同時便于后續擴展,如添加不同難度級別或主題皮膚。
系統能力集成
游戲集成了鴻蒙OS的系統能力,如對話框提示功能:
promptAction.showDialog({title: '游戲結束: 游戲失敗!',buttons: [{ text: '重新開始', color: '#ffa500' }]
}).then(() => {this.initializeGame();
});
promptAction
是鴻蒙OS提供的系統對話框API,支持自定義標題、消息和按鈕,使得游戲能夠無縫融入系統交互體系。
游戲擴展與進階優化
基于當前的掃雷游戲實現,還可以從以下幾個方面進行擴展和優化:
難度級別擴展
添加不同難度級別是掃雷游戲的常見需求,可通過修改網格大小和地雷數量實現:
// 簡單模式:8x8網格,10個地雷
private initEasyMode() {this.mineCount = 10;this.generateBoard(8, 8);
}// 中級模式:16x16網格,40個地雷
private initMediumMode() {this.mineCount = 40;this.generateBoard(16, 16);
}// 高級模式:30x16網格,99個地雷
private initHardMode() {this.mineCount = 99;this.generateBoard(30, 16);
}
同時需要調整cellSize
以適應不同網格大小,確保界面在各種難度下都能良好顯示。
主題與視覺優化
添加主題切換功能可以提升游戲體驗,可通過定義不同的顏色方案實現:
// 主題接口
interface Theme {cellColor: ResourceColor;revealedColor: ResourceColor;mineColor: ResourceColor;numberColors: ResourceColor[];
}// 亮色主題
const lightTheme: Theme = {cellColor: Color.Gray,revealedColor: Color.White,mineColor: Color.Red,numberColors: [Color.Blue, Color.Green, Color.Orange, Color.Red, Color.Purple, Color.Teal, Color.Brown, Color.Black]
};// 暗色主題
const darkTheme: Theme = {cellColor: Color.DarkGray,revealedColor: Color.DimGray,mineColor: Color.Red,numberColors: [Color.SkyBlue, Color.Lime, Color.Orange, Color.Red, Color.Plum, Color.Aqua, Color.Chocolate, Color.White]
};
性能與體驗優化
針對大規模網格(如高級模式30x16),可實現以下優化:
- 虛擬滾動:只渲染可見區域的方塊,而非全部方塊
- 異步初始化:使用
setTimeout
分批次初始化地雷和計算數字,避免UI卡頓 - 手勢優化:添加雙擊快捷揭示功能(點擊數字方塊時揭示周圍未標記方塊)
- 動畫效果:為方塊揭示添加淡入動畫,提升視覺體驗
// 雙擊快捷揭示
doubleTapGesture().onAction(() => {if (this.gameBoard[row][col].neighborMines > 0) {this.revealSurroundingCells(row, col);}})
附:代碼
import { promptAction } from '@kit.ArkUI';@Entry
@Component
struct Index {// 游戲面板數據@State private gameBoard: Cell[][] = [];// 地雷總數@State private mineCount: number = 10;// 已經揭示的方塊集合@State private revealedCells: Set<string> = new Set();// 標記為地雷的方塊集合@State private flaggedCells: Set<string> = new Set();// 方塊大小@State private cellSize: number = 60;// 方塊之間的邊距@State private cellMargin: number = 2;// 游戲開始時間private startTime: number = Date.now();// 游戲結束標志@State private isGameOver: boolean = false;// 在組件即將顯示時初始化游戲aboutToAppear(): void {this.initializeGame();}// 初始化游戲private initializeGame() {this.isGameOver = false;this.startTime = Date.now();this.revealedCells.clear();this.flaggedCells.clear();this.generateBoard();}// 生成游戲面板private generateBoard() {this.gameBoard = [];for (let i = 0; i < 10; i++) {this.gameBoard.push([]);for (let j = 0; j < 10; j++) {this.gameBoard[i].push(new Cell(i, j));}}this.placeMines();this.calculateNumbers();}// 隨機放置地雷private placeMines() {let placed = 0;while (placed < this.mineCount) {let x = Math.floor(Math.random() * 10);let y = Math.floor(Math.random() * 10);if (!this.gameBoard[x][y].hasMine) {this.gameBoard[x][y].hasMine = true;placed++;}}}// 計算每個方塊周圍的地雷數量private calculateNumbers() {for (let i = 0; i < 10; i++) {for (let j = 0; j < 10; j++) {if (!this.gameBoard[i][j].hasMine) {this.gameBoard[i][j].neighborMines = this.countNeighborMines(i, j);this.gameBoard[i][j].value = this.gameBoard[i][j].neighborMines.toString();} else {this.gameBoard[i][j].value = '雷';}}}}// 計算給定坐標周圍地雷的數量private countNeighborMines(row: number, col: number): number {let count = 0;for (let dx = -1; dx <= 1; dx++) {for (let dy = -1; dy <= 1; dy++) {if (dx === 0 && dy === 0) {continue;}let newRow = row + dx, newCol = col + dy;if (newRow >= 0 && newRow < 10 && newCol >= 0 && newCol < 10 && this.gameBoard[newRow][newCol].hasMine) {count++;}}}return count;}// 揭示方塊private revealCell(row: number, col: number) {if (this.isGameOver || this.revealedCells.has(`${row},${col}`)) {return;}const key = `${row},${col}`;this.revealedCells.add(key);if (this.gameBoard[row][col].hasMine) {this.showGameOverDialog();} else {if (this.gameBoard[row][col].neighborMines === 0) {for (let dx = -1; dx <= 1; dx++) {for (let dy = -1; dy <= 1; dy++) {if (dx === 0 && dy === 0) {continue;}let newRow = row + dx, newCol = col + dy;if (newRow >= 0 && newRow < 10 && newCol >= 0 && newCol < 10) {this.revealCell(newRow, newCol);}}}}}if (this.isVictory()) {this.showVictoryDialog();}}// 顯示游戲結束對話框private showGameOverDialog() {this.isGameOver = true;promptAction.showDialog({title: '游戲結束: 游戲失敗!',buttons: [{ text: '重新開始', color: '#ffa500' }]}).then(() => {this.initializeGame();});}// 顯示勝利對話框private showVictoryDialog() {this.isGameOver = true;promptAction.showDialog({title: '恭喜你,游戲勝利!',message: `用時:${((Date.now() - this.startTime) / 1000).toFixed(3)}秒`,buttons: [{ text: '重新開始', color: '#ffa500' }]}).then(() => {this.initializeGame();});}// 判斷游戲是否勝利private isVictory() {let revealedNonMineCount = 0;for (let i = 0; i < this.gameBoard.length; i++) {for (let j = 0; j < this.gameBoard[i].length; j++) {if (this.revealedCells.has(`${i},${j}`)) {revealedNonMineCount++;}}}return revealedNonMineCount == 90;}// 決定是否顯示方塊值private isShowValue(cell: Cell): string {if (this.isGameOver) {return cell.value === '0' ? '' : cell.value;} else {if (this.revealedCells.has(`${cell.row},${cell.column}`)) {return cell.value === '0' ? '' : cell.value;} else {return '';}}}build() {Column({ space: 10 }) {// 重置游戲按鈕Button('重新開始').onClick(() => {this.initializeGame()});// 創建游戲面板容器Flex({ wrap: FlexWrap.Wrap }) {// 遍歷每一行ForEach(this.gameBoard, (row: Cell[], rowIndex: number) => {// 遍歷每一列ForEach(row, (cell: Cell, colIndex: number) => {Stack() {// 顯示方塊上的數字或雷Text(this.isShowValue(cell)).width(`${this.cellSize}lpx`).height(`${this.cellSize}lpx`).margin(`${this.cellMargin}lpx`).fontSize(`${this.cellSize / 2}lpx`).textAlign(TextAlign.Center).backgroundColor(this.revealedCells.has(`${rowIndex},${colIndex}`) ?(this.isShowValue(cell) === '雷' ? Color.Red : Color.White) : Color.Gray).fontColor(!this.revealedCells.has(`${rowIndex},${colIndex}`) || this.isShowValue(cell) === '雷' ?Color.White : Color.Black).borderRadius(5).parallelGesture(GestureGroup(GestureMode.Exclusive,TapGesture({ count: 1, fingers: 1 }).onAction(() => this.revealCell(rowIndex, colIndex)),LongPressGesture({ repeat: true }).onAction(() => cell.isFlag = true)));// 顯示標記旗幟Text(`${!this.revealedCells.has(`${rowIndex},${colIndex}`) ? '旗' : ''}`).width(`${this.cellSize}lpx`).height(`${this.cellSize}lpx`).margin(`${this.cellMargin}lpx`).fontSize(`${this.cellSize / 2}lpx`).textAlign(TextAlign.Center).fontColor(Color.White).visibility(cell.isFlag && !this.isGameOver ? Visibility.Visible : Visibility.None).onClick(() => {cell.isFlag = false;})}});});}.width(`${(this.cellSize + this.cellMargin * 2) * 10}lpx`);}.padding(20).backgroundColor('#ffffff').width('100%').height('100%');}
}// 方塊類
@ObservedV2
class Cell {// 方塊所在的行row: number;// 方塊所在的列column: number;// 是否有地雷hasMine: boolean = false;// 周圍地雷數量neighborMines: number = 0;// 是否被標記為地雷@Trace isFlag: boolean = false;// 方塊值@Trace value: string;// 構造函數constructor(row: number, column: number) {this.row = row;this.column = column;this.value = '';}
}
鴻蒙OS掃雷游戲的開發實踐展示了平臺在游戲應用開發中的強大能力,從聲明式UI的高效開發到系統能力的深度集成,再到性能優化和交互設計,鴻蒙OS為開發者提供了完整的解決方案。掃雷游戲作為經典的算法與交互結合的案例,其實現過程涵蓋了數據結構、算法設計、用戶體驗等多個維度,對于理解移動應用開發具有重要參考價值。
隨著鴻蒙OS生態的不斷發展,游戲開發者可以進一步利用ArkTS語言的特性和平臺的底層能力,開發出更復雜、更具沉浸感的游戲應用。從掃雷這樣的輕量級游戲到更大型的3D游戲,鴻蒙OS都提供了從開發到部署的全流程支持,為開發者和用戶帶來全新的體驗。