游戏设计思路
在迷宫小游戏中,玩家需要探索一个迷宫,找到通往出口的正确路径。迷宫由墙壁和路径格子组成,玩家需要通过按钮来控制角色的移动,寻找正确的路径并避开墙壁。以下是游戏设计的一些关键考虑因素:
- 迷宫生成算法选择:迷宫生成算法直接影响到游戏体验。本游戏选择了深度优先搜索算法(DFS)来生成迷宫。DFS 是一种探索算法,它从一个起点开始,一直探索到迷宫中没有路径可走时结束。这样可以生成一个有迷宫特色且可解的迷宫。
- 角色移动和目标达成:玩家可以通过按钮控制角色向上、向下、向左和向右移动。当玩家尝试移动到一个路径格子时,角色会在目标位置更新。游戏规则要求玩家找到通往出口的正确路径。通过检查玩家位置和目标位置是否相等,可以判断玩家是否成功达到目标。
- 迷宫大小设置:为了增加游戏的多样性,可以在游戏开始前设置迷宫的宽度和高度。通过滑动条或按钮控件,玩家可以调整迷宫的大小。每次改变迷宫大小后,都会重新生成迷宫并将玩家位置重置为起点。
- 用户界面设计:游戏的用户界面应该简洁直观,使玩家能够轻松地理解和操作。通过OnGUII在界面上展示迷宫、角色、出口等元素。
代码结构和功能
以下是代码的主要部分及其功能的更详细介绍:
模型(Model)
- mazeWidth 和 mazeHeight:分别存储迷宫的宽度和高度。
- mazeData:使用二维布尔数组表示迷宫中的墙壁和路径。True 表示墙壁,False 表示路径。
- playerPosition:存储玩家角色的当前位置。
- target:存储出口的位置。
- moveDirection:枚举移动方向
- public Texture2D image_wall 作为墙壁的图片
- public Texture2D image_ground 作为地面的图片
- public Texture2D image_player 作为人物的图片
- public Texture2D image_target 作为出口的图片
控制器(Controller)
- GenerateMaze() 函数:使用DFS算法生成迷宫。首先将所有位置初始化为墙壁,然后通过DFS算法打通墙壁,构建迷宫。
private void GenerateMaze()
{
mazeData = new bool[mazeWidth, mazeHeight];
for (int x = 0; x < mazeWidth; x++)
{
for (int y = 0; y < mazeHeight; y++)
{
mazeData[x, y] = true; // 将所有位置默认设置为墙壁
}
}
DFS(1, 1); // 深度优先搜索构建迷宫
// 设置出口
for (int i = mazeHeight - 1; i >= 0; i--)
{
if (mazeData[mazeWidth - 2, i] == false)
{
mazeData[mazeWidth - 1, i] = false;
target = new int[2] { mazeWidth - 1, i };
break;
}
}
}
- DFS() 函数:DFS算法的实现,用于从给定的起点开始探索,并递归地打通墙壁。
private void DFS(int x, int y)
{
mazeData[x, y] = false; // 将当前位置设置为路径
// 随机排序4个方向
int[] directions = new int[] { 0, 1, 2, 3 };
for (int i = 0; i < directions.Length; i++)
{
int temp = directions[i];
int randomIndex = Random.Range(i, directions.Length);
directions[i] = directions[randomIndex];
directions[randomIndex] = temp;
}
foreach (int direction in directions)
{
int newX = x;
int newY = y;
if (direction == 0) // 向上移动
{
newY -= 2;
}
else if (direction == 1) // 向下移动
{
newY += 2;
}
else if (direction == 2) // 向左移动
{
newX -= 2;
}
else if (direction == 3) // 向右移动
{
newX += 2;
}
// 检验探索位置是否合理
if (newX >= 0 && newY >= 0 && newX <= mazeWidth - 1 && newY <= mazeHeight - 1 && mazeData[newX, newY] == true)
{
int wallX = x + (newX - x) / 2;
int wallY = y + (newY - y) / 2;
mazeData[wallX, wallY] = false; // 打通墙壁
if (newX != 0 && newX != mazeWidth - 1 && newY != 0 && newY != mazeHeight - 1)
{
DFS(newX, newY); // 递归地探索下一个位置
}
}
}
}
- Move() 函数:根据移动方向参数更新玩家位置。根据方向参数的不同,调整玩家位置的x和y坐标。
private void Move(moveDirection md)
{
if (md == moveDirection.up)
{
playerPosition.y--;
}
else if (md == moveDirection.down)
{
playerPosition.y++;
}
else if (md == moveDirection.left)
{
playerPosition.x--;
}
else
{
playerPosition.x++;
}
// Debug.Log(playerPosition);
// Debug.Log(new Vector2(target[0], target[1]));
// Debug.Log(playerPosition.Equals(new Vector2(target[0], target[1])));
}
- SetMazeSize() 函数:根据给定的宽度和高度设置迷宫的大小。在设置迷宫大小之前,将当前的迷宫清空,并重新生成迷宫。
private void SetMazeSize(int width, int height)
{
mazeHeight = height;
mazeWidth = width;
GenerateMaze();
playerPosition = new Vector2(1, 1);
}
- Reset() 函数:将迷宫大小和玩家位置恢复到默认设置。默认设置是一个6x6的迷宫。
private void Reset()
{
SetMazeSize(6, 6);
}
- IsWin() 函数:判断玩家是否胜利
private bool IsWin()
{
if (playerPosition.Equals(new Vector2(target[0], target[1])))
{
return true;
}
return false;
}
视图(View)
OnGUI() 方法:使用Unity的OnGUI函数,在游戏界面上绘制迷宫、角色、出口等元素。使用不同的纹理来表示墙壁、路径、角色和出口。
- 创建一个自定义样式用来画各种素材
- 游戏开始时,画迷宫。
// 创建一个自定义样式
GUIStyle style_wall = new GUIStyle(GUI.skin.label);
style_wall.normal.background = image_wall;
GUIStyle style_ground = new GUIStyle(GUI.skin.label);
style_ground.normal.background = image_ground;
GUIStyle style_player = new GUIStyle(GUI.skin.label);
style_player.normal.background = image_player;
GUIStyle style_target = new GUIStyle(GUI.skin.label);
style_target.normal.background = image_target;
for (int x = 0; x < mazeWidth; x++)
{
for (int y = 0; y < mazeHeight; y++)
{
// 画迷宫视图
Rect rect = new Rect(x * 20 + 20, y * 20 + 20, 20, 20);
GUI.Box(rect, GUIContent.none, mazeData[x, y] == true ? style_wall : style_ground);
// 画玩家视图
if (playerPosition.x == x && playerPosition.y == y)
{
GUI.Label(rect, GUIContent.none, style_player);
}
// 画出口
GUI.Label(new Rect(target[0] * 20 + 20, target[1] * 20 + 20, 20, 20), GUIContent.none, style_target);
}
}
- 玩家通过点击按钮来控制角色移动。移动方向有上、下、左和右四个选项。每次移动后,调用 Move() 函数根据移动方向更新玩家的位置。
// 玩家控制按钮
if (GUI.Button(new Rect(20, mazeHeight * 20 + 30, 100, 30), "上"))
{
if (playerPosition.y > 0 && !mazeData[(int)playerPosition.x, (int)playerPosition.y - 1])
{
Move(moveDirection.up);
}
}
if (GUI.Button(new Rect(130, mazeHeight * 20 + 30, 100, 30), "下"))
{
if (playerPosition.y < mazeHeight - 1 && !mazeData[(int)playerPosition.x, (int)playerPosition.y + 1])
{
Move(moveDirection.down);
}
}
if (GUI.Button(new Rect(240, mazeHeight * 20 + 30, 100, 30), "左"))
{
if (playerPosition.x > 0 && !mazeData[(int)playerPosition.x - 1, (int)playerPosition.y])
{
Move(moveDirection.left);
}
}
if (GUI.Button(new Rect(350, mazeHeight * 20 + 30, 100, 30), "右"))
{
if (playerPosition.x < mazeWidth - 1 && !mazeData[(int)playerPosition.x + 1, (int)playerPosition.y])
{
Move(moveDirection.right);
}
}
- 当玩家到达出口位置时,判断是否达成目标条件,如果达成,则游戏胜利。
// 判断胜利条件
if (IsWin())
{
GUIStyle style = new GUIStyle(GUI.skin.label);
style.fontSize = 30;
style.normal.textColor = Color.red;
Rect rect = new Rect(350, mazeHeight * 20 + 80, 120, 50);
GUI.Label(rect, "you win!", style);
}
- 玩家可以通过滑动条来调整迷宫的大小,并在调整后重新生成迷宫。
- 提供一个“复原”按钮,用于将迷宫大小和玩家位置恢复到默认设置。
// 设置迷宫大小条
int w = (int)GUI.HorizontalSlider(new Rect(20, mazeHeight * 20 + 80, 100, 30), mazeWidth, 5.0f, 30.0f);
int h = (int)GUI.HorizontalSlider(new Rect(130, mazeHeight * 20 + 80, 100, 30), mazeHeight, 5.0f, 30.0f);
if (w != mazeWidth || h != mazeHeight)
{
SetMazeSize(w, h);
}
// 复原按钮
if (GUI.Button(new Rect(240, mazeHeight * 20 + 80, 60, 30), "默认设置"))
{
Reset();
}
总结
迷宫小游戏展示了一个基于MVC架构的代码结构,将游戏逻辑、数据和视图分离,使代码易于管理和扩展。通过深度优先搜索算法生成迷宫,实现了玩家角色的移动和出口达成、调整迷宫大小等功能。希望这篇博客对于理解迷宫游戏的设计思路和MVC架构的应用有所帮助。
添加素材
额外的工作:需要添加素材图片到Assets里,也需要把脚本里的对象关联到素材。
视频展示
unity Ongui 迷宫小游戏
主要代码
using UnityEngine;
public class MazeGame : MonoBehaviour
{
/* model */
// 迷宫的长和宽
private int mazeWidth = 10;
private int mazeHeight = 10;
// 存储是路还是墙壁
private bool[,] mazeData;
// 存储人物的位置
private Vector2 playerPosition;
// 移动方向
private enum moveDirection
{
up,
down,
left,
right
};
// 出口位置
private int[] target = { 0, 0 };
// 墙壁
public Texture2D image_wall;
// 地面
public Texture2D image_ground;
// 人物
public Texture2D image_player;
// 出口
public Texture2D image_target;
/* system start */
private void Start()
{
GenerateMaze();
playerPosition = new Vector2(1, 1);
}
/* controller */
// 生成迷宫
private void GenerateMaze()
{
mazeData = new bool[mazeWidth, mazeHeight];
for (int x = 0; x < mazeWidth; x++)
{
for (int y = 0; y < mazeHeight; y++)
{
mazeData[x, y] = true; // 将所有位置默认设置为墙壁
}
}
DFS(1, 1); // 深度优先搜索构建迷宫
// 设置出口
for (int i = mazeHeight - 1; i >= 0; i--)
{
if (mazeData[mazeWidth - 2, i] == false)
{
mazeData[mazeWidth - 1, i] = false;
target = new int[2] { mazeWidth - 1, i };
break;
}
}
}
private void DFS(int x, int y)
{
mazeData[x, y] = false; // 将当前位置设置为路径
// 随机排序4个方向
int[] directions = new int[] { 0, 1, 2, 3 };
for (int i = 0; i < directions.Length; i++)
{
int temp = directions[i];
int randomIndex = Random.Range(i, directions.Length);
directions[i] = directions[randomIndex];
directions[randomIndex] = temp;
}
foreach (int direction in directions)
{
int newX = x;
int newY = y;
if (direction == 0) // 向上移动
{
newY -= 2;
}
else if (direction == 1) // 向下移动
{
newY += 2;
}
else if (direction == 2) // 向左移动
{
newX -= 2;
}
else if (direction == 3) // 向右移动
{
newX += 2;
}
// 检验探索位置是否合理
if (newX >= 0 && newY >= 0 && newX <= mazeWidth - 1 && newY <= mazeHeight - 1 && mazeData[newX, newY] == true)
{
int wallX = x + (newX - x) / 2;
int wallY = y + (newY - y) / 2;
mazeData[wallX, wallY] = false; // 打通墙壁
if (newX != 0 && newX != mazeWidth - 1 && newY != 0 && newY != mazeHeight - 1)
{
DFS(newX, newY); // 递归地探索下一个位置
}
}
}
}
// 移动位置
private void Move(moveDirection md)
{
if (md == moveDirection.up)
{
playerPosition.y--;
}
else if (md == moveDirection.down)
{
playerPosition.y++;
}
else if (md == moveDirection.left)
{
playerPosition.x--;
}
else
{
playerPosition.x++;
}
// Debug.Log(playerPosition);
// Debug.Log(new Vector2(target[0], target[1]));
// Debug.Log(playerPosition.Equals(new Vector2(target[0], target[1])));
}
// 设置迷宫大小
private void SetMazeSize(int width, int height)
{
mazeHeight = height;
mazeWidth = width;
GenerateMaze();
playerPosition = new Vector2(1, 1);
}
// 默认设置
private void Reset()
{
SetMazeSize(6, 6);
}
private bool IsWin()
{
if (playerPosition.Equals(new Vector2(target[0], target[1])))
{
return true;
}
return false;
}
/* view */
private void OnGUI()
{
// 创建一个自定义样式
GUIStyle style_wall = new GUIStyle(GUI.skin.label);
style_wall.normal.background = image_wall;
GUIStyle style_ground = new GUIStyle(GUI.skin.label);
style_ground.normal.background = image_ground;
GUIStyle style_player = new GUIStyle(GUI.skin.label);
style_player.normal.background = image_player;
GUIStyle style_target = new GUIStyle(GUI.skin.label);
style_target.normal.background = image_target;
for (int x = 0; x < mazeWidth; x++)
{
for (int y = 0; y < mazeHeight; y++)
{
// 画迷宫视图
Rect rect = new Rect(x * 20 + 20, y * 20 + 20, 20, 20);
GUI.Box(rect, GUIContent.none, mazeData[x, y] == true ? style_wall : style_ground);
// 画玩家视图
if (playerPosition.x == x && playerPosition.y == y)
{
GUI.Label(rect, GUIContent.none, style_player);
}
// 画出口
GUI.Label(new Rect(target[0] * 20 + 20, target[1] * 20 + 20, 20, 20), GUIContent.none, style_target);
}
}
// 玩家控制按钮
if (GUI.Button(new Rect(20, mazeHeight * 20 + 30, 100, 30), "上"))
{
if (playerPosition.y > 0 && !mazeData[(int)playerPosition.x, (int)playerPosition.y - 1])
{
Move(moveDirection.up);
}
}
if (GUI.Button(new Rect(130, mazeHeight * 20 + 30, 100, 30), "下"))
{
if (playerPosition.y < mazeHeight - 1 && !mazeData[(int)playerPosition.x, (int)playerPosition.y + 1])
{
Move(moveDirection.down);
}
}
if (GUI.Button(new Rect(240, mazeHeight * 20 + 30, 100, 30), "左"))
{
if (playerPosition.x > 0 && !mazeData[(int)playerPosition.x - 1, (int)playerPosition.y])
{
Move(moveDirection.left);
}
}
if (GUI.Button(new Rect(350, mazeHeight * 20 + 30, 100, 30), "右"))
{
if (playerPosition.x < mazeWidth - 1 && !mazeData[(int)playerPosition.x + 1, (int)playerPosition.y])
{
Move(moveDirection.right);
}
}
// 判断胜利条件
if (IsWin())
{
GUIStyle style = new GUIStyle(GUI.skin.label);
style.fontSize = 30;
style.normal.textColor = Color.red;
Rect rect = new Rect(350, mazeHeight * 20 + 80, 120, 50);
GUI.Label(rect, "you win!", style);
}
// 设置迷宫大小条
int w = (int)GUI.HorizontalSlider(new Rect(20, mazeHeight * 20 + 80, 100, 30), mazeWidth, 5.0f, 30.0f);
int h = (int)GUI.HorizontalSlider(new Rect(130, mazeHeight * 20 + 80, 100, 30), mazeHeight, 5.0f, 30.0f);
if (w != mazeWidth || h != mazeHeight)
{
SetMazeSize(w, h);
}
// 复原按钮
if (GUI.Button(new Rect(240, mazeHeight * 20 + 80, 60, 30), "默认设置"))
{
Reset();
}
}
}
本unity项目链接
已分享在github上:链接