unity的迷宮生成演算法
阿新 • • 發佈:2019-01-22
思路:
定義一個二維陣列,二維陣列中奇數行列視為圍牆,偶數為路徑。 從起始點開始,隨機從上下左右四個位置尋找周圍沒有被找到過的位置,找到後此點標記為1,並把此點與前一點之間的位置設定為1。 如果全部位置已經找到過,則退回到上一個點重複次邏輯,直到所有點都記錄到或 退回到起始點且沒有可用點。
文章最後我會附上完整程式碼。
下面我們來實現一下
新建一個迷宮生成類:MazeCreate
定一個二維列表,用來儲存我們的資料,因為迷宮的尺寸需要是可配置的,所有我們用list來進行儲存:
為了區分每個點代表的不同內容,我們定義一個列舉:public List<List<int>> mapList = new List<List<int>>();
public enum PointType{
wall = 0,//牆
way = 1,//路
startpoint = 2,//起始點
endpoint = 3,//結束點
nullpoint = 4,//空位置,不進行任何操作
}
在建構函式中對迷宮的尺寸進行設定:
private MazeCeator(int row, int col){
this.row = row;
this.col = col;
}
然後定義Start方法開始迷宮資料的生成初始化陣列,按照行數和列數對資料進行初始化:
然後隨機找一個點作為起始點,因為我們前面說過,奇數行為牆,偶數行為路線,所以要對隨機的結果進行判斷,避免起始點在牆上,像這樣:for (int i = 0; i < row; i++) { mapList.Add(new List<int>()); for (int j = 0; j < col; j++) { mapList[i].Add((int)PointType.wall); } }
int _row = Random.Range(1, row - 1);
int _col = Random.Range(1, col - 1);
if (_row % 2 == 0) { _row += 1; }
if (_col % 2 == 0) { _col += 1; }
然後給起始點的資料賦值:mapList[_row][_col] = (int)PointType.startpoint;
開始生成迷宮首先實現一下尋找周圍可用點的方法:
void FindNearPoint(List<int> nearpoint,int index){
nearpoint.Clear();
int _row = index / col;
int _col = index % col;
//up
if (_row >= 2)
{
AddNearPoint(nearpoint, (_row - 2) * col + _col);
}
//down
if (_row < row - 2)
{
AddNearPoint(nearpoint, (_row + 2) * col + _col);
}
//left
if (_col >= 2)
{
AddNearPoint(nearpoint, _row * col + _col - 2);
}
//up
if (_col < col - 2)
{
AddNearPoint(nearpoint, _row * col + _col + 2);
}
}
nearpoint引數用來記錄所有的可用點,index為要尋找的點,這裡為了傳遞引數更加方便,把二維索引轉換成一維來進行傳遞。AddNearPoint方法用來判斷這個點是否滿足可一個被找到的條件:
void AddNearPoint(List<int> nearpoint, int p)
{
int _row = p / col;
int _col = p % col;
if (p >= 0 && p < maxcount && mapList[_row][_col] == (int)PointType.wall)
{
nearpoint.Add(p);
}
}
接下來進行生成邏輯,定義一個方法void FindPoint(int nowindex);nowindex為當前的點。
然後在Start呼叫FindPoint並傳入起始點:
int nowindex = _row * col + _col;
FindPoint(nowindex);
FindPoint裡面我們使用遞迴的方式來找到所有的點。
首先判斷是否找到所有的點:
if(findList.Count >= maxcount){
return;
}
其中maxcount=row * col,為了方便使用,把它定義成全域性變數並在Start中賦值。
然後我們定義一個列表用來儲存附近可用的點,即值為0的點並開始尋找:
List<int> nearpoint = new List<int>();
FindNearPoint(nearpoint,nowindex);
如果找到了附近可用的點,這遍歷這些點並對其進行處理: while (nearpoint.Count > 0)
{
int rand = Random.Range(0, nearpoint.Count);
//中間的格子
int midindex = nowindex + (nearpoint[rand] - nowindex) / 2;
SetPoint(midindex);
//新的格子
int newindex = nearpoint[rand];
SetPoint(newindex);
nearpoint.RemoveAt(rand);
//遞迴
FindPoint(newindex);
FindNearPoint(nearpoint, nowindex);
}
還需要一個設定目標點的方法:
void SetPoint(int index)
{
int _row = index / col;
int _col = index % col;
mapList[_row][_col] = (int)PointType.way;
findList.Add(index);
}
這裡先是在所有可用點中隨機找出一個即rand,然後把當前點與rand之間的點打通,即設定為1,把目標點從nearpoint中移除,
然後遞迴呼叫FindPoint,並傳入新的目標點。
這裡因為在每一次迴圈後,之前找到的nearpoint中的點的狀態有可能會改變,所以最後需要重新尋找一次附近可用的點。這樣我們的迷宮就可以正常生成了,我們來測試一下。
建立一個cube並做成預製體,放在Resources/Prefabs中取名為maze。
建立一個測試指令碼MazeTest:
定義地圖的寬高已經迷宮生成類:
const int row = 41, col = 41;
MazeCreate mazeCreate;
Start方法中初始化迷宮類
mazeCreate = MazeCreate.GetMaze(row, col);
然後遍歷迷宮資料並生成物體,這裡我們判斷是在路徑上生成物體:
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (mazeCreate.mapList[i][j] == (int)MazeCreate.PointType.startpoint ||
mazeCreate.mapList[i][j] == (int)MazeCreate.PointType.way)
{
GameObject column = (GameObject)Resources.Load("Prefabs/maze");
column = MonoBehaviour.Instantiate(column);
column.transform.position = new Vector3(i, 0, j);
}
}
}
把MazeTest掛到攝像機上並執行場景,我們可以看到:
這樣我們的迷宮就生成成功了。
為了看到迷宮的生成過程,我們使用findList,這裡記錄了路徑點的先後順序,我們來試一下:
還是上邊的測試類,我們把for迴圈的部分註釋調,然後在Update中加入如下程式碼
float addTime = 0;
int addindex = 0;
// Update is called once per frame
void Update () {
if (addindex >= mazeCreate.findList.Count)
{
return;
}
addTime += Time.deltaTime;
if (addTime > 0.05)
{
addTime = 0;
int index = mazeCreate.findList[addindex];
int _row = index / col;
int _col = index % col;
GameObject column = (GameObject)Resources.Load("Prefabs/maze");
column = MonoBehaviour.Instantiate(column);
column.transform.position = new Vector3(_row, 0, _col);
addindex++;
}
}
效果:
最後附上MazeCreate的完整程式碼:
/// <summary>
/// 迷宮生成類
/// 迷宮生成思路:
/// 規則:二維陣列中奇數行列視為圍牆,偶數為路徑
/// 從起始點開始,隨機從上下左右四個位置尋找周圍沒有被找到過的位置,找到後此點標記為1,並把此點與前一點之間的位置設定為1
/// 如果全部位置已經找到過,則退回到上一個點重複次邏輯,直到所有點都記錄到或 退回到起始點且沒有可用點
/// </summary>
public class MazeCreate : MonoBehaviour
{
public enum PointType{
wall = 0,//牆
way = 1,//路
startpoint = 2,//起始點
endpoint = 3,//結束點
nullpoint = 4,//空位置,不進行任何操作
}
//行數
public int row{ get; private set; }
//列數
public int col{ get; private set; }
//全部點數量
int maxcount;
private MazeCreate(int row, int col){
this.row = row;
this.col = col;
Start();
}
public static MazeCreate GetMaze(int row, int col)
{
MazeCreate maze = new MazeCreate(row,col);
return maze;
}
/// <summary>
/// 迷宮生成過程中,記錄已經找到的點
/// </summary>
public List<int> findList = new List<int>();
//迷宮資料
public List<List<int>> mapList = new List<List<int>>();
void Start(){
maxcount = row * col;
//初始化陣列
for (int i = 0; i < row; i++)
{
mapList.Add(new List<int>());
for (int j = 0; j < col; j++)
{
mapList[i].Add((int)PointType.wall);
}
}
//for (int i = 0; i < 10; i++)
//{
// for (int j = 0; j < 10; j ++){
// mapList[row / 2 - 2 + i][col / 2 - 2 + j] = (int)PointType.nullpoint;
// }
//}
//起始點
int _row = Random.Range(1, row - 1);
int _col = Random.Range(1, col - 1);
if (_row % 2 == 0) { _row += 1; }
if (_col % 2 == 0) { _col += 1; }
mapList[_row][_col] = (int)PointType.startpoint;
int nowindex = _row * col + _col;
findList.Add(nowindex);
//遞迴生成路徑
FindPoint(nowindex);
}
void FindPoint(int nowindex){
if(findList.Count >= maxcount){
return;
}
List<int> nearpoint = new List<int>();
FindNearPoint(nearpoint,nowindex);
while (nearpoint.Count > 0)
{
int rand = Random.Range(0, nearpoint.Count);
//中間的格子
int midindex = nowindex + (nearpoint[rand] - nowindex) / 2;
SetPoint(midindex);
//新的格子
int newindex = nearpoint[rand];
SetPoint(newindex);
nearpoint.RemoveAt(rand);
//遞迴
FindPoint(newindex);
FindNearPoint(nearpoint, nowindex);
}
}
//尋找附近可用的點
void FindNearPoint(List<int> nearpoint,int index){
nearpoint.Clear();
int _row = index / col;
int _col = index % col;
//up
if (_row >= 2)
{
AddNearPoint(nearpoint, (_row - 2) * col + _col);
}
//down
if (_row < row - 2)
{
AddNearPoint(nearpoint, (_row + 2) * col + _col);
}
//left
if (_col >= 2)
{
AddNearPoint(nearpoint, _row * col + _col - 2);
}
//up
if (_col < col - 2)
{
AddNearPoint(nearpoint, _row * col + _col + 2);
}
}
//設定路徑
void SetPoint(int index)
{
int _row = index / col;
int _col = index % col;
mapList[_row][_col] = (int)PointType.way;
findList.Add(index);
}
//附近的點是否滿足尋找條件
void AddNearPoint(List<int> nearpoint, int p)
{
int _row = p / col;
int _col = p % col;
if (p >= 0 && p < maxcount && mapList[_row][_col] == (int)PointType.wall)
{
nearpoint.Add(p);
}
}
}