W03:mvc结构的基于unity的OnGUI的迷宫小游戏

游戏设计思路

在迷宫小游戏中,玩家需要探索一个迷宫,找到通往出口的正确路径。迷宫由墙壁和路径格子组成,玩家需要通过按钮来控制角色的移动,寻找正确的路径并避开墙壁。以下是游戏设计的一些关键考虑因素:

  1. 迷宫生成算法选择:迷宫生成算法直接影响到游戏体验。本游戏选择了深度优先搜索算法(DFS)来生成迷宫。DFS 是一种探索算法,它从一个起点开始,一直探索到迷宫中没有路径可走时结束。这样可以生成一个有迷宫特色且可解的迷宫。
  2. 角色移动和目标达成:玩家可以通过按钮控制角色向上、向下、向左和向右移动。当玩家尝试移动到一个路径格子时,角色会在目标位置更新。游戏规则要求玩家找到通往出口的正确路径。通过检查玩家位置和目标位置是否相等,可以判断玩家是否成功达到目标。
  3. 迷宫大小设置:为了增加游戏的多样性,可以在游戏开始前设置迷宫的宽度和高度。通过滑动条或按钮控件,玩家可以调整迷宫的大小。每次改变迷宫大小后,都会重新生成迷宫并将玩家位置重置为起点。
  4. 用户界面设计:游戏的用户界面应该简洁直观,使玩家能够轻松地理解和操作。通过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上:链接

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值