数字配对——Unity3D小游戏
游戏规则介绍
在一个n×n的棋盘中,有n×n÷2组相同数字。
玩家每次点开一个格子,显示当前格子的数字,下一次点开的数字如果和上一次的相同,那么就匹配成功。如果不相同,那么就隐藏这两次点开的数字。把所有的数字都点开匹配,则游戏结束。
点开一次算一次操作步数,完成游戏所需的操作步数越少越好。
简单来说,就是一个考验记忆力的游戏,能够记住越多点开过的数字,越快完成游戏。(当然运气好也可以快速通关)
游戏效果
下面是游戏中的一些画面。
左侧从上到下依次是:剩余未配对数字组数、当前难度等级、重新开始按钮。
完成游戏的显示效果:
黄字提示完成游戏,并告诉你共用了多少步来完成。
以下是难度等级2的棋盘(6×6)
总共设置了0~3四个难度等级,分别对应2×2,4×4,6×6,8×8四种棋盘大小。
点击左侧中间的按钮(难度等级)来调节当前难度,点击一次,难度加一级,到达3难度以后变回0难度。
游戏实现
首先创建一个空对象,用于执行脚本。
接着在Assets创建C#脚本文件Game.cs(具体代码写在Game.cs中)
代码采用MVC框架,即Model-View-Controller(模型-视图-控制器) 模式
先定义一些所需的数据模型
private static int size = 4;//棋盘行、列格子数
private int[,] chessBoard;//实际棋盘数字
private bool[,] showBoard;//是否显示棋盘格子
private int cell_size = 60;//每个格子长、宽
private int showed;//已翻开未配对的数字 (-1表示不存在)
private int showed_i, showed_j;//已翻开未配对的数字的坐标 (-1表示不存在)
private int count;//步数
private int remain;//剩余未匹配的数字对
private int mode = 1;//游戏难度
再定义视图,即游戏显示界面。
void Start()//进行游戏初始化
{
Init();
}
void OnGUI()//一直渲染
{
//数字字体设置
GUIStyle fontStyle = new GUIStyle();
fontStyle.alignment = TextAnchor.MiddleCenter;
fontStyle.fontSize = cell_size / 2;
fontStyle.normal.textColor = Color.white;
//背景框
GUI.Box(new Rect(250, 100, size * cell_size + 10, size * cell_size + 10), "");
//提示剩余未配对
GUI.Box(new Rect(100, 100, 100, 50), "remain:\n " + remain + " pairs");
//重开按钮
if (GUI.Button(new Rect(100, 250, 100, 50), "Restart")) Init();
//选择难度
if (GUI.Button(new Rect(100, 180, 100, 60), "Difficulty Level\n" + mode))
{
//点击提升难度
AddDifficulty();
}
//方格棋盘
if (!GameOver())
{
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
//还没翻开的格子,点击方格翻开
if (showBoard[i, j] == false && GUI.Button(new Rect(255 + j * cell_size, 105 + i * cell_size, cell_size, cell_size),""))
{
FlopCell(i, j);
}
else if (showBoard[i, j] == true )//翻开的格子
{
GUI.Button(new Rect(255 + j * cell_size, 105 + i * cell_size, cell_size, cell_size), chessBoard[i, j] + "", fontStyle);
}
}
}
}
else//游戏结束提示
{
fontStyle.fontSize = 20;
fontStyle.normal.textColor = Color.yellow;
GUI.Box(new Rect(250, 10, 200, 100), "Congratulations! \n You have finished in \n " + count + " steps!", fontStyle);
}
}
最后写控制器函数,用于处理数据交互,修改模型的数据
void Init()
{
chessBoard = new int[size, size];
showBoard = new bool[size, size];
count = 0;//步数清零
remain = size * size / 2;//共有格子数的一半的数字对
showed = showed_i = showed_j = -1;//已翻开数字和坐标都不存在
int k = 0;
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++)
{
chessBoard[i, j] = (k++) / 2;
showBoard[i, j] = false;
}
Shuffle(chessBoard);
}
IEnumerator Wait(int i,int j)
{
yield return new WaitForSeconds(0.3f);
showBoard[i, j] = showBoard[showed_i, showed_j] = false;
showed = showed_i = showed_j = -1;
}
//随机洗牌
void Shuffle(int[,] deck)
{
for (int i = 0; i < size * size ; i++)
{
int temp = deck[i / size, i % size];
int randomIndex = Random.Range(i, size * size );
deck[i / size, i % size] = deck[randomIndex / size, randomIndex % size];
deck[randomIndex / size, randomIndex % size] = temp;
}
}
void AddDifficulty()
{
if (mode < 3)//难度等级范围0-3
mode++;
else mode = 0;
size = (mode + 1) * 2;
Init();
}
//翻开方格
void FlopCell(int i, int j)
{
count++;
showBoard[i, j] = true;
if (showed == -1)//未有翻开or翻开的都配对
{
showed = chessBoard[i, j];
showed_i = i;
showed_j = j;
}
else//已经有一个翻开但未配对的格子
if (showed == chessBoard[i, j])//上次翻开的与这次翻开的匹配
{
remain--;
showed = showed_i = showed_j = -1;
}
else//不匹配
{
StartCoroutine(Wait(i,j));//等待0.3s后盖上
}
}
bool GameOver()
{
if (remain <= 0)
return true;
return false;
}
这个小游戏实现的关键,在于如何处理翻开格子。
不难想到,需要记录上一次翻开的数字(showed)是什么,从而判断这次翻开的数字是否匹配。
而如果不匹配,还需要重新盖上这两个格子,所以还需要上一次翻开的格子坐标(showed_i, showed_j)才能实现。
另外,除了一个记录数字的二维数组(chessboard),还用了一个同样大小的数组(showboard)来存储当前格子是否需要翻开。
在实现了这些之后,游戏基本能正常运行了。但是很快就发现了一个被忽略的问题:玩家看不到第二次翻开时,这个不匹配的数字是什么。因为计算机执行速度极快,显示的数字一闪而过,玩家根本无法察觉这个数字是什么。
这就需要等待执行了。通过网上资料,学到了如何使用协程执行等待函数,从而实现翻开的数字等一小会再消失。
总结
在开发这个小游戏的过程中,学到了如何在Unity中等待执行代码,对于GUI的一些组件的基本熟悉了,同时也对MVC框架模式有了更深的体会。看着自己亲自完成游戏,虽然简陋,但有股莫名的自豪感!