# TicTacToe **Repository Path**: lenrony/tic-tac-toe ## Basic Information - **Project Name**: TicTacToe - **Description**: No description available - **Primary Language**: C# - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-09-22 - **Last Updated**: 2021-09-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: Csharp ## README # TicTacToe项目说明 Author: [@Lenrony](https://gitee.com/lenrony) ## 项目介绍 本项目是 3D 游戏编程与设计课程的一次编程实践作业,按技术限制的要求,仅使用 IMGUI 构建 Unity 中的 GUI ,基于 ECS 的基本理念实现一个小游戏。本项目实现了一个简单的井字棋游戏,游戏展示界面如下: ![img1](./pic/img1.png) ## 项目配置 为了可以在 Unity 上搭建本项目,此处提供必要的配置说明。 首先打开 Unity,新建一个 2D 项目。然后将本仓库中 Assets 内的所有图片和 C# 脚本代码放到该项目的 Assets 中。如图: ![img2](./pic/img2.png) 然后,新建一个空对象。Unity 菜单栏 → GameObject → Create Empty,将空对象重命名为 “GameStart”。然后拖动 Assets 中的 C# 代码 TicTacToe,拖到该对象中,使脚本代码作为该对象的一个组件。 ![img3](./pic/img3.png) 接着将 Assets 中的 Background 拖入 GameStart 对象的 TicTacToe 组件的 Img 中,作为背景图。Assets 的其余两张图片同理,见下图。 ![img4](./pic/img4.png) 然后,为了使游戏镜头适配背景图片的大小,我们需要进行微调。在 Game 视图下添加镜头预设(Add New Item),Label 可改为 BGfixed,Type 改为 Fixed Resolution,Width & Height 设为 1360 × 850。这样,开启游戏后的背景就可以铺满镜头。 ![img5](./pic/img5.png) 最后,点击运行,等待片刻便出现初始界面。 ![img6](./pic/img6.png) ## 实现过程 ### 1. 定义变量 设计 int 型变量 turn 来记录当前回合的操作者;设计 3 × 3 二维数组变量 state 来保存九宫格的当前状态;设计 result 来记录当前游戏进行的状态(游戏进行中、某一方获胜或平局)。用 img、img1、img2 分别表示背景插图、Player1 棋子和 Player2 棋子的插图。 ```c# private int turn = 1; // 记录当前回合的操作者,1代表Player1,0代表Player2 private int[,] state = new int[3, 3]; // 九宫格状态 private int result = 0; // 0表示游戏进行中,1表示Player1获胜,2表示Player2获胜,3表示平局 // 界面背景及棋子的插图 public Texture2D img; public Texture2D img1; public Texture2D img2; ``` ### 2. 游戏初始化的方法 用 Start 方法调用 Reset 方法对游戏进行初始化。Reset 方法中,置 turn 为 1 表示当前回合由 Player1 主导;置 result 为 0 表示当前对局进行中;将 state 清零表示清空当前九宫格中的棋子。 ```c# // 重置游戏,将游戏状态恢复为初始状态 void Reset() { turn = 1; result = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { state[i, j] = 0; } } } // 开始游戏 void Start() { Reset(); } ``` ### 3. 判断游戏结束的条件 游戏的状态有四种:1. 进行中;2. Player1 获胜;3. Player2 获胜;4. 平局。 从 state 中获取判断游戏状态的信息:当一方玩家的三枚棋子呈横向连线、纵向连线或斜向连线时,判断该玩家获胜。若双方的棋子都无法呈连线状,则计算当前棋局上棋子的数目 count,如果棋子数等于 9 ,说明双方已经陷入僵局,判定平局;如果棋子数小于 9,说明此时棋局仍在进行中。 ```c# // 判断游戏结束的条件 int check() { // 判断横向连线 for (int i = 0; i < 3; i++) { if (state[i,0] != 0 && state[i,0] == state[i,1] && state[i,1] == state[i,2]) { return state[i, 0]; } } // 判断纵向连线 for (int j = 0; j < 3; j++) { if (state[0,j] != 0 && state[0,j] == state[1,j] && state[1,j] == state[2,j]) { return state[0, j]; } } // 判断斜向连线 if (state[1,1]!=0 && (state[0,0] == state[1,1] && state[1,1] == state[2,2] || state[0,2] == state[1,1] && state[1,1] == state[2,0])) { return state[1, 1]; } // 判断平局 int count = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (state[i,j] != 0) { count++; } } } if (count == 9) { return 3; } // 如果以上情况都不出现,则返回0默认对局进行中 return 0; } ``` ### 4. 显示界面信息 总体来说,游戏界面的内容有:标题(TicTacToe)、游戏背景、双方棋子的贴图及描述、主动重置按钮、当前游戏状态提示字和九宫格棋盘。通过调用 IMGUI 中的 Label、Button 等组件可以完成场景的布置,为了节省篇幅,此处不粘贴相关代码,详见代码 TicTacToe.cs。 ### 5. 显示棋盘及下棋控制逻辑 为了产生点击一下就能放置棋子的效果,采用 Button 组件来设计九宫格。当某一格子对应的 state 元素为 0 时,某一玩家回合下,该玩家点击此格子就可以在格子的位置上放置他的棋子,并将回合交给另一方;当某一格子对应的 state 元素不为 0 时,该格子的位置上显示 state 对应玩家的棋子,点击该格子将无事件触发。 ```c# // 九宫格 for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (state[i,j] == 1) // Player1的棋子 { GUI.Button(new Rect(Screen.width / 2 - 225 + i * 150, Screen.height / 2 - 225 + j * 150, 150, 150), img1); } if (state[i, j] == 2) // Player2的棋子 { GUI.Button(new Rect(Screen.width / 2 - 225 + i * 150, Screen.height / 2 - 225 + j * 150, 150, 150), img2); } if (GUI.Button(new Rect(Screen.width / 2 - 225 + i * 150, Screen.height / 2 - 225 + j * 150, 150, 150), "")) // 空白格 { if (result == 0) // 当对局进行中触发按钮 { if (turn == 1) // Player1的回合 state[i, j] = 1; else // Player2的回合 state[i, j] = 2; turn = 1 - turn; // 触发按钮后回合翻转 } } } } ``` ## 效果展示 见[Unity小项目《TicTacToe》](https://www.bilibili.com/video/BV1Yh411n7v2/) ## 总结 通过本次项目作业我初步学会了使用 Unity 的 OnGUI 事件,学习使用 C# 脚本动态地对游戏项目 debug,如调整界面显示信息的方位、修改按钮的响应事件等等。 利用 ECS 架构的基本思路,用实体-组件-系统的方式实现游戏项目,符合软件工程思想中高内聚、低耦合的原则。在本项目中,使用三个主要的变量存储数据:用 turn 记录当前回合的操作者;用 state 记录当前九宫格的状态;用 result 记录当前游戏的状态。其他方法作为控制系统,如 Reset 方法重置以上变量,check 方法通过以上变量判断游戏结束状态,九宫格按钮触发事件则根据上述变量的变化而变化。