HarmonyOS游戏开发入门:用ArkTS打造经典五子棋

✨家人们记得点个账号关注,会持续发布大前端领域技术文章💕 🍃

引言

五子棋是一款经典的策略游戏,规则简单但乐趣无穷。本文将带你使用 HarmonyOS 的 ArkUI 框架,以组件化的思想快速实现一个双人对战的五子棋游戏。我们将逻辑与 UI 分离,打造一个结构清晰、易于维护的应用。

仓库地址(小游戏/五子棋目录)https://gitee.com/harmonyos-projects/codelabs

1. 棋盘

ets/pages/gobang/01-棋盘.ets

less 复制代码
@Entry
@Component
struct Study {

  // 一个15x15的二维数组,表示棋盘,初始值为0表格空位,1表示黑棋,2表示白棋。
  @State board: number[][] = Array(15).fill(null).map(() => Array(15).fill(0))
  @State currentPlayer: number = 1 // 1表示黑棋,2表示白棋。
  @State gameOver: boolean = false

  build() {
    Column() {
      // 游戏标题和状态显示
      Row() {
        Text('五子棋').fontSize(24).fontWeight(FontWeight.Bold)
        Text(`当前:${this.currentPlayer==1?'黑':'白'}棋`).fontSize(16).margin({ left: 20 })
      }
      .width('100%')
      .margin({ bottom: 10 })
      .justifyContent(FlexAlign.Center)

      // 棋盘
      Column() {
        // 行
        ForEach(this.board, (row: number[], rowIndex:number) => {
          Row() {
            ForEach(row, (cell: number, colIndex:number) => {
              // 列
              Column() {
                if (cell) {
                  Text().width(18).height(18).backgroundColor(cell === 1 ? '#000' : '#fff').borderRadius(9)
                }
              }
              .width(20).height(20).backgroundColor('#DEB887')
              .border({ width: 1, color: '#999' })
            })
          }
        })
      }.margin(10)

      // 操作按钮
      Row() {
        Button('悔棋').width(120)
        Button('重新开始').width(120).margin({ left: 10 })
      }
      .width('100%')
      .justifyContent(FlexAlign.Center)
    }.padding(20)
  }
}

2. 棋子

ets/pages/gobang/02-棋子.ets

less 复制代码
@Entry
@Component
struct Study {

  // 一个15x15的二维数组,表示棋盘,初始值为0表格空位,1表示黑棋,2表示白棋。
  @State board: number[][] = Array(15).fill(null).map(() => Array(15).fill(0))
  @State currentPlayer: number = 1 // 1表示黑棋,2表示白棋。
  @State gameOver: boolean = false

  // 下棋
  playChess(row: number, col: number) {
    // 1. 过滤数据
    if (this.gameOver || this.board[row][col] !== 0) return

    // 2. 修改状态
    // - 棋盘
    this.board[row][col] = this.currentPlayer  // 修改二维没响应式
    this.board = [...this.board]               // 直接覆盖原数据响应式生效
    // - next 棋子
    this.currentPlayer = this.currentPlayer === 1 ? 2 : 1
  }

  build() {
    Column() {
      // 游戏标题和状态显示
      Row() {
        Text('五子棋').fontSize(24).fontWeight(FontWeight.Bold)
        Text(`当前:${this.currentPlayer==1?'黑':'白'}棋`).fontSize(16).margin({ left: 20 })
      }
      .width('100%')
        .margin({ bottom: 10 })
        .justifyContent(FlexAlign.Center)

      // 棋盘
      Column() {
        // 行
        ForEach(this.board, (row: number[], rowIndex:number) => {
          Row() {
            ForEach(row, (cell: number, colIndex:number) => {
              // 列
              Column() {
                if (cell) {
                  Text().width(18).height(18).backgroundColor(cell === 1 ? '#000' : '#fff').borderRadius(9)
                }
              }
              .width(20).height(20).backgroundColor('#DEB887')
                    .border({ width: 1, color: '#999' })
                    .onClick(() => this.playChess(rowIndex, colIndex))
                    })
          }
        })
      }.margin(10)

      // 操作按钮
      Row() {
        Button('悔棋').width(120)
        Button('重新开始').width(120).margin({ left: 10 })
      }
      .width('100%')
        .justifyContent(FlexAlign.Center)
    }.padding(20)
  }
}

3. 检查胜负

ets/pages/gobang/03-检查胜负.ets

less 复制代码
@Entry
@Component
struct Study {

  // 一个15x15的二维数组,表示棋盘,初始值为0表格空位,1表示黑棋,2表示白棋。
  @State board: number[][] = Array(15).fill(null).map(() => Array(15).fill(0))
  @State currentPlayer: number = 1 // 1表示黑棋,2表示白棋。
  @State gameOver: boolean = false

  // 下棋
  playChess(row: number, col: number) {
    // 1. 过滤数据
    if (this.gameOver || this.board[row][col] !== 0) return

    // 2. 修改状态
    // - 棋盘
    this.board[row][col] = this.currentPlayer  // 修改二维没响应式
    this.board = [...this.board]               // 直接覆盖原数据响应式生效
    // - next 棋子
    // this.currentPlayer = this.currentPlayer === 1 ? 2 : 1

    // 3. 判断下棋后结果
    if (this.checkWin(row, col)) {
      this.gameOver = true
      AlertDialog.show({ message: `${this.currentPlayer === 1 ? '黑棋' : '白棋'}获胜!` })
    } else {
      this.currentPlayer = this.currentPlayer === 1 ? 2 : 1
    }
  }

  // 检查胜负
  checkWin(row: number, col: number): boolean {
    const directions = [      [[-1, 0], [1, 0]],   // 垂直
      [[0, -1], [0, 1]],   // 水平
      [[-1, -1], [1, 1]],  // 主对角线
      [[-1, 1], [1, -1]]   // 副对角线
    ]

    for (let direction of directions) {
      let count = 1
      for (let i = 0; i < direction.length; i++) {
        let dx = direction[i][0]
        let dy = direction[i][1]
        let x = row + dx
        let y = col + dy
        while (x >= 0 && x < 15 && y >= 0 && y < 15 &&
               this.board[x][y] === this.currentPlayer) {
          count++
          x += dx
          y += dy
        }
      }
      if (count >= 5) return true
    }
    return false
  }


  build() {
    Column() {
      // 游戏标题和状态显示
      Row() {
        Text('五子棋').fontSize(24).fontWeight(FontWeight.Bold)
        Text(`当前:${this.currentPlayer==1?'黑':'白'}棋`).fontSize(16).margin({ left: 20 })
      }
      .width('100%')
        .margin({ bottom: 10 })
        .justifyContent(FlexAlign.Center)

      // 棋盘
      Column() {
        // 行
        ForEach(this.board, (row: number[], rowIndex:number) => {
          Row() {
            ForEach(row, (cell: number, colIndex:number) => {
              // 列
              Column() {
                if (cell) {
                  Text().width(18).height(18).backgroundColor(cell === 1 ? '#000' : '#fff').borderRadius(9)
                }
              }
              .width(20).height(20).backgroundColor('#DEB887')
                    .border({ width: 1, color: '#999' })
                    .onClick(() => this.playChess(rowIndex, colIndex))
                    })
          }
        })
      }.margin(10)

      // 操作按钮
      Row() {
        Button('悔棋').width(120)
        Button('重新开始').width(120).margin({ left: 10 })
      }
      .width('100%')
        .justifyContent(FlexAlign.Center)
    }.padding(20)
  }
}

4. 重新开始

ets/pages/gobang/04-重新开始.ets

javascript 复制代码
// 重新开发
resetGame() {
  this.board = Array(15).fill(null).map(() => Array(15).fill(0))
  this.currentPlayer = 1
  this.gameOver = false
}

5. 悔棋 选写

  • 下棋playChess时,保存row、col索引
  • 点击悔棋修改改索引状态为空格
  • 并且把currentPlayer修改

6. 面向对象封装

ets/gobang/Control.ets

typescript 复制代码
export class Control {

  private currentPlayer: number = 1 // 1表示黑棋,2表示白棋。
  private gameOver: boolean = false

  // 下棋
  playChess(board:number[][], row: number, col: number) {
    // 1. 过滤数据
    if (this.gameOver || board[row][col] !== 0) return

    // 2. 修改状态
    // - 棋盘
    board[row][col] = this.currentPlayer
    // - next 棋子
    // this.currentPlayer = this.currentPlayer === 1 ? 2 : 1

    // 3. 判断下棋后结果
    if (this.checkWin(board, row, col)) {
      this.gameOver = true
      AlertDialog.show({ message: `${this.currentPlayer === 1 ? '黑棋' : '白棋'}获胜!` })
    } else {
      this.currentPlayer = this.currentPlayer === 1 ? 2 : 1
    }

    return [...board]
  }

  // 检查胜负
  checkWin(board:number[][], row: number, col: number): boolean {
    const directions = [
      [[-1, 0], [1, 0]],   // 垂直
      [[0, -1], [0, 1]],   // 水平
      [[-1, -1], [1, 1]],  // 主对角线
      [[-1, 1], [1, -1]]   // 副对角线
    ]

    for (let direction of directions) {
      let count = 1
      for (let i = 0; i < direction.length; i++) {
        let dx = direction[i][0]
        let dy = direction[i][1]
        let x = row + dx
        let y = col + dy
        while (x >= 0 && x < 15 && y >= 0 && y < 15 &&
          board[x][y] === this.currentPlayer) {
          count++
          x += dx
          y += dy
        }
      }
      if (count >= 5) return true
    }
    return false
  }

  // 重新开始
  resetGame():number[][] {
    this.currentPlayer = 1
    this.gameOver = false
    return this.sourceData()
  }

  // 数据源
  sourceData():number[][] {
    return Array(15).fill(null).map(() => Array(15).fill(0))
  }
}

ets/pages/gobang/05-封装.ets

scss 复制代码
import { Control } from './Control'

@Entry
@Component
struct Study {

  private control1 = new Control()
  @State board1: number[][] = this.control1.sourceData()

  private control2 = new Control()
  @State board2: number[][] = this.control2.sourceData()

  build() {
    Column() {
      // 棋盘1
      Column() {
        // 行
        ForEach(this.board1, (row: number[], rowIndex:number) => {
          Row() {
            ForEach(row, (cell: number, colIndex:number) => {
              // 列
              Column() {
                if (cell) {
                  Text().width(18).height(18).backgroundColor(cell === 1 ? '#000' : '#fff').borderRadius(9)
                }
              }
              .width(20).height(20).backgroundColor('#DEB887')
              .border({ width: 1, color: '#999' })
              .onClick(() => {
                const data = this.control1.playChess(this.board1, rowIndex, colIndex)
                if (data) this.board1 = data
              })
            })
          }
        })
        Button('重新开始').onClick(() => this.board1 = this.control1.resetGame())
      }.margin(10)

      // 棋盘2
      Column() {
        // 行
        ForEach(this.board2, (row: number[], rowIndex:number) => {
          Row() {
            ForEach(row, (cell: number, colIndex:number) => {
              // 列
              Column() {
                if (cell) {
                  Text().width(18).height(18).backgroundColor(cell === 1 ? '#000' : '#fff').borderRadius(9)
                }
              }
              .width(20).height(20).backgroundColor('#DEB887')
              .border({ width: 1, color: '#999' })
              .onClick(() => {
                const data = this.control2.playChess(this.board2, rowIndex, colIndex)
                if (data) this.board2 = data
              })
            })
          }
        })
        Button('重新开始').onClick(() => this.board2 = this.control2.resetGame())
      }.margin(10)
    }.padding(20)
  }
}

鸿蒙开发者班级

✨家人们点个账号关注,会持续发布大前端领域技术文章💕 🍃

✨家人们点个账号关注,会持续发布大前端领域技术文章💕 🍃

✨家人们点个账号关注,会持续发布大前端领域技术文章💕 🍃

^_^ 点关注、不迷路、主播带你学技术 (๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤

相关推荐
q***R3082 小时前
HarmonyOS在智能家居中的场景模式
华为·智能家居·harmonyos
可观测性用观测云2 小时前
为鸿蒙生态注入可观测动力:观测云 HarmonyOS SDK 重磅上线
harmonyos
xq95273 小时前
鸿蒙next sqlite进阶版本来了
harmonyos
c***97985 小时前
HarmonyOS在智能车载系统的集成
华为·车载系统·harmonyos
1***s6325 小时前
HarmonyOS智能电视应用开发指南
华为·harmonyos·智能电视
lqj_本人10 小时前
鸿蒙Cordova开发踩坑记录:跨域请求的“隐形墙“
harmonyos
Z***258014 小时前
HarmonyOS在物联网场景的应用
物联网·华为·harmonyos
Pocker_Spades_A16 小时前
John the Ripper 在 HarmonyOS 上的构建与适配
华为·harmonyos
不爱吃糖的程序媛17 小时前
鸿蒙PC Electron 打印服务实现详解
华为·electron·harmonyos