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ᵒᵛᵉᵧₒᵤ❤

相关推荐
nashane8 小时前
HarmonyOS 6学习:CapsLock键失效诊断与长截图完整实现指南
学习·华为·harmonyos
richard_yuu10 小时前
鸿蒙心理测评模块实战|PHQ-9/GAD7双量表答题、实时计分与结果本地化存储
华为·harmonyos
不爱吃糖的程序媛13 小时前
2026年Electron 鸿蒙PC环境搭建指南
人工智能·华为·harmonyos
nashane13 小时前
HarmonyOS 6学习:长截图功能开发中的滚动拼接与权限处理实战
人工智能·华为·harmonyos
大师兄666815 小时前
从零开发一个 HarmonyOS 输入法——KikaInputMethod 完整拆解
harmonyos·服务卡片·harmonyos6·formkit
Python私教20 小时前
鸿蒙 NEXT 也能接 MCP?用 ArkTS 跑通 AI Agent 工具链
人工智能·华为·harmonyos
Swift社区1 天前
分布式能力在鸿蒙 PC 上到底怎么用?
分布式·华为·harmonyos
nashane1 天前
HarmonyOS 6学习:外接键盘CapsLock与长截图功能的实战调试与完整解决方案
学习·华为·计算机外设·harmonyos
aqi002 天前
一文理清 HarmonyOS 6.0.2 涵盖的十个升级点
android·华为·harmonyos·鸿蒙·harmony