目录
前言
项目需求
Prim算法生成迷宫
1 算法思路介绍
2 代码示例
3 用于测试的代码
预制体(预制件)
1 定义
2 构建迷宫墙体预制体
完善工程
本节我们来做迷宫生成。生成迷宫的算法其实有很多种,最简单的比如递归。而这一节我们选择prim算法生成迷宫,因为这种算法生成的迷宫较为自然,并且算法原理简易。
使用prim算法和预制体生成迷宫。
本部分中的所有图示采用RPG Maker XP建立。顺便吐槽一句,现在网上流传的有关prim算法生成迷宫的算法描述(那个据说来自维基的)简直是语文不及格产物。
Prim算法与图论有关(我在article105中介绍过基本的图搜索算法和一个使用Ruby书写的搜索模版),它最开始被用于在加权连通图里搜索一个最小生成树。个人认为它还有些像用堆实现的二叉树遍历算法。其具体资料请读者自行学习,笔者在此只给出一个用于迷宫生成场景下较为通俗的描述。
我们假设在区域内有一个N*M列的迷宫。对于迷宫的每个格子,它们只有两个状态:通路/墙。在初始状态下,这个迷宫里的所有格子都是墙。设定一个墙的类Wall,其数据为[Vector2Int pos, Vector2Int relative],也就是其位置向量pos与其相对格子的位置向量relative(在后续步骤中会解释)。同时,我们建立一个墙的空列表M。
然后我们选定一个起点A,将此位置的墙移除:
将与起点A相邻的所有墙加入M中,其中A作为这些墙的父格子。对于一面墙而言,我们称在其父格子另一边的格子为相对格子。它们的例子如图:
在M中随机选择一个元素x。对于x,如果其相对格子为墙,则移除相对格子和x的墙,然后将相对格子作为父格子,将相邻墙加入列表。否则则只从M中移除x。如此循环直到M中为空。
我们会创建一个墙的类Wall与生成迷宫的类Maze。
Wall.cs
public class Wall //内部类Wall
{
public Vector2Int pos; //位置向量
public Vector2Int relative; //相对格子向量
public Wall(Vector2Int pos,Vector2Int anc){
this.pos=pos;
ancToRelative(anc);
}
public void ancToRelative(Vector2Int ancestor){ //输入一个父格子,求出其相对格子并赋值
this.relative=pos*2-ancestor; //int不能左乘Vector2Int(因为这里的*是Vector2Int的运算符重载)
}
}
Maze.cs
public class Maze //内部类Maze
{
private Vector2Int[] e={new Vector2Int(1,0),new Vector2Int(-1,0),new Vector2Int(0,1),new Vector2Int(0,-1),}; //四方向
private int width;
private int height; //迷宫矩阵的宽和高 默认[宽,高],Maze位置的游标在<[0..width],[0..height]>
private Vector2Int origin; //起点
public List M; //list
public List W; //list
public List maze; //最终的迷宫列表
public Maze(int width,int height,Vector2Int origin){
this.width=width;
this.height=height;
this.origin=origin;
this.M=new List();
this.W=new List();
this.maze=new List();
this.maze.Add(origin);
}
bool findMaze(Vector2Int pos){ //只判断此位置是否已移除墙
if(this.maze.Contains(pos)){
return true;
}
return false;
}
bool borderExam(Vector2Int pos){ //边界检查
if(pos.x>=0 &&pos.x=0 &&pos.y
在生成迷宫时,声明一个Maze类的实例并且调用其exec方法,就可得到一个迷宫。
此代码在Start方法内调用,可以在控制台生成一份迷宫示意图。
string a="";
Maze maze=new Maze(30,20,new Vector2Int(0,0));
maze.exec();
for(int i=0;i<30;i++){
for(int j=0;j<20;j++){
if(maze.maze.Contains(new Vector2Int(i,j))){
a=a+"-";
}
else{
a=a+"#";
}
}
a=a+"\n";
}
print(a);
-本节相关内容请读者参考:
-预制件 - Unity 手册,《预制件》
-Object-Instantiate - Unity 脚本 API,《Object.Instantiate》
预制体(英文为prefab,在unity手册中被称为预制件)可以看做是一个文件化的游戏对象,一个已经构建好了的样板。如果从编程的角度进行类比,预制体可以被看成一个用于派生类的基类,或者是一个单纯的被复制对象。预制体以实体文件(.prefab)的形式存在于unity中,并可以在有需要时动态加载进游戏场景。
预制体通常被应用于如下两个场景中:一,被经常使用的游戏对象(比如森林场景中的一棵树);二,在场景被加载时不应当存在于场景中的游戏对象(比如说闯关游戏中的通关画面)。
首先确认预制体的形状和大小。由于迷宫的场地是正方形的,我们采用以正方形为底的四棱柱作为预制体的形状,则底边长设定为scale1单位,高度为scale2单位。
创建一个Cube,调整其大小和位置直到满足上文中的条件。然后,重命名Cube为prefab,选中prefab并将其拖动到Project选项卡下的游戏资源文件夹内部,可以看到出现了一个同名文件,即为预制体。
将预制体文件拖动到Hierarchy选项卡下,可以看到预制体又出现在场景中了。需要注意的是,如果要编辑预制体文件本身,请点击预制体右边的“>”,在场景中直接编辑的不是预制体,而只是其的一个副本。预制体在场景中以蓝色显示。
将目的区域的大小调整到与球体相同的水平,也就是scale0.1大小(顺便要调整变色和音效生效的区域)。
创建一个空游戏对象MazeController,搭载用于生成迷宫的脚本MazeController.cs。
敲代码。
MazeController.cs
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Random=UnityEngine.Random;
public class Wall //内部类Wall
{
public Vector2Int pos; //位置向量
public Vector2Int relative; //相对格子向量
public Wall(Vector2Int pos,Vector2Int anc){
this.pos=pos;
ancToRelative(anc);
}
public void ancToRelative(Vector2Int ancestor){ //输入一个父格子,求出其相对格子并赋值
this.relative=pos*2-ancestor; //int不能左乘Vector2Int(因为这里的*是Vector2Int的运算符重载)
}
}
public class Maze //内部类Maze
{
private Vector2Int[] e={new Vector2Int(1,0),new Vector2Int(-1,0),new Vector2Int(0,1),new Vector2Int(0,-1),}; //四方向
private int width;
private int height; //迷宫矩阵的宽和高 默认[宽,高],Maze位置的游标在<[0..width],[0..height]>
private Vector2Int origin; //起点
public List M; //list
public List W; //list
public List maze; //最终的迷宫列表
public Maze(int width,int height,Vector2Int origin){
this.width=width;
this.height=height;
this.origin=origin;
this.M=new List();
this.W=new List();
this.maze=new List();
this.maze.Add(origin);
}
bool findMaze(Vector2Int pos){ //只判断此位置是否已移除墙
if(this.maze.Contains(pos)){
return true;
}
return false;
}
bool borderExam(Vector2Int pos){ //边界检查
if(pos.x>=0 &&pos.x=0 &&pos.y
运行游戏,如图所示: