HarmonyOS 小游戏《对战五子棋》开发第33篇 - AI异步落子与setTimeout

让AI"思考"一下再落子------异步交互的体验设计

截图如下:

AI落子的时序问题

如果AI在玩家落子后立即响应,用户体验会很奇怪------感觉不像在"对战"。通过setTimeout添加延迟,让AI有"思考"的时间。

玩家落子触发AI

typescript 复制代码
private onCellClick(row: number, col: number): void {
  if (this.result !== GameResult.PLAYING || this.aiThinking) return;
  if (this.currentPlayer !== BLACK) return;  // 只有玩家(黑方)能点击

  const success = this.engine.placePiece(row, col, BLACK);
  if (!success) return;

  this.refreshBoardData();

  if (this.result !== GameResult.PLAYING) {
    this.handleGameEnd();
    return;
  }

  // AI回合
  this.aiThinking = true;
  this.aiTimerId = setTimeout(() => {
    this.makeAIMove();
  }, 400);
}

aiThinking状态

typescript 复制代码
@State aiThinking: boolean = false;

aiThinking有双重作用:

  1. UI展示:显示"AI思考中..."和LoadingProgress动画
  2. 交互锁:AI思考时禁止玩家点击棋盘
typescript 复制代码
// 交互锁
if (this.result !== GameResult.PLAYING || this.aiThinking) return;

// UI展示
Text(this.getStatusText())  // 返回 "AI思考中..."
if (this.aiThinking) {
  LoadingProgress().width(16).height(16).color('#FA8C16')
}

// 按钮状态
Button('重新开始')
  .enabled(!this.aiThinking)  // AI思考时禁用

makeAIMove实现

typescript 复制代码
private makeAIMove(): void {
  this.aiTimerId = -1;  // 清除定时器ID
  
  if (this.result !== GameResult.PLAYING) {
    this.aiThinking = false;
    return;
  }

  // AI计算最佳落子
  const move = this.aiPlayer.getMove(this.engine.board);
  
  // 引擎执行落子
  this.engine.placePiece(move.row, move.col, WHITE);
  
  // 解除思考状态
  this.aiThinking = false;
  
  // 刷新UI
  this.refreshBoardData();

  // 检查游戏结束
  if (this.result !== GameResult.PLAYING) {
    this.handleGameEnd();
  }
}

setTimeout的作用

typescript 复制代码
this.aiTimerId = setTimeout(() => {
  this.makeAIMove();
}, 400);

400ms延迟的设计考量:

  1. 用户体验:给AI"思考"的感觉
  2. 避免UI卡顿:困难模式的Minimax搜索需要200-500ms,setTimeout让搜索在下一个事件循环中执行
  3. 可取消:保存timerId用于清理

定时器清理

typescript 复制代码
aboutToDisappear(): void {
  if (this.aiTimerId !== -1) {
    clearTimeout(this.aiTimerId);
    this.aiTimerId = -1;
  }
}

private backToLevelSelect(): void {
  if (this.aiTimerId !== -1) {
    clearTimeout(this.aiTimerId);
    this.aiTimerId = -1;
  }
  this.inGame = false;
  // ...
}

清理时机:

  1. 页面销毁时(aboutToDisappear)
  2. 返回选关时(backToLevelSelect)

为什么要清理? 如果不清理,定时器在页面销毁后仍会执行makeAIMove,可能访问已销毁的组件状态,导致错误。

AI落子的完整流程

复制代码
玩家点击落子
    ↓
engine.placePiece(玩家黑棋)
    ↓
游戏结束? → 是 → handleGameEnd()
    ↓ 否
aiThinking = true
    ↓
setTimeout(400ms)
    ↓
makeAIMove()
    ↓
aiPlayer.getMove(board) → 计算最佳位置
    ↓
engine.placePiece(AI白棋)
    ↓
aiThinking = false
    ↓
refreshBoardData()
    ↓
游戏结束? → 是 → handleGameEnd()
    ↓ 否
等待玩家下一手

状态文字的变化

typescript 复制代码
private getStatusText(): string {
  if (this.result === GameResult.BLACK_WIN) return '你赢了!';
  if (this.result === GameResult.WHITE_WIN) return 'AI获胜!';
  if (this.result === GameResult.DRAW) return '平局!';
  if (this.aiThinking) return 'AI思考中...';
  return '你的回合';
}

状态文字随游戏进程动态变化:

复制代码
你的回合 → AI思考中... → 你的回合 → ... → 你赢了!/AI获胜!

LoadingProgress组件

typescript 复制代码
if (this.aiThinking) {
  LoadingProgress()
    .width(16)
    .height(16)
    .margin({ left: 8 })
    .color('#FA8C16')  // 橙色加载圈
}

LoadingProgress是ArkUI内置的加载动画组件------一个旋转的圆圈,在AI思考时显示。

总结

AI异步落子的设计要点:

  1. setTimeout延迟:400ms模拟AI"思考"
  2. aiThinking状态:UI展示+交互锁双重作用
  3. LoadingProgress:内置加载动画
  4. 定时器清理:页面销毁/返回时清除
  5. 流程清晰:玩家落子→延迟→AI落子→刷新

这种异步设计让对战体验自然流畅,避免了"AI瞬间响应"的违和感。

相关推荐
乃嘿仔3 分钟前
AI 热点日报 · 2026-10-01
开发语言·人工智能·php
AAASilverwolf4 分钟前
GPT-6 Sol 与 Luna 上线:Astra 的能力下放,价格却降了 50%?
人工智能·gpt·api
欣欣之王来了5 分钟前
AI合规专项:AI自动化决策的合规管控要点
运维·人工智能·自动化
科技峰行者6 分钟前
Bedrock AgentCore在亚马逊云科技中国区域正式可用,助力企业加速AI Agent规模化部署
人工智能·科技·agent·亚马逊·亚马逊云科技
fellow9916 分钟前
把 DeepSeek Harness 搬进鸿蒙的 8 个坑
华为·harmonyos
海盗123417 分钟前
微软技术日报 2026-10-01:VS Code 1.140 让模型互相挑错,EWS 今天起关停
人工智能·驱动开发·microsoft·机器人·aigc
richard_yuu19 分钟前
Haykin 精讲终篇:从感知器到深度学习——一部神经网络的「进化史」
人工智能·深度学习·神经网络
海宇AI19 分钟前
零信任架构实战:基于海宇车辆估值构建自动化二手车收车测算网关
运维·人工智能·架构·自动化
agicall.com25 分钟前
信创电话助手录音盒多路设备配置教程
人工智能·语音识别·信创电话助手·座机语音转文字·固话座机录音转文字
MiYi1240626 分钟前
2026 企业 AI 办公工具选型指南:从需求分析到任务交付的完整评估框架
大数据·人工智能