技术基础:
本此实践主要使用unity 自带的即时模式GUI(IMGUI)完成游戏设计。IMGUI是Unity主要基于GameObject的UI的完全独立功能系统, 是一个代码驱动的GUI系统。它通过在任何实现它的脚本上调用OnGUI函数来驱动。例如,下面代码:
void OnGUI() {
if (GUILayout.Button("Press Me")) Debug.Log("Hello!");
}
结果是,它将显示下面一个按钮:
当我们要创建IMGUI元素,必须编写进入名为OnGUI的特殊函数的代码,OnGUI()函数就会在每个帧中调用 - 就像Update()函数一样,将元素绘制到屏幕上。
游戏设计思路:
游戏的关键部分是创建一个棋盘,并且当玩家在下棋时,点击棋盘位置可以根据角色不同,填充”X“或者”O“。每下一步棋,程序能够自动检测游戏结果。
1. 关于棋盘,我们可以通过将9个button排列为3*3的阵列来表示,所有格子的状态用3*3的二维数组表示,0-该格为空、1-放置了player1的棋子,2-放置player2的棋子
2. 利用button的特性和OnGUI()函数每帧都会被调用,我们可以点击棋格(button)时,可以设置保存棋格状态的数组,如当turn为1,点击空格时,该格对应状态设为1。并且设置对应关系,根据棋格状态显示不同内容
3. 每走完一步棋,判断该棋子在横向,竖向,对角线方向上是否有三个相同对象连成线,从而判断游戏结果
UI设计
UI基于GUI方法的固定显示方式
游戏效果展示
字体内容用GUI.Label显示,可以设置字体的颜色和大小。参数本身提供了一个Rect()函数。Rect()定义了四个属性:最左侧位置,最顶层位置,总宽度,总高度, 坐标系是基于左上角的。例如Rect(10,20,300,100)定义一个Rectangle,从坐标:10,20开始,到坐标310,120结束。
button可以用来显示文本或图像,当点击button时,或触发相应的事件
在本实践中,button中显示图片时,图片不能完全填充button,效果比较难看。本实践设置了下button的格式:首先,在代码中声明一个GUIStyle属性,然后将它应用于button上面,在设置button的背景色为白色
实验代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TicTacToe : MonoBehaviour
{
private int turn = 1;//1-player1,0-player2
private int [,] board = new int [3,3];//记录棋盘状态
private int result = 0;//0-游戏中,1-player1获胜,2-player2获胜,3-平局
//button样式设置
public GUIStyle customButton;//背景设置为白色,优化button图片显示
//声明背景图片和棋子图片内容
public Texture2D backgroundImg;
public Texture2D img1;
public Texture2D img2;
void Reset()
{
//重置结果,清空棋盘内容
result = 0;
turn = 1;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
board[i, j] = 0;
}
void Start()
{
Reset();
}
void OnGUI()
{
//GUIStyle
//标题字体样式
GUIStyle fontStyle1 = new GUIStyle();
fontStyle1.normal.background = null;
fontStyle1.normal.textColor = new Color(100, 100, 0);
fontStyle1.fontSize = 20;
//文本字体格式
GUIStyle fontStyle2 = new GUIStyle();
fontStyle2.normal.background = null;
fontStyle2.normal.textColor = new Color(0, 1, 0);
fontStyle2.fontSize = 15;
//player
//设置背景
GUI.Label(new Rect(0, 0, 720, 405), backgroundImg);
GUI.Label(new Rect(190, 15, 100, 100), "Welcome to TicTacToe",fontStyle1);
//显示2个玩家
GUI.Label(new Rect(50, 50, 50, 50), img1);
GUI.Label(new Rect(120, 75, 100, 50), "Player1",fontStyle2);
GUI.Label(new Rect(50, 120, 50, 50), img2);
GUI.Label(new Rect(120, 145, 100, 50), "Player2",fontStyle2);
//显示当前走棋的玩家
GUI.Label(new Rect(30, 25, 100, 50), "Turn:",fontStyle2);
if (turn == 1)
GUI.Label(new Rect(30, 60, 100, 50), ">",fontStyle2);
else
GUI.Label(new Rect(30, 130, 100, 50), ">",fontStyle2);
if (GUI.Button(new Rect(250, 60, 70, 50), "RESET"))
Reset();
//刷新棋盘
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) {
if (board[i, j] == 1)
GUI.Button(new Rect(210 + i * 50, 140 + j * 50, 50, 50), img1, customButton);
if (board[i, j] == 2)
GUI.Button(new Rect(210 + i * 50, 140 + j * 50, 50, 50), img2, customButton);
if(GUI.Button(new Rect(210 + i * 50, 140 + j * 50, 50, 50), ""))
{
if(result == 0)
{
if (turn == 1)
board[i, j] = 1;
else
board[i, j] = 2;
turn = 1 - turn;
}
}
}
//显示结果
GUI.Label(new Rect(50, 210, 100, 50), "Result:",fontStyle2);
result = check();
if (result == 1)
GUI.Label(new Rect(105, 210, 100, 50), "Player1 wins!",fontStyle2);
else if(result == 2)
GUI.Label(new Rect(105, 210, 100, 50), "Player2 wins!",fontStyle2);
else if(result == 3)
GUI.Label(new Rect(105, 210, 100, 50), "No one wins!",fontStyle2);
else
{
GUI.Label(new Rect(105, 210, 100, 50), "playing...",fontStyle2);
}
//检查游戏结果
int check()
{
int count = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
if (board[i, j] != 0)
count++;
}
for (int i = 0; i < 3; ++i)
{
if (board[i, 0] != 0 && board[i, 0] == board[i, 1] && board[i, 1] == board[i, 2])
return board[i, 0];
}
for (int j = 0; j < 3; ++j)
{
if (board[0, j] != 0 && board[0, j] == board[1, j] && board[1, j] == board[2, j])
return board[0, j];
}
if (board[1, 1] != 0
&& board[0, 0] == board[1, 1] && board[1, 1] == board[2, 2]
||board[0, 2] == board[1, 1] && board[1, 1] == board[2, 0])
return board[1, 1];
if (count == 9)
return 3;
return 0;
}
}
}