前言
中山大学数据科学与计算机学院3D游戏课程学习记录博客。
游戏代码:gitee 游戏视频:bilibili
游戏要求
- 游戏内容:井字棋
- 技术限制:仅使用IMGUI构建UI
- 作业目的:
- 了解 OnGUI() 事件,提升 debug 能力
- 提升阅读 API 文档能力
规则分析
- 每个位置只能有一个棋子
- 两人交替下棋,每次下一枚棋子
- 当三个同种棋子连成一线,游戏结束
- 棋盘大小为3x3
游戏实现
游戏实现大概分为两步:制作UI和规则代码化。
制作UI:
- 先选取合适的位置,合适的大小画出棋盘:
int w=Screen.width/2;
GUI.Box(new Rect(w-150,50,300,400),"井字棋");
- 再画出目前的游戏状态,比如play(游戏中),O win(玩家1胜利)等:
GUI.Box(new Rect(w-50,80,100,35),"Play!");
- 然后画出重置按钮,便于重新开始游戏:
if(GUI.Button(new Rect(w-50,415,100,35),"Reset"))
reset();
当reset被点击时需要重新开始游戏,所以需要判断是否被点击。
- 最后用border来表示各个位置的值,画出棋盘上的每个位置:
private int[][] border=new int[3][]{new int[3],new int[3],new int[3]};
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
if(border[i][j]==0){
GUI.Button(new Rect(w-150+i*100,115+j*100,100,100),"O");
}else if(border[i][j]==1){
GUI.Button(new Rect(w-150+i*100,115+j*100,100,100),"X");
}else{
if(GUI.Button(new Rect(w-150+i*100,115+j*100,100,100),"")){
//空按钮被点击的操作
}
}
}
}
border是一个2维数组,其中储存的值代表棋盘上的值。
-1代表该位置没有值,0代表该位置为"O",1代表该位置为"X"。
由于只有当位置为空(border值为-1)时才允许点击放置新值,所以需要判断是否被点击。
至此,完成了对UI的设计。接下来只需要用代码实现井字棋的逻辑即可。
规则代码化:
其实可以发现主要的操作分为两部分,第一部分为点击空按钮是发生的事件,第二部分为点击reset按钮是发生的事件。
点击reset只需要重置所有变量即可,因此把重点放在实现空按钮点击即可。
空按钮点击时发生的事件:
- 判断当前游戏是否已经分出胜负,若分出胜负则不做任何操作:
private int end=0;
if(点击空按钮){
if(end==1) break;
}
使用变量end来标记游戏是否结束,0代表未结束,1代表结束。
- 改变当前位置的值为当前玩家所代表的值:
private int player=0;
if(点击空按钮){
border[i][j]=player;
}
使用变量player来标记当前玩家,0代表"O"玩家,1代表"X"玩家。
- 判断这步之后是否分出胜负:
private int res=-1;
if(点击空按钮){
if(border[0][j]==border[1][j] && border[0][j]==border[2][j])
res=player;
else if(border[i][0]==border[i][1] && border[i][0]==border[i][2])
res=player;
else if(i==j && border[0][0]==border[1][1] && border[0][0]==border[2][2])
res=player;
else if(i+j==2 && border[0][2]==border[1][1] && border[0][2]==border[2][0])
res=player;
}
使用res来标记当前游戏的状态,-1代表游戏正在进行,而0或者1代表决出胜负。
四行if:
第一行if用来判断同一列是否连成线;
第二行if用来判断同一行是否连成线;
第三四两行if用来判断对角线是否连成线。
- 判断是否平局:
private int cnt=0;
if(点击空按钮){
cnt++;
if(res!=-1 && cnt==9) res=2;
if(res!=-1) end=1;
}
使用变量cnt来表示已经点击过的空按钮数量,假如达到9而且未分出胜负,则平局。
res为2代表平局。
如果res不为-1,则代表游戏已经结束,改变end。
- 根据胜负改变"Play!"位置的值:
void printRes(int res){
int w=Screen.width/2;
if(res==0){
GUI.Box(new Rect(w-50,80,100,35),"O win!");
}else if(res==1){
GUI.Box(new Rect(w-50,80,100,35),"X win!");
}else if(res==2){
GUI.Box(new Rect(w-50,80,100,35),"Draw!");
}else{
GUI.Box(new Rect(w-50,80,100,35),"Play!");
}
}
使用函数printRes来根据res改变游戏状态值。
- 该表代表当前玩家的值:
player=player^1;
使用位运算。
点击reset按钮发生的事件:
void reset(){
player=0;
cnt=0;
end=0;
res=-1;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
border[i][j]=-1;
}
}
}
使用reset函数初始化刚刚用过的所有变量。
至此完成了规则代码化。
对于游戏的完整实现代码和游戏视频,请访问前言中的链接。