井字棋的实现
程序员文章站
2024-03-18 23:56:16
...
unity实现井字棋
前言
这是中山大学2020年3D游戏设计第二次作业代码题部分,如有错误,欢迎指正,感谢您的阅读!
要求
仅允许使用 IMGUI 构建 UI
目的
- 了解 OnGUI() 事件,提升 debug 能力
- 提升阅读 API 文档能力
完成效果图
完成gif演示
功能
本代码除井字棋基本的游戏之外,还实现了简单的ai和悔棋的功能。
具体实现
变量定义
这部分实现的是所要使用的变量
private int turn = 1; //1 - Player 1, 2 - Player 2
private int[,] board = new int[3, 3]; //设置3*3棋盘
private int result = 0;//0代表未结束,1为1赢,2为2赢,3为平局
private int[,] retract = new int[3, 3]; //设置下棋记录
private int step = 0;//步数,用于恢复
//背景及棋子
public Texture2D backgrounds;
public Texture2D arrows;
public Texture2D player1;
public Texture2D player2;
开始
开始即调用初始化函数进行初始化即可
void Start()
{
Reset();
}
重置
这部分用于初始化和重置
void Reset()
{
turn = 1;
result = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
board[i, j] = 0;
retract[i, j] = 0;
}
step = 0;
}
检测是否胜利
用一个函数判断是否胜利,胜利需要分为横纵,交查或已经被填满三种情况考虑,并返回状态码。
int check()
{
int total = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (board[i, j] != 0) total++;
for(int i = 0; i < 3; i++)
{
if (board[i, 0] != 0 && board[i, 0] == board[i, 1] && board[i, 0] == board[i, 2]) return board[i, 0];
if(board[0, i] != 0 && board[0, i] == board[1, i] && board[0, i] == board[2, i]) return board[0, i];
}//判断横纵
if (board[1, 1] != 0 && (
(board[0, 0] == board[1, 1] && board[1, 1] == board[2, 2]) ||
(board[0, 2] == board[1, 1] && board[2, 0] == board[1, 1]))) return board[1, 1];//判断交叉
if (total == 9) return 3;//判断是否满
return 0;//未结束
}
提示部分
本部分会自动帮你走一步,优先级为:自己胜利,对手马上胜利,对手制造双2,按序下,代码很简单,不再赘述。
void helpme()//一个人工智障
{
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
if (board[i, j] != 0) continue;
board[i, j] = turn;
if (check()!=0) return;
else board[i, j] = 0;
}//判断是否可以赢
for (int i = 0; i < 3; i++)
{
if (board[i, 0] != 0 && board[i, 0] == board[i, 1] && board[i, 2] == 0) { board[i, 2] = turn; setretract(i, 2); return; }
if (board[i, 1] != 0 && board[i, 1] == board[i, 2] && board[i, 0] == 0) { board[i, 0] = turn; setretract(i, 0); return; }
if (board[i, 2] != 0 && board[i, 0] == board[i, 2] && board[i, 1] == 0) { board[i, 1] = turn; setretract(i, 1); return; }
if (board[0, i] != 0 && board[0, i] == board[1, i] && board[2, i] == 0) { board[2, i] = turn; setretract(2, i); return; }
if (board[1, i] != 0 && board[1, i] == board[2, i] && board[0, i] == 0) { board[0, i] = turn; setretract(0, i); return; }
if (board[2, i] != 0 && board[0, i] == board[2, i] && board[1, i] == 0) { board[1, i] = turn; setretract(1, i); return; }
}//判断横纵
if (board[1, 1] != 0)
{
if (board[0, 0] == board[1, 1] && board[2, 2] == 0) { board[2, 2] = turn; setretract(2, 2); return; }
if (board[2, 2] == board[1, 1] && board[0, 0] == 0) { board[0, 0] = turn; setretract(0, 0); return; }
if (board[0, 2] == board[1, 1] && board[2, 0] == 0) { board[2, 0] = turn; setretract(2, 0); return; }
if (board[2, 0] == board[1, 1] && board[0, 2] == 0) { board[0, 2] = turn; setretract(0, 2); return; }
}
else
{
if (board[0, 0] == board[2, 2] || board[0, 2] == board[2, 0]) { board[1, 1] = turn;setretract(1, 1);return; }
}//判断对角
int cmp = turn % 2 + 1;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
if (board[i, j] == cmp && board[j, i] == 0)
{
board[j, i] = turn;
setretract(j, i);
return;
}
}//防止对手下双二
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
if (board[i, j] == 0)
{
board[i, j] = turn;
setretract(i, j);
return;
}
}//按序放置
}
悔棋
我们使用另外一个3*3的数组来存储下棋的顺序,并使用一个变量来存储当前的步数,设置和悔棋函数如下:
void retractone()
{
if (step == 0) return;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (retract[i, j] == step)
{
step--;
retract[i, j] = 0;
board[i, j] = 0;
turn = turn % 2 + 1;
return;
}
}
void setretract(int i,int j)
{
step++;
retract[i, j] = step;
}
UI界面
最后,是UI界面的实现。UI界面的难点在于调整位置,需要计算好每个框的大小和位置,并设置好按钮。
void OnGUI()
{
GUIStyle playingstyle = new GUIStyle();
playingstyle.normal.textColor = new Color(0, 0, 1);
playingstyle.fontSize = 40;//playing字体
GUIStyle winstyle = new GUIStyle();
winstyle.normal.textColor = new Color(0, 1, 0);
winstyle.fontSize = 40;//win字体
GUIStyle drawstyle = new GUIStyle();
drawstyle.normal.textColor = new Color(1, 0, 0);
drawstyle.fontSize = 40;//draw字体
GUIStyle titlestyle = new GUIStyle();
titlestyle.normal.textColor = new Color(1, 1, 1);
titlestyle.fontSize = 40;//playing字体
GUIStyle playerfont = new GUIStyle();
playerfont.normal.textColor = new Color(1, 0, 0);
playerfont.fontSize = 23;//player字体
//设置背景及基本UI
GUI.Label(new Rect(0, 0, 640, 320), backgrounds);
GUI.Label(new Rect(40, 10, 90, 40), "Tic Tac Toe", titlestyle);
GUI.Label(new Rect(65, 50, 50, 50), player1);
GUI.Label(new Rect(130, 65, 90, 40), "Player1", playerfont);
GUI.Label(new Rect(65, 120, 50, 50), player2);
GUI.Label(new Rect(130, 135, 100, 50), "Player2", playerfont);
if (turn == 1) GUI.Label(new Rect(20, 50, 100, 50), arrows);
else GUI.Label(new Rect(20, 120, 100, 50), arrows);
if (GUI.Button(new Rect(60, 200, 70, 50), "Restart")) Reset();
//设置井字棋棋盘
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
{
if (board[i, j] == 1)
GUI.Button(new Rect(320 + i * 70, 80 + j * 70, 70, 70), player1);
if (board[i, j] == 2)
GUI.Button(new Rect(320 + i * 70, 80 + j * 70, 70, 70), player2);
if (GUI.Button(new Rect(320 + i * 70, 80 + j * 70, 70, 70), ""))
{
if (result == 0)
{
board[i, j] = turn;
turn = turn % 2 + 1;
setretract(i, j);
}
}
}
//结果输出
result = check();
if (result == 1)
{
GUI.Label(new Rect(320, 20, 100, 50), "Player1 wins!", winstyle);
}
else if (result == 2)
{
GUI.Label(new Rect(320, 20, 100, 50), "Player2 wins!", winstyle);
}
else if (result == 3)
{
GUI.Label(new Rect(320, 20, 100, 50), "In a draw!", drawstyle);
}
else
{
GUI.Label(new Rect(320, 20, 100, 50), "Playing...", playingstyle);
}
//调用自动走一步
if (GUI.Button(new Rect(160, 200, 70, 50), "Help Me") && result == 0)
{
helpme();
turn = turn % 2 + 1;
}
//调用悔棋
if (GUI.Button(new Rect(110, 260, 70, 50), "Retract") && result == 0)
{
retractone();
}
}
将上面所有代码组合到一个类中,即可生成脚本。
项目完整文件
如果有需要图片素材资源可以自取(图片都是网上图片ps抠出来的),位于asset/scene中。
上一篇: Latex入门篇
下一篇: 10-在linux上安装tomcat