您好,登錄后才能下訂單哦!
這篇文章主要介紹“Android Flutter如何實(shí)現(xiàn)在多端運(yùn)行的掃雷游戲”的相關(guān)知識(shí),小編通過實(shí)際案例向大家展示操作過程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“Android Flutter如何實(shí)現(xiàn)在多端運(yùn)行的掃雷游戲”文章能幫助大家解決問題。
話不多說,先上效果圖。(包含不同端、不同難度、不同游戲主題)
Windows端
網(wǎng)頁端
Android端
定義GameSetting
單例類,確保掃雷程序中只有一個(gè)實(shí)例,并且該實(shí)例可以被全局訪問,主要用于共享資源。
class GameSetting { GameSetting._(); }
然后定義一個(gè)私有的、靜態(tài)的、不可變的 _default
對(duì)象,它是 GameSetting
類的默認(rèn)實(shí)例,該實(shí)例在第一次使用時(shí)被創(chuàng)建。再定義一個(gè) GameSetting
工廠構(gòu)造函數(shù),它通過返回 _default 對(duì)象實(shí)現(xiàn)了單例模式的實(shí)例化,該工廠構(gòu)造函數(shù)是唯一可以實(shí)例化 GameSetting 對(duì)象的方法。
static final GameSetting _default = GameSetting._(); factory GameSetting() => _default;
完成了單例類的基本定義,現(xiàn)在再來定義與掃雷相關(guān)的,先定義游戲的難度。在掃雷中游戲的難度主要有兩部分組成:
棋盤格子的數(shù)量
///游戲的難度,默認(rèn)為8*8 int difficulty = 8;
雷的數(shù)量
///雷的數(shù)量 (格子總數(shù) * 0.18 向下取整),通常掃雷的雷數(shù)在0.16-0.2之間。 int get mines => (difficulty * difficulty * 0.18).floor();
最后在定義一些游戲的顏色主題:
List<Color> c_5ADFD0 = [ Color(0xFF299794), Color(0xFF2EC4C0), Color(0xFF2EC4C0) ]; List<Color> c_A0BBFF = [ Color(0xFF5067C5), Color(0xFF838CFF), Color(0xFFA0BBFF), ]; ///默認(rèn)主題 Color themeColor = Color(0xFF5ADFD0);
在進(jìn)行掃雷游戲的時(shí)候,需要記錄棋盤格子上每個(gè)格子的參數(shù),記錄格子是否被標(biāo)記為雷、是否被翻開。也需要記錄游戲是否獲勝、是否踩到了地雷。
late List<List<int>> board; // 棋盤 late List<List<bool>> revealed; // 記錄格子是否被翻開 late List<List<bool>> flagged; // 記錄格子是否被標(biāo)記 late bool gameOver; // 游戲是否結(jié)束 late bool win; // 是否獲勝
其他初始化參數(shù):
late int numRows; // 行數(shù) late int numCols; // 列數(shù) late int numMines; // 雷數(shù) //游戲時(shí)間 late int _playTime;
定義了游戲的參數(shù)后,在游戲開始時(shí),需要對(duì)其進(jìn)行賦值。
numRows = gameSetting.difficulty; numCols = gameSetting.difficulty; numMines = gameSetting.mines; // 初始化棋盤 board = List.generate(numRows, (_) => List.filled(numCols, 0)); // 初始化格子是否被翻開 revealed = List.generate(numRows, (_) => List.filled(numCols, false)); // 初始化格子是否被標(biāo)記 flagged = List.generate(numRows, (_) => List.filled(numCols, false)); // 將游戲定義為未結(jié)束 gameOver = false; // 將游戲定義為還未獲勝 win = false;
通過while
循環(huán)在棋盤上隨機(jī)放置地雷,直到放置的地雷數(shù)量達(dá)到預(yù)定的 numMines
。
int numMinesPlaced = 0; while (numMinesPlaced < numMines) { ... }
使用 Random().nextInt
方法生成兩個(gè)隨機(jī)數(shù) i 和 j,分別用于表示棋盤中的行和列。
int i = Random().nextInt(numRows); int j = Random().nextInt(numCols);
通過 board[i][j] != -1
的判斷語句,檢查這個(gè)位置是否已經(jīng)放置了地雷。如果沒有則將 board[i][j]
的值設(shè)置為 -1,表示在這個(gè)位置放置了地雷,并將numMinesPlaced
的值加 1。
if (board[i][j] != -1) { board[i][j] = -1; numMinesPlaced++; }
放完了地雷,那么就到了掃雷的核心邏輯,計(jì)算每個(gè)非地雷格子周圍的地雷數(shù)量,然后將計(jì)算得到的地雷數(shù)量保存在對(duì)應(yīng)的格子上。具體實(shí)現(xiàn)的邏輯為:通過兩個(gè)嵌套的 for
循環(huán)遍歷整個(gè)棋盤,內(nèi)層的兩個(gè)嵌套循環(huán)會(huì)計(jì)算這個(gè)格子周圍的所有格子中地雷的數(shù)量,并將這個(gè)數(shù)量保存在 count
變量中。
for (int i = 0; i < numRows; i++) { for (int j = 0; j < numCols; j++) { ... } }
循環(huán)中具體的邏輯是:在每個(gè)單元格上,如果它不是地雷(值不為為-1)則內(nèi)部嵌套兩個(gè)循環(huán)遍歷當(dāng)前單元格周圍的所有單元格,計(jì)算地雷數(shù)量并存儲(chǔ)在當(dāng)前單元格中。
if (board[i][j] != -1) { int count = 0; for (int i2 = max(0, i - 1); i2 <= min(numRows - 1, i + 1); i2++) { for (int j2 = max(0, j - 1); j2 <= min(numCols - 1, j + 1); j2++) { if (board[i2][j2] == -1) { count++; } } } board[i][j] = count; }
只要用戶點(diǎn)擊了,就要將格子設(shè)置為翻開了。
void reveal(int i, int j) { revealed[i][j] = true; }
當(dāng)用戶點(diǎn)擊了一個(gè)格子后,我們需要判斷以下幾點(diǎn):
如果翻開的是地雷
if (board[i][j] == -1) { //將所有的地雷翻開,告訴用戶所有的地雷位置 for (int i2 = 0; i2 < numRows; i2++) { for (int j2 = 0; j2 < numCols; j2++) { if (board[i2][j2] == -1) { revealed[i2][j2] = true; } } } //游戲結(jié)束 gameOver = true; //結(jié)束動(dòng)畫 ... }
如果點(diǎn)擊的格子周圍都沒有雷就自動(dòng)翻開相鄰的空格
if (board[i][j] == 0) { for (int i2 = max(0, i - 1); i2 <= min(numRows - 1, i + 1); i2++) { for (int j2 = max(0, j - 1); j2 <= min(numCols - 1, j + 1); j2++) { if (!revealed[i2][j2]) { reveal(i2, j2); } } } }
檢查是否勝利
///它會(huì)遍歷整個(gè)棋盤,檢查每一個(gè)未被翻開的格子是否都是地雷, bool checkWin() { for (int i = 0; i < numRows; i++) { for (int j = 0; j < numCols; j++) { if (board[i][j] != -1 && !revealed[i][j]) { return false; } } } return true; } if (checkWin()) { win = true; gameOver = true; _timer?.cancel(); //獲勝動(dòng)畫 ... }
定義枚舉類BlockType
,用于判斷不同的狀態(tài)下顯示不同的格子樣式。
enum BlockType { //數(shù)字 figure, //雷 mine, //標(biāo)記 label, //未標(biāo)記(未被翻開) unlabeled, }
封裝格子的代碼其實(shí)很簡(jiǎn)單,根據(jù)不同的狀態(tài)封裝即可,這里就不過多展示了。
此處只分析游戲棋盤的布局。
通過GridView.builder
構(gòu)建棋盤,使用SliverGridDelegateWithFixedCrossAxisCount
實(shí)現(xiàn)每一行具有相同數(shù)量的列。
GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: numCols, childAspectRatio: 1.0, ), itemBuilder: (BuildContext context, int index) { ... } )
通過對(duì)index的整除和取模,得到行和列,然后根據(jù)每個(gè)格子的當(dāng)前狀態(tài)對(duì)封裝好的格子布局傳入不同的參數(shù)。
itemBuilder: (BuildContext context, int index) { int i = index ~/ numCols; int j = index % numCols; BlockType blockType; //格子被翻開 if (revealed[i][j]) { //是地雷 if (board[i][j] == -1) { blockType = BlockType.mine; } else { blockType = BlockType.figure; } } else { //被用戶標(biāo)記 if (flagged[i][j]) { blockType = BlockType.label; } else { blockType = BlockType.unlabeled; } } return GestureDetector( onTap: () => reveal(i, j), onDoubleTap: () => toggleFlag(i, j), child: BlockContainer( backColor: gameSetting.themeColor, value: revealed[i][j] && board[i][j] != 0 ? board[i][j] : 0, blockType: blockType, ), ); },
其中,如果雙擊格子代表標(biāo)記或取消標(biāo)記,定義了一個(gè)方法toggleFlag
。
///標(biāo)記雷 void toggleFlag(int i, int j) { if (!gameOver) { setState(() { flagged[i][j] = !flagged[i][j]; }); } }
到這里,就完成了對(duì)掃雷這款游戲的實(shí)現(xiàn)。更改游戲的主題狀態(tài)或游戲難度,只需更改不同的初始化參數(shù)即可。
有一個(gè)計(jì)時(shí)器,會(huì)大大提高用戶玩算法類、解謎類這樣游戲的樂趣,例如魔方。在Flutter中通過Timer.periodic
去實(shí)現(xiàn)計(jì)時(shí)器是很簡(jiǎn)答的,就不過多講述了,主要看下如何格式化為時(shí)鐘的形式:
String get playTime { int minutes = (_playTime ~/ 60); // 計(jì)算分鐘數(shù) int seconds = (_playTime % 60); // 計(jì)算秒數(shù) //padLeft方法用于補(bǔ)齊不足兩位的數(shù)字,第一個(gè)參數(shù)是補(bǔ)齊后的字符串總長(zhǎng)度,第二個(gè)參數(shù)是用于補(bǔ)齊的字符。 return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}'; }
關(guān)于“Android Flutter如何實(shí)現(xiàn)在多端運(yùn)行的掃雷游戲”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。