简介:这是一份面向 Unity 开发者的棋盘游戏合集源码,涵盖中国象棋、围棋、五子棋、国际象棋、日本将棋、黑白棋、扫雷、数独等十余款经典棋类与益智游戏。项目既包含单机 AI 对战,也实现了部分联机玩法,代码结构清晰,适合作为游戏逻辑、AI 算法及网络同步的学习范本,也可直接改造用于商业或毕设项目。整份资源共 2000 个文件,压缩包体积 85.61MB,其中有 535 个 C# 脚本、195 个预制体和 19 个 asset 配置,同时包含 997 个 meta 文件以及 159 个 md 说明文档,能看出工程完整且附带使用说明。基于 Unity 2019.4.24f1c1 及以上版本,额外包含安卓相关 aar 与 dll 插件,方便移动端适配。目前已有 786 人学习下载,适合希望系统了解多种棋盘玩法和 AI 实现的开发者参考。通过本合辑可以一次摸清不同棋类的规则抽象、走法生成、胜负判断与人机交互流程,联机模块也提供了现成的网络框架,对想扩充个人作品集或做游戏原型验证的开发者是一份扎实的实用资源。 做了几年 Unity 开发,棋类游戏一直是我用来练手和梳理基本功的首选项目。它不依赖重美术资源,逻辑密度却高得惊人,一套棋类合集做下来,等于把状态管理、规则引擎、UI 编排、AI 搜索、数据持久化这些 Unity 经典问题全部过了一遍。最近我把中国象棋、围棋、五子棋等棋种整合进了同一个 Unity 工程,整理成了一套可运行的棋盘游戏合集源码,趁热把从设计到落地的完整思路写下来,给想动手做棋类游戏、或者想研究棋类算法的朋友当一份参考地图。
这个合集解决的核心痛点是:很多新手写棋类项目都是每个棋种单独开工程,UI 和输入逻辑重复写,规则模块却耦合在一起,改一个地方崩一片。我在这个项目里把规则引擎与表现层彻底分离,共享框架、各自实现,既能降低维护成本,也方便后续扩展新棋种。无论你是刚学 Unity 想找个练手项目,还是想深入理解 minimax 搜索、蒙特卡洛树搜索这类算法,这套代码都值得拆开看看。
1. 项目整体设计思路:把三种棋装进一个工程
1.1 为什么选择多棋种合一的框架
很多人第一反应是:中国象棋、围棋、五子棋玩法完全不同,硬凑到一个工程里不是自找麻烦吗?实际做下来,我的体会恰恰相反,棋类之间的差异集中在规则引擎层,而工程框架层的需求高度一致——都需要启动流程、场景切换、UI 面板管理、音效播放、存档设置,这些完全可以共用一套代码。
设计的关键在于克制:不要为了统一而强行抽象一套走法接口。五子棋是"空格落子",中国象棋是"棋子沿规则移动",围棋是"区域死活判断",这三者的底层建模差异太大,强行抽象出统一接口反而会让每个棋种都写得很别扭。我的方案是:只约定最小公共接口,比如棋盘的初始化、是否是有效落点、当前轮到哪方、是否结束,具体规则各自实现。这样既省了重复劳动,又不牺牲各棋种的自由度。
1.2 工程目录与模块划分
目录结构决定了项目的可维护性,我在这次整理中把代码按"通用核心 + 单棋种 + 表现层 + AI"四个大块组织。核心层放所有棋种共用的东西,比如 GameManager、场景状态机、UI 面板基类;单棋种模块则完全独立,互不引用。
Assets/Scripts/ Core/ # 通用核心:场景状态机、事件中心、存档服务 GameManager.cs SceneStateMachine.cs EventBus.cs Chess/ # 中国象棋 ChessBoardModel.cs ChessPiece.cs ChessRuleValidator.cs Go/ # 围棋 GoBoardModel.cs GoRuleManager.cs Gomoku/ # 五子棋 GomokuBoardModel.cs GomokuWinDetector.cs UI/ PanelBase.cs BoardInputController.cs AI/ MinimaxAI.cs GomokuEvaluator.cs GoMCTS.cs这样划分有个很直接的好处:新增一个棋种时,不需要改动已有棋种的任何代码。目录里每种棋都是一个完整的功能域,规则逻辑都放在纯 C# 类中,完全不挂 MonoBehaviour,调试和测试都方便很多。
1.3 规则引擎与表现层彻底解耦
我一直强调棋类项目的规则引擎要写成纯 C# 类,不依赖任何 Unity API。这样做的好处有三个:第一,可以直接跑单元测试,把棋盘状态喂进去,断言落子结果是否合法,极大缩短调试时间;第二,以后如果要做联机对战,这套逻辑可以平移成服务器端的判定模块;第三,渲染层只负责"显示状态"和"接收输入",逻辑层只负责"计算状态",两边的 bug 不容易互相污染。
表现层的 BoardInputController 拿到玩家的点击坐标后,只做一件事:把坐标转换成逻辑落点,调用规则引擎的 TryMove 或 PlaceStone 方法,返回成功后再刷新棋盘显示。任何动画、音效、特效都不允许插在规则调用中间,否则状态机和表现会越搅越乱。
2. 核心规则模块拆解:三种棋的规则引擎实现
2.1 中国象棋规则:格点建模、走法与特殊局面
中国象棋的棋盘是 9 列 10 行的格点模型。我用Vector2Int表示坐标,column 和 row 分别对应棋盘的列与行,棋盘本身就是一个byte[10, 9]的二维数组,0 表示空,1 表示红方,2 表示黑方。每一枚棋子的类型和归属则单独存在一个结构体数组里,和棋盘数据分开管理。
走法生成是整个引擎的核心,我在基类中定义了一个统一入口:List<Move> GetLegalMoves(int row, int col),每个棋子类型覆盖这个方法来返回候选走法。以"马"为例,它走"日"字,八个方向都要判断是否蹩马腿,比如向右上跳时,需要检查右侧紧邻的位置是否被占,被占则不能走。拿到候选走法后,还要做一层全局过滤:模拟走完这一步,看己方的将帅是否处于被吃状态,如果是,则裁掉这个走法。这层过滤就是很多新手容易漏掉的"将帅不能照面"规则,没有它,双方将帅会在同一条竖线上隔空相对,局面直接非法。
对局流程上,我把核心机制设计成一个循环模型:一方走完 → 合法性校验 → 更新棋盘 → 检查将军、被将死或和棋 → 切换回合。这个模型无论做人机还是人人对战都通用。特别提醒一点,长将长捉这类循环局面的自动判和很复杂,参考源码里我用的是简单的"步数 + 局面重复次数"阈值判断,够用但不追求极致,如果想做专业级裁判逻辑,还需要叠加 Zobrist 哈希做局面去重。
2.2 围棋规则:气的统计、提子与劫争
围棋的规则模型比象棋更抽象。棋盘是 19×19 的交叉点,落子不再是"移动棋子",而是在某个空点放置己方棋子,然后根据"气"的规则进行提子和禁着点判断。气的定义是:一块棋子连通区域相邻的空点总数。一旦某个连通块的气为 0,这个连通块就要被提走,也就是从棋盘上移除。
我在实现中把提子和气的统计合并成了一个局部更新的过程:每落一子在 (r, c),先检查周围四个相邻位置中对手棋子的连通块是否有气,再检查己方连通块是否有气,顺序有讲究——如果己方落子后形成己方无气,但对手同时无气,按围棋规则先提对方再提己方,实现时要先扫描周围对方的连通块,确认无气就提走,然后再处理己方。这个顺序我曾经写反过,结果遇到"打吃反提"的棋型直接出 bug。
劫争是最让人头疼的规则之一,完整实现需要记录上一手棋面并使用同型禁着判断。我在参考源码里做了简化:只在规则管理器里记录上一手棋的位置和棋子信息,若当前落子导致提子数恰好为 1,且提掉的位置正好是对方上一手落子的位置,判定为"劫",当前这一手不允许落在那个点。这个简化版本能处理 99% 的入门棋局,专业级角度的无休劫等复杂规则不在此列,但作为学习和演示足够。
2.3 五子棋规则:棋盘、落子与胜负判定
五子棋是三款里最简单也最适合入门学习的一个。我用 15×15 的二维数组建模,落子只有空位检查,判定胜负时的核心是方向扫描。每次最后落子的位置为起点,沿四个方向(水平、垂直、两条对角线)分别统计连续同色棋子数,只要其中一组数量达到 5 就判定胜利。
private readonly (int dr, int dc)[] directions = { (1, 0), // 纵向 (0, 1), // 横向 (1, 1), // 右下对角线 (1, -1) // 左下对角线 }; public bool CheckWin(int row, int col, byte player) { foreach (var dir in directions) { int count = 1; count += CountContinuous(row, col, player, dir.dr, dir.dc); count += CountContinuous(row, col, player, -dir.dr, -dir.dc); if (count >= 5) return true; } return false; } private int CountContinuous(int row, int col, byte player, int dr, int dc) { int count = 0; int r = row + dr, c = col + dc; while (r >= 0 && r < 15 && c >= 0 && c < 15 && board[r, c] == player) { count++; r += dr; c += dc; } return count; }只需要扫描以最后落子为中心的四条线,不需要全盘遍历,落子越靠中心,命中五连的效率越高。这种以最后一手为起点的思路,也是其他棋类判定逻辑里最常用的优化方式。
3. 交互与 UI 实现:让棋局在屏幕上"活"起来
3.1 逻辑坐标与屏幕坐标的映射
棋类游戏的交互链路是:玩家点击屏幕 → 得到屏幕坐标 → 转换为棋盘逻辑坐标 → 调用规则引擎。因为三种棋盘尺寸不同,我抽了一个IBoardView接口,内部提供ScreenToLogical(Vector2 pos)和LogicalToScreen(Vector2Int logicalPos)两个方法,每种棋盘各写一套实现。
有一个很关键的优化:不要在 Update 里反复计算坐标映射。棋盘上的格点位置在初始化后是恒定不变的,我在加载棋盘时就把所有逻辑坐标对应的屏幕坐标缓存到一个字典里,点击检测时直接查字典配合反向查找,省掉了大量重复计算。手机屏幕适配方面,我用 Canvas Scaler 将参考分辨率设置为 1920×1080 的等比缩放,棋盘基座和棋子的锚点都放在屏幕中心,横竖屏切换时只需要重新计算一次缓存坐标即可。
3.2 落子动画、提示与输入锁定
动画反馈在棋类游戏中主要有两种:移动类(中国象棋的走子)和放置类(围棋、五子棋的落子)。移动类动画我用协程配合简单的插值函数实现,把棋子从起点平滑移动到终点,300 毫秒左右的时长手感最自然;放置类则给棋子加一个从放大到回弹的缩放入场效果,视觉上更有"啪嗒"落地的感觉。
实战中最容易踩的坑是动画期间的输入锁定。玩家在棋子移动动画没结束的时候连续点击,会导致状态机出现"当前棋子已被移动但棋盘数据还没更新"的异常。我在 BoardInputController 里维护了一个isAnimating标志,任何动画播放期间直接吞掉点击事件。还有一个细节:音效也应该绑定在动画结束的回调里而不是点击瞬间播放,否则快速操作时音效会严重早于实际落子,体验非常差。
3.3 菜单、悔棋、重开与状态流转
UI 面板我用一个轻量级状态机来管理。整个游戏循环被拆分为 MainMenu、GamePlay、Settings、GameOver 四个状态,每个状态对应一个面板,状态切换时统一关闭旧面板、打开新面板。这样一来,从主菜单进入中国象棋、围棋还是五子棋,只是切换对局的模式参数,底层框架完全不用改。
悔棋功能是这次重构中比较满意的一块设计。我没有为每个棋种单独写 Undo 逻辑,而是用"命令模式"统一记录操作:每次落子或走一步,生成一个 Command 压入栈中,悔棋就是弹栈并调用 Undo 方法。中国象棋的 Command 需要记录起止坐标和被吃掉的棋子,围棋需要记录提走的棋子集合,五子棋最简单,只需要记录清空位置。这样无论哪种棋,多步悔棋、重开一局、甚至录制复盘数据都复用同一套命令栈逻辑。
4. AI 与对战逻辑:从无脑随机到有章法的搜索
4.1 五子棋 AI:评估函数与搜索深度
五子棋 AI 是入门搜索算法的绝佳载体。我先写了一个简单的局面评估函数,对棋盘上每个空点打分:落在己方活四附近得分极高,冲三、活三次之,眠二活二较低;再叠加一个中心倾向分,鼓励 AI 抢占中央区域。这个评估函数配合固定深度的贪心搜索,已经能打败大多数随手下的玩家。
要做得更聪明,就得上 minimax + Alpha-Beta 剪枝。我在参考源码里提供了一份可调深度的实现,深度设置为 4 时流畅度还不错,评估函数越精确,搜索深度越有效。一个提升剪枝效率的小技巧是走法排序:先评估每个候选点的初始分数,按分数从高到低排序后再进入递归搜索,这样能显著提高剪枝率。注意控制搜索深度,别贪,19×15 的棋盘上全量搜索 6 层会明显卡顿,我在实现里会把候选点裁剪到最近落子周围的若干格之内。
4.2 中国象棋 AI:走法排序让剪枝更高效
中国象棋的 AI 复杂度明显高于五子棋。每个局面平均有几十种合法走法,搜索树非常庞大,如果不做剪枝优化,3 层深度都跑不动。我的实现分三层:走法生成、局面评估和 Alpha-Beta 搜索。走法生成依赖第二章的规则模块,每个合法走法都会带着"吃子、将军"这类特征标记;局面评估则从子力价值和位置价值两部分打分。
子力价值我参考了传统的经验值:车约 900、马约 400、炮约 400、兵卒随位置变化、士象约 200。位置价值用一张 10×9 的估值表实现,比如兵过河后价值会明显上升,马的位置在中心比在边角更有威胁。最关键的一步是走法排序:在搜索前把吃子、将军等高价值走法排在前面,Alpha-Beta 剪枝的效率可以提升好几倍。我实测同样的深度,排序后的搜索时间只有排序前的五分之一左右。如果想更贴近实战,还可以加上若干回合的无吃子强制变招等规则,但这个版本作为参考源码已经够用。
4.3 围棋 AI 的轻量实现方案
围棋因为状态空间太大,传统搜索算法在中大棋盘上几乎不可用。参考资料里我建议做 9 路围棋,配合简化的蒙特卡洛模拟思路:在某个候选点落子后,快速让黑白双方随机落子直到终局,统计该候选点对应的最终胜率。这个思路在面对入门玩家时已经有一定的棋力。
实际实现的时候要注意模拟次数和局时的取舍。我用 9×9 棋盘、每手模拟约 300 次随机对局,单步延时可以控制在 2 秒以内,体验尚可。为了让随机模拟更贴近真实棋理,我加了一个小改进:模拟时优先落子在当前局面"被打吃"的点附近,而不是全局完全随机,步骤虽小,棋力提升却很明显。19 路棋盘建议不要用这种纯随机的方案,运算量太大,只想学习的话可以用 9 路,想商用级别则建议接入成熟的开源棋力引擎。
5. 常见问题与优化实践
5.1 坐标转换、边界与中文适配的坑
先说坐标转换里最容易翻车的点:中国象棋的棋盘是格点,五子棋的棋盘也是交叉点,但围棋的棋子在视图上对应的是"格点交叉"而非"格子中心",如果直接用 RectTransform 的 anchoredPosition 去计算,经常会差半个棋子半径。我统一把棋子渲染的 pivot 设为棋盘交叉点对应的锚点,所有逻辑坐标转换成屏幕坐标时的基准都按 pivo 来算,省去了一大堆偏移调参。
另一个坑是中文适配。棋类游戏面板上大量使用"楚河汉界""黑方胜""玩家白棋"这类中文文案,在老版本 Unity TrueType 字体处理上偶尔会出现中文乱码或者字形缺失。我在项目里统一改用 Font Asset 方式管理字体,并确保场景中的 Text 组件明确指定了支持中文的字体资源,打包时勾选好简体中文语言,实测下来 Android 和 PC 平台都很稳定。还有存档文件里的中文编码,JSON 序列化统一用 UTF-8,避免 Windows 记事本打开乱码。
5.2 棋局存档、复盘与单元测试
棋局存档我设计成了一种通用数据模型:GameRecord,包含棋种标识、创建时间戳、黑白/红蓝身份信息、完整的走子序列。走子序列用List<string>存储,中国象棋每一步记录成类似e2-e4的起始-终点坐标字符串,五子棋和围棋记录成row,col,统一用MoveToString和StringToMove做转换。
[System.Serializable] public class GameRecord { public string gameType; // "chess", "go", "gomoku" public long timestamp; public List<string> moveList; // 走子序列,字符串化 public bool isPlayerFirst; }序列化直接用 JsonUtility 或 Newtonsoft.Json 都可以,我推荐 Newtonsoft.Json,对字典这类结构支持更好。存档路径我放在Application.persistentDataPath下,用游戏类型加时间戳命名文件,这样每个棋种都自动有了独立的存档目录。
复盘功能在这个数据结构上就很简单了:从头开始逐步回放 moveList,每走一步刷一次棋盘。这个功能对调试规则引擎和 AI 特别有用,我在写象棋规则时就是用复盘功能一步步对比真实棋谱,发现了至少三个走法生成的边界 bug。
5.3 性能优化与调优清单
棋类游戏通常不重渲染,性能瓶颈反而集中在规则引擎的高频调用。我的优化经验主要有三个:第一,走法生成时避免频繁 new 对象,定义好 List 和数组后尽量复用,减少 GC 压力;第二,围棋的气统计和提子采用局部更新而不是每次全棋盘遍历,落子周围的 4 个相邻块是主要扫描区域,全盘遍历放到 AI 评估时再做;第三,所有 AI 计算都放进协程或者异步线程中执行,避免主线程卡顿,协程里还可以做到"每模拟若干局让出一次主线程",保证界面流畅。
另外强烈建议给规则引擎写单元测试,不需要装额外测试框架,Unity Test Framework 就够用。每个棋种都准备几组典型的棋局状态,比如中国象棋的蹩马腿、围棋的打吃提子、五子棋的活四走法,断言规则模块的输出是否符合预期。这样后续改 AI 或者修 bug 时,心里会非常有底。
6. 写在最后:多棋种项目串联的那些心得
这套源码最大的价值不在于某个单一棋种的实现有多惊艳,而在于它提供了一个可以横向对比、纵向复用的框架。我在整理过程中最大的体会是:棋类游戏之间的共性比表面上多得多——状态管理、回合流转、交互反馈、命令回滚这些模块一旦抽象到位,新加一个棋种往往只需要补规则和评估函数,框架几乎零改动。
如果你打算自己动手写一套,我的建议是先从五子棋开始,它逻辑最简单,半天就能出一个可玩版本,打着舒服了再扩展中国象棋和围棋。每加一种棋,都回头看看通用框架是否需要微调,这个"扩展—重构—再扩展"的过程,才是这类项目最有价值的修炼。
最后再分享一个小技巧:我习惯在每个棋种的规则引擎文件顶部写一份简单的"棋规说明"注释,把走法、胜负条件、特殊规则都列清楚。这不仅是给自己留档,也是给以后接手这个项目的同伴一份最直观的文档。代码会越改越复杂,但规则文档一旦写明白了,项目的可维护性会一直保持在高位。
本文还有配套的精品资源,点击获取