
简介基于cocos creator开发的单机中国象棋资源面向游戏开发学习者与象棋算法爱好者。电脑AI采用经典Alpha-Beta剪枝算法并划分简单、普通、困难三档棋力困难模式已具备较强对战能力可直接体验或作为策略游戏研究样例。整套资源共730个文件压缩包约17.89MB以json配置、png图片、prefab预制体、js脚本、map地图等为主另含plist图集、ttf字体、fire场景及mp4演示视频覆盖从场景搭建、界面资源到核心逻辑的完整工程结构。目前已有1275人学习下载。读者可借此了解Cocos Creator工程的组织方式分析AI走棋与剪枝的实现思路也可替换资源或扩展棋力算法适合有一定JavaScript基础、想动手实践棋类AI的开发者。 做了几年小游戏这阵子有个老同学让我帮忙做一款中国象棋的Demo说是要用 Cocos Creator 打包成APK给家里的老爷子在手机上玩。说实话市面上现成的象棋源码不少但要么是Laya的、要么是白鹭的直接拿来在 Cocos Creator 里跑总会有一堆兼容问题。与其改别人的烂摊子不如从头捋一遍。这篇文章我就把从零开始做《中国象棋 Cocos Creator 版》的完整思路和关键代码分享出来包括棋盘渲染、走棋规则、简单的电脑AI以及打包安卓APK时容易踩的坑。想自己动手写一个棋类游戏的朋友这篇应该能帮你省下不少时间。1. 项目整体设计先把棋局想明白1.1 为什么用 Cocos Creator 2.x 而不是 3.x先说结论这个项目用的是 Cocos Creator 2.4.x。原因很直接——2.4 的 API 稳定、文档全、社区资料丰富打包 APK 的流程也相对成熟。3.x 虽然新但改版幅度大很多老教程直接白看而且 2D 棋类游戏根本用不上 3D 那套东西没必要为了新版而新版。如果你的项目是从零开始且必须用 3.x那核心思路也是一样的就是组件挂载、坐标转换、事件监听这些 API 名字变了而已。1.2 功能需求拆解拿象棋来说最基本的玩法闭环是棋盘渲染10横线 × 9竖线楚河汉界、九宫、炮位标记棋子摆盘红黑双方各16子走棋规则车的直线、马的日字、象的田字、士的斜线、将帅九宫、炮的翻山吃子判定不能吃自己的子不能送将将帅不能照面胜负判定将帅被将死、困毙简单电脑AI残局或者随机走子做Demo够了这里最容易被新手忽略的是“走棋合法性校验”。很多第一次写象棋的人只顾着棋子移动忘了“走完这步棋之后自己的帅将还在不在”这个核心规则——也就是所谓的“不能送将”。这个必须在所有走棋逻辑的最后统一校验而不是每个棋子单独判断否则会出现“吃掉了对方的老将但自己也被将军”这种逻辑漏洞。1.3 数据模型设计棋局的数据结构我用了一个一维数组长度90对应9行×10列。节点下标和坐标的换算公式是行 Math.floor(index / 9) 列 index % 9棋子的类型用数字表示数字含义0空位1帅2仕3相4马5车6炮7兵黑方用负数表示-1为将-7为卒以此类推。这样判断敌我只需要value * value 0同号是自己人异号是敌人。这个模型的好处是判断吃子只需要检查目标位置的值是否是异号渲染棋子时用value的绝对值找对应的贴图资源即可非常干净。2. 棋盘与棋子的渲染Cocos Creator 里的坐标换算2.1 棋盘节点搭建在场景里建一个空节点作为棋盘根节点挂一个Graphics组件用来画线这样不用准备棋盘图片素材纯代码绘制后续换皮肤也方便。关键代码大概是这样的const g this.node.getComponent(Graphics); g.lineWidth 2; g.strokeColor new Color(72, 46, 23, 255); // 画横线10条 for (let i 0; i 10; i) { g.moveTo(0, i * CELL_SIZE); g.lineTo(8 * CELL_SIZE, i * CELL_SIZE); } // 画竖线9条中间第四到第五行之间断开留出河界 for (let j 0; j 9; j) { if (j 0 || j 8) { g.moveTo(j * CELL_SIZE, 0); g.lineTo(j * CELL_SIZE, 9 * CELL_SIZE); } else { g.moveTo(j * CELL_SIZE, 0); g.lineTo(j * CELL_SIZE, 4 * CELL_SIZE); g.moveTo(j * CELL_SIZE, 5 * CELL_SIZE); g.lineTo(j * CELL_SIZE, 9 * CELL_SIZE); } } // 画九宫斜线、炮位、兵位等标记……炮位和兵位的标记是棋盘美感的细节。炮位我用两个小的交叉线段表示兵位用小圆点。这些都可以用Graphics的circle方法画出来注意坐标要落在交叉点上别偏移。2.2 棋子的坐标换算棋子在棋盘上的位置不是标准网格坐标而是交叉点。所以棋子的摆盘要按“交叉点”定位而不是“格子中心”。坐标换算的核心是先把棋盘的左下角锚点对齐到场景坐标然后x col * CELL_SIZE BOARD_OFFSET_X y row * CELL_SIZE BOARD_OFFSET_Y这里最容易出问题的是行、列的方向。Cocos Creator 的坐标系是 y 轴向上和数组的行索引正好相反。我建议统一用“行从下往上、列从左往右”来定位棋子即[0][0]对应棋盘左下角。这样在写走棋规则时向上走就是row 1向下走就是row - 1符合直觉。2.3 棋子的节点结构与点击事件每个棋子是一个独立的Sprite节点挂一个Pieces脚本组件记录两个属性Row和Col以及Value。点击事件用Node的on(Node.EventType.TOUCH_END)来监听。注意棋子节点一定要记得UITransform的ContentSize设置得比棋子图片大一些比如棋子直径64触摸区域可以设到80。否则手指粗一点的用户点边缘会没反应。实测下来这是很多新手最容易忽略的体验细节。3. 走棋规则与AI实现不能只会移棋子3.1 走棋合法性的完整校验流程每个棋子的走法规则本质上就是返回一组“可以去的坐标列表”。比如车getValidMoves(row, col, board) { let moves []; const dirs [[1,0],[-1,0],[0,1],[0,-1]]; for (let d of dirs) { let r row d[0]; let c col d[1]; while (inBoard(r,c)) { if (board[r][c] 0) { moves.push([r,c]); } else { if (board[r][c] * this.value 0) { moves.push([r,c]); // 吃子 } break; // 被挡住 } r d[0]; c d[1]; } } return moves; }马的走法要注意“蹩马腿”兵要注意“过河前后方向不同”士和将会被限制在九宫内炮吃子时必须隔一个棋子。3.2 不能送将所有走法的最后一道关卡我之前写过一个版本单独校验每个棋子的移动结果出现了一个很隐蔽的Bug红车可以移动到某一列去将军但它自己移动到那个位置后黑方的炮正好在那条线上隔着马瞄着红帅也就是说红方这一步把自家的帅暴露在炮的威胁下了。这个Bug在“吃子后自曝”的场景里特别容易出现。解决方法很朴素写一个isKingAliveAfterMove(fromRow, fromCol, toRow, toCol, board)函数模拟走子之后检查己方帅/将是否被攻击。如果是则这一步非法。这个函数对所有的“合法移动”结果都要过滤一遍不能只靠单个棋子的走法列表。模拟走棋时的写法要注意先深拷贝棋盘数组再交换值再校验。浅拷贝只会让你改到原数组排查半天指针问题。3.3 电脑AIDemo级别的简单评估函数做给长辈玩的DemoAI不要求多强能走就行。我用了最简单的“贪心评估”对每一步可能走法计算走完后己方棋子的总价值与对方棋子的总价值之差棋子价值车9、马4、炮4.5、兵1、相2、仕2、将1000选择价值增量最大的走法如果是人机对战让电脑走一步等500毫秒再走避免瞬间移动让人困惑。function getBestMove(board, isRed) { let bestScore -Infinity; let bestMove null; for (let r0; r10; r) { for (let c0; c9; c) { let v board[r][c]; if (isRed v 0) continue; if (!isRed v 0) continue; let moves getValidMovesWithCheck(r,c,board); for (let m of moves) { let score evaluate(board, r, c, m[0], m[1]); if (score bestScore) { bestScore score; bestMove {from:[r,c], to:m}; } } } } return bestMove; }这个AI的强度大概介于“完全不会下的人”和“刚会规则的新手”之间作为Demo展示完全够用。想要更强就得加搜索深度和剪枝但体感没必要毕竟棋盘上棋子越多搜索越慢手机端要想流畅只能在启动时把评估函数算完缓存起来。在实现时还有个小细节评估函数里如果吃到了对方的将/帅分数直接给最高。这样AI会优先去捉将而不会傻乎乎地吃个卒。3.4 和棋与特殊局面的判断如果没做“自然限着”或者“60回合不吃子判和”你会碰到一个问题AI和人对弈双方就剩那么几个棋子互相转悠永远结束不了。Demo可以简单处理——连续40步没有吃子就提示和棋。这个数值和正式规则有出入但在实际游玩场景中体验不错不会让人烦躁。4. 打包APKCocos Creator 生成安卓安装包全流程4.1 环境准备与构建打包APK涉及两个工具链缺一不可Android Studio用于编译和签名这里只用到 SDK 和 Build ToolsCocos Creator 的构建发布面板构建步骤在 Creator 菜单栏选择“项目 → 构建发布”平台选择“Android”填好包名建议用com.xxx.chess这种格式不要用默认的com.creator.empty不然后面签名会有坑构建模式选“Release”源码模式不用管点击构建等待生成build/android工程构建完成后用 Android Studio 打开生成的原生工程等 Gradle 同步完然后选择 Build → Build Bundle(s) / APK(s) → Build APK(s)。4.2 构建过程中常见的打包错误我在这里踩过几个实实在在的坑坑一Gradle 版本与 SDK 版本不匹配Cocos Creator 2.4 自带的 Gradle 版本比较老而新安装的 Android Studio 默认用的 JDK 是 17 或更高构建时会直接报错。解决办法是在gradle-wrapper.properties里指定一个支持的 Gradle 版本同时把项目的 JDK 版本调到 1.8。坑二找不到local.properties新克隆的构建工程没有这个文件Android Studio 会报 SDK location not found。直接在local.properties里写sdk.dirC:\\Users\\你的用户名\\AppData\\Local\\Android\\Sdk注意 Windows 路径里的反斜杠要写成两个否则解析失败。坑三64位和32位 so 库问题Cocos Creator 2.4 默认打包包含 arm64-v8a 和 armeabi-v7a 两个架构的 so 库如果你在构建面板只勾了一个但手机上运行还是报找不到 so往往是 Android 模拟器是 x86 架构。真机一般没这个问题开发调试时尽量用真机。4.3 APK瘦身与首屏优化一个空项目打包出来就有几十MB象棋这种2D小游戏纯粹是让玩家等加载体验很差。我做了三件事把包压到了 8MB 左右棋盘、棋子素材用 PNG 压缩格式不要用大尺寸透明背景图把其他语言和不需要的加载资源删掉构建发布面板里去掉不必要的“图集”和“音频”关闭“调试模式”选“发布模式”首屏方面这个体量的游戏加载很快没必要做闪屏但如果你加了启动动画记得把加载脚本挂到常驻节点上保证场景切换时初始化代码不会重复执行。5. 常见问题与调试技巧实录5.1 棋子点击无响应怎么办首选检查UITransform的大小和节点的 zIndex。棋子之间如果有重叠后面的节点会挡住前面的触摸事件。建议所有棋子的 zIndex 设为同一个值选中时临时把 zIndex 调高走完再调回来。这样就不会出现“兵被马挡住点不到”的情况。5.2 走棋后棋盘坐标错乱出现坐标偏移基本可以断定是“行”的方向搞反了。Cocos Creator 的getPosition()是左下角为原点但你在视觉上习惯“最上面一行是第一行”。解决方式是在初始化棋子位置时直接把第一行放到(0, 8 * CELL_SIZE)而不是硬套数组下标。5.3 高速重复点击导致棋子瞬移这是事件绑定重复导致的。如果你在挂载on(TOUCH_END)时没有在onDestroy里移除监听场景切换或重开对局后同一个节点的触摸回调会被注册两次点一下会走两步棋。这个Bug排查起来很隐蔽我当时的表象是“棋子会自己跳一下”。处理办法是在组件销毁时统一调用this.node.off(Node.EventType.TOUCH_END, this.onTouchEnd, this)并且所有玩家选择棋子的逻辑入口用同一个方法保证不会重复绑定。5.4 如何提高 AI 的响应速度如果你的 AI 评估函数里有循环嵌套数组拷贝走一步棋可能要卡个一两秒。在 Demo 阶段最简单的优化是把可走位置的for循环里把常量提出来别在循环体内频繁调用this.getComponent()这类获取组件的方法。// 每次循环都拿组件 → 很慢 let pieces this.node.getComponent(Pieces); // 循环前拿一次 → 快得多 const piecesComp this.node.getComponent(Pieces); for (let m of moves) { piecesComp.moveTo(m); }真机测试的时候我碰到一次点击完电脑卡了差不多4秒才有动作把所有getComponent改成缓存引用、数组用TypedArray之后这个数值降到了 200ms 以内体验完全不一样。5.5 完整对局测试的土办法自己跟自己下棋很容易漏掉边角情况。我最后是用了一个“双人模式”来测试的——就是一个屏幕内红黑双方都能点击移动这样我坐在电脑前左手右手交替下棋把能想到的残局都走了一遍。测试过程中一旦出现非法走子没有拦住的情况就在getValidMovesWithCheck入口处打日志把棋盘数组 dump 出来。这个方法虽然土但效率极高。代码里可以留一个DEBUG_MODE开关线上版本关掉开发版本打开遇到问题直接看打印的棋盘快照比自己瞎猜坐标快得多。6. 写在最后的几点经验打包APK实际跑在Android真机上之后真机性能和模拟器差距明显。老款手机开高精度渲染反而有可能出现触摸偏移。如果美术素材不是必须高清建议把Canvas的适配策略设成“固定宽度”高度自动缩放这样各种屏幕都能正常显示不会出现黑边。另外如果准备给长辈用字号和棋子的尺寸尽量大一些。我的棋盘用了 64 像素直径的棋子在6.7英寸手机上看起来刚好再小就费眼了。做这个小项目最大的体会是棋类游戏表面上逻辑简单但真正要做得“没人骂”需要花不少功夫在边界条件和交互手感上。强烈建议在写规则时把“走棋合法性”和“走棋效果”分成两个独立模块前者只负责判断后者只负责播放动画和刷新UI这样改AI时完全不用碰渲染代码。最后再分享一个小技巧棋子的选中状态用高亮描边比改贴图颜色更直观也不会破坏原素材。Cocos Creator 2.4 里可以直接给棋子节点的 Sprite 加一层Graphics的圆环描边选中时显示、落子后清除代码量很小但手感提升明显。本文还有配套的精品资源点击获取