写点什么

鸿蒙开发案例:一个简单的扫雷游戏应用

作者:zhongcx
  • 2024-10-12
    广东
  • 本文字数:4577 字

    阅读完需:约 15 分钟

我们的扫雷游戏将具备以下功能:

• 动态生成 10x10 的游戏面板。

• 放置 10 个随机地雷。

• 计算并显示每个方块周围的地雷数量。

• 用户可以通过点击来揭示方块,长按来标记地雷。

• 当揭示到地雷时,游戏结束;当所有非雷方块都被揭示时,游戏胜利。

完整代码

import { promptAction } from '@kit.ArkUI'; @Entry@Componentstruct MineSweeper {  // 游戏面板数据  @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`);    }    .backgroundColor(Color.Orange)    .width('100%')    .height('100%');  }} // 方块类@ObservedV2class 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 = '';  }}
复制代码

实现步骤

步骤 1:定义游戏状态

首先,我们需要定义游戏的状态变量,包括游戏面板数据、地雷数量、已揭示方块集合、标记为地雷的方块集合等。

struct MineSweeper {  @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;}
复制代码

步骤 2:初始化游戏

在组件即将显示时初始化游戏,包括清空状态变量、生成游戏面板、放置地雷等。

aboutToAppear(): void {  this.initializeGame();} private initializeGame() {  this.isGameOver = false;  this.startTime = Date.now();  this.revealedCells.clear();  this.flaggedCells.clear();  this.generateBoard();}
复制代码

步骤 3:生成游戏面板与放置地雷

游戏面板由 10x10 的方块组成,随机放置 10 个地雷,并计算每个方块周围的地雷数量。

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();}
复制代码

步骤 4:处理用户交互

用户可以通过点击方块来揭示其内容,也可以通过长按来标记地雷。

private revealCell(row: number, col: number) {  // 处理揭示方块的逻辑...} build() {  // 使用Flex布局和ForEach遍历游戏面板,添加点击和长按手势...}
复制代码

步骤 5:显示游戏结果

当玩家揭示到地雷时,游戏结束;当所有非雷方块都被揭示时,游戏胜利。

private showGameOverDialog() {  // 显示游戏结束对话框...} private showVictoryDialog() {  // 显示胜利对话框...}
复制代码

步骤 6:定义 Cell 类

最后,定义一个 Cell 类来存储每个方块的信息。

@ObservedV2class Cell {  // 定义方块的各种属性...}
复制代码


用户头像

zhongcx

关注

还未添加个人签名 2024-09-27 加入

还未添加个人简介

评论

发布
暂无评论
鸿蒙开发案例:一个简单的扫雷游戏应用_zhongcx_InfoQ写作社区