天天看點

unity随機方向的代碼_unity3d随機地牢生成代碼

做完了以後我又想了一下,發現其實根本不需要這麼麻煩,果然demo裡的代碼對我的思路影響還是有點大。demo裡的c++代碼為了展示地牢的牆壁,在二維數組中加上了wall這個東西表示牆壁。事實上用unity來做的話,隻需要考慮地闆的位置,然後根據鄰接的地闆有沒有東西來判斷是否生成牆壁即可。

首先用一個枚舉類型代表地牢迷宮中的各個元素:

public enumTile

{

Default,

DirtFloor,//房間地闆

Wall_w,//上方的牆

Wall_s,//下方的牆

Wall_a,//左方的牆

Wall_d,//右方的牆

Corridor_ad,//橫向走廊

Corridor_ws,//縱向走廊

Door,//房門

UpStairs,//入口

DownStairs//出口

}

然後考慮使用二維數組來儲存地牢元素的資訊。既然是用unity來做,先不考慮随機地牢的邏輯數組要怎麼生成,先把二維數組轉化為實體的方法寫出來:

建立一個test腳本,用于測試生成用的creat_dungeon方法是否好用,并在裡面定義一個測試用的二維數組:

usingUnityEngine;usingSystem.Collections;usingSystem;public classtest : MonoBehaviour {voidStart () {

Action _Instantiate=(t,v)=>{Instantiate(t,v,t.rotation);};//這裡考慮不當,理論上不應該在test腳本中寫Instantiate方法,而是應該在creat_dungeon中用。不過既然寫出來了也能顯示我懂一些委托的知識...将錯就錯好了

Tile[,] _map=new Tile[4,4];

_map [0, 1] =Tile.Wall_s;

_map [0, 2] =Tile.Wall_s;

_map [1, 1] =Tile.DirtFloor;

_map [1, 2] =Tile.DirtFloor;

_map [2, 1] =Tile.DirtFloor;

_map [2, 2] =Tile.DirtFloor;

_map [3, 1] =Tile.Corridor_ws;

_map [3, 2] =Tile.Wall_w;

_map [1, 3] =Tile.Corridor_ad;

_map [2, 3] =Tile.Wall_d;

_map [1, 0] =Tile.Corridor_ad;

_map [2, 0] = Tile.Wall_a;//自定義一個地圖

creat_dungeon.Instance.creat (_map,_Instantiate);//調用生成函數

}

接下去就是考慮怎麼寫creat_dungeon這個類了,首先這個類隻用于生成實體,是以不應該存在多份,是以設計成單例:

public classcreat_dungeon{private staticcreat_dungeon _instance;privatecreat_dungeon(){}public staticcreat_dungeon Instance{get{if (_instance == null)

{

_instance= newcreat_dungeon ();

}return_instance;

}

}

}

然後這個類中肯定需要動态加載預設的資源,這裡用resources.load方法加載,在初始化函數中加上加載資源的代碼:

privateTransform floor,pillar,wall_ws,wall_ad;privatecreat_dungeon(){

floor= Resources.Load ("Floor_1_Prefab",typeof (Transform)) asTransform;

pillar= Resources.Load ("Pillar_1_Prefab",typeof (Transform))asTransform;

wall_ws= Resources.Load ("Wall_w", typeof(Transform))asTransform;

wall_ad= Resources.Load ("Wall_a", typeof(Transform))asTransform;

}

然後根據資源模型的大小,定義一個常量:

private float floor_length=1.518111f;

最後就是其中生成用的代碼:

///根據二維數組執行個體化地牢

public voidcreat(Tile[,] map,Action Instantiate){for (int i = 0; i < map.GetLength (0); i++)for (int j = 0; j < map.GetLength (1); j++) {switch(map [i, j]) {case (Tile.DirtFloor)://地闆的場合

Instantiate (floor,new Vector3(i*floor_length,0,j*floor_length));break;case (Tile.Wall_s)://牆壁的場合

Instantiate (wall_ws,new Vector3((i+0.5f)*floor_length,0,j*floor_length));

Instantiate (pillar,new Vector3((i+0.5f)*floor_length,0,(j+0.5f)*floor_length));//在牆壁對應的位置生成柱子,由于最終生成的是一個封閉的空間,是以牆壁和柱子的數目必定是相等的。

break;case(Tile.Wall_w):

Instantiate (wall_ws,new Vector3((i-0.5f)*floor_length,0,j*floor_length));

Instantiate (pillar,new Vector3((i-0.5f)*floor_length,0,(j-0.5f)*floor_length));break;case(Tile.Wall_a):

Instantiate (wall_ad,new Vector3((i)*floor_length,0,(j+0.5f)*floor_length));

Instantiate (pillar,new Vector3((i-0.5f)*floor_length,0,(j+0.5f)*floor_length));break;case(Tile.Wall_d):

Instantiate (wall_ad,new Vector3((i)*floor_length,0,(j-0.5f)*floor_length));

Instantiate (pillar,new Vector3((i+0.5f)*floor_length,0,(j-0.5f)*floor_length));break;case (Tile.Corridor_ad)://走廊的話,則需要額外生成兩面牆和兩根柱子,不妨想想為什麼要這麼做

Instantiate (floor,new Vector3(i*floor_length,0,j*floor_length));

Instantiate (wall_ws,new Vector3((i+0.5f)*floor_length,0,j*floor_length));

Instantiate (wall_ws,new Vector3((i-0.5f)*floor_length,0,j*floor_length));

Instantiate (pillar,new Vector3((i-0.5f)*floor_length,0,(j+0.5f)*floor_length));

Instantiate (pillar,new Vector3((i+0.5f)*floor_length,0,(j-0.5f)*floor_length));break;case(Tile.Corridor_ws):

Instantiate (floor,new Vector3(i*floor_length,0,j*floor_length));

Instantiate (wall_ad,new Vector3(i*floor_length,0,(j+0.5f)*floor_length));

Instantiate (wall_ad,new Vector3(i*floor_length,0,(j-0.5f)*floor_length));

Instantiate (pillar,new Vector3((i+0.5f)*floor_length,0,(j+0.5f)*floor_length));

Instantiate (pillar,new Vector3((i-0.5f)*floor_length,0,(j-0.5f)*floor_length));break;default:break;

}

}

}

最後運作效果如下:

unity随機方向的代碼_unity3d随機地牢生成代碼

可以看到三個走廊沒有封上口,因為這裡預定走廊兩端必須要連接配接房間,不能出現死胡同的情況,這裡可以看到左側和右側走廊的柱子是可以對上的。

好,這樣的話轉化為實體的方法就寫好了,現在考慮怎麼生成邏輯地形。

思路是這樣的:第一次生成房間時,直接在地圖最中間生成,之後每次生成一個房間和一個走廊,把走廊和之前造出來的東西連起來。考慮到不想讓走廊拐彎,是以每次先生成走廊再生成房間。

最終map類的代碼如下:

usingUnityEngine;usingSystem.Collections;usingSystem.Collections.Generic;public classmap

{private Tile[,] full_map;//地圖

private int room_max_length, room_max_width, room_min_length, room_min_width, map_max_length, map_max_width, room_num,min_corridor_len,max_corridor_len;//房間的最大最小寬度,地圖的最大長寬,房間的個數

private bool_first;private staticmap _instance;privatemap ()

{

}///地圖的單例

public staticmap Instance {get{if (null ==_instance) {

_instance= newmap ();

}return_instance;

}

}///初始化函數,全部賦予default

public void Init (int room_max_length,int room_max_width,int room_min_length,int room_min_width,int map_max_length,int map_max_width,int room_num,int min_corridor_len,intmax_corridor_len)

{

full_map= newTile[map_max_width, map_max_length];

_first= true;this.room_max_length =room_max_length;this.room_max_width =room_max_width;this.room_min_length =room_min_length;this.room_min_width =room_min_width;this.map_max_length =map_max_length;this.map_max_width =map_max_width;this.room_num =room_num;this.min_corridor_len =min_corridor_len;this.max_corridor_len =max_corridor_len;

}///判斷某一塊是否在地圖區域中

private bool IsInBounds_x (intx)

{if ((x < 0) || (x > map_max_width - 1))return false;else

return true;

}///判斷某一塊是否在地圖區域中

private bool IsInBounds_y (inty)

{if ((y < 0) || (y > map_max_length - 1))return false;else

return true;

}///将一塊區域設定為指定類型塊

private void SetCells (int xStart, int yStart, int xEnd, intyEnd, Tile cellType)

{for (int i = xStart; i <= xEnd; i++)for (int j = yStart; j <= yEnd; j++) {

full_map [i, j]=cellType;

}

}///判斷一個區域是否被使用過

private bool IsAreaUnused (int xStart, int yStart, int xEnd, intyEnd)

{for (int i = xStart; i <= xEnd; i++)for (int j = yStart; j <= yEnd; j++)if (full_map [i, j] !=Tile.Default)return false;return true;

}///建立單個房間

private void Creat_room(int xStart, int yStart, int xEnd, intyEnd){for (int i = xStart + 1; i < xEnd ; i++)for (int j = yStart + 1; j < yEnd; j++)

full_map [i, j]=Tile.DirtFloor;for (int i = xStart + 1; i < xEnd ; i++) {

full_map [i, yStart]=Tile.Wall_a;

full_map [i, yEnd]=Tile.Wall_d;

}for (int j = yStart + 1; j < yEnd; j++) {

full_map [xStart, j]=Tile.Wall_s;

full_map [xEnd, j]=Tile.Wall_w;

}

}///建立單個走廊

private void Creat_Corridor(int xStart, int yStart, int xEnd, intyEnd,Tile t){for (int i = xStart; i <= xEnd ; i++)for (int j = yStart; j <= yEnd; j++)

full_map [i, j]=t;

}private Tile[] tiles = System.Enum.GetValues (typeof(Tile)) asTile[];///建立走廊與房間

private bool MakeRoomAndCorridor(int x,inty){int xStart = -1, xEnd = -1, yStart = -1, yEnd = -1, width, length;

width=Random.Range (room_min_width, room_max_width );

length= Random.Range (room_min_length, room_min_length );//随機長寬

if(_first) {

xStart= map_max_width / 2 - width / 2;

yStart= map_max_length / 2 - length / 2;

xEnd= xStart +width;

yEnd= yStart +length;if (IsInBounds_x (xStart) && IsInBounds_x (xEnd) && IsInBounds_y (yStart) &&(IsInBounds_y (yEnd))) {if(IsAreaUnused (xStart, yStart, xEnd, yEnd)) {

_first= false;//如果是第一次建立房間的話,就在地圖中間生成一個

Creat_room (xStart, yStart, xEnd, yEnd);return true;

}else

return false;

}

}else{if ((full_map [x, y] == Tile.Wall_a) || (full_map [x, y] == Tile.Wall_w) || (full_map [x, y] == Tile.Wall_s) || (full_map [x, y] ==Tile.Wall_d)) {//生成走廊與房間

int corridor_length = Random.Range (min_corridor_len - 2, max_corridor_len - 1);int c_xStart = -1, c_xEnd = -1, c_yStart = -1, c_yEnd = -1;int away = Random.Range (1, length - 1);//偏移量

Tile type=Tile.Default;//根據打穿的牆壁類型決定房間和走廊的位置

switch(full_map [x, y]) {case(Tile.Wall_a):

xStart= x -away;

xEnd= x +width;

yEnd= y - corridor_length - 1;

yStart= yEnd -length;

c_yEnd=y;

c_yStart= y - corridor_length - 1;

c_xEnd=x;

c_xStart=x;

type=Tile.Corridor_ad;break;case(Tile.Wall_d):

xStart= x -away;

xEnd= x +width;

yStart= y + corridor_length + 1;

yEnd= yStart +length;

c_yStart=y;

c_yEnd= y + corridor_length + 1;

c_xEnd=x;

c_xStart=x;

type=Tile.Corridor_ad;break;case(Tile.Wall_w):

yStart= y -away;

yEnd= yStart +length;

xStart= x + corridor_length + 1;

xEnd= xStart +width;

c_xStart=x;

c_xEnd= x + corridor_length + 1;

c_yStart=y;

c_yEnd=y;

type=Tile.Corridor_ws;break;case(Tile.Wall_s):

yStart= y -away;

yEnd= yStart +length;

xEnd= x - corridor_length - 1;

xStart= xEnd -width;

c_xEnd=x;

c_xStart= x - corridor_length - 1;

c_yStart=y;

c_yEnd=y;

type=Tile.Corridor_ws;break;default:break;

}if(IsAreaUnused (xStart, yStart, xEnd, yEnd)) {

Creat_room (xStart, yStart, xEnd, yEnd);

Creat_Corridor (c_xStart, c_yStart, c_xEnd, c_yEnd,type);//判斷是否在地圖内,然後生成

return true;

}else

return false;

}else

return false;

}return true;

}public void make_dungeon(intstep){intx, y;int num=0;for (int i = 0; i < step; i++) {

x= Random.Range (0,map_max_width);

y= Random.Range (0,map_max_length);if(MakeRoomAndCorridor(x,y)){

num++;

}if (num==room_num){break;

}

}if(num

Debug.Log ("無法生成指定個數的房間!請确認資料的合法性或加大步數");

}

}publicTile[,] getmap(){return(full_map);

}

}

然後是用于控制生成的類,綁在錄影機上就能運作:usingUnityEngine;usingSystem.Collections;usingSystem;public classgameDriver : MonoBehaviour {privateTransform dungeon;public int random_seed=1;public int room_max_length=5,room_max_width=5,room_min_length=3,room_min_width=3,map_max_length=100,map_max_width=100,room_num=5,min_corridor_len=1,max_corridor_len=3,step=10000;//房間的最大最小寬度,地圖的最大長寬,房間的個數//Use this for initialization

voidStart () {

dungeon= GameObject.Find ("dungeon").transform;//在地圖上事先建立了一個放實體的空物體

Action _Instantiate=(t,v)=>{object g=Instantiate(t,v,t.rotation);((Transform)g).SetParent(dungeon);};

UnityEngine.Random.InitState (random_seed);//初始化随機種子

map.Instance.Init (room_max_length, room_max_width, room_min_length, room_min_width, map_max_length, map_max_width, room_num,min_corridor_len,max_corridor_len);//初始化參數

map.Instance.make_dungeon(step);//生成地牢

creat_dungeon.Instance.creat (map.Instance.getmap (), _Instantiate);//實體化地牢

}//Update is called once per frame

voidUpdate () {

}

}

最終的效果預覽:

unity随機方向的代碼_unity3d随機地牢生成代碼

當然這個地牢還有很多東西沒加上去,比如我在枚舉類中定義的門(可以加在走廊的兩端),出入口。還有裝飾用的燈啊,小物品等,暫時就先這樣吧,也沒想到用這個後續搞點事情。

補發工程源碼:

連結:https://pan.baidu.com/s/1jIuh36m 密碼:hx9b