本文章首發於我的個人部落格,希望大家多多支援!hi! this is showhoop studio!
你即將看到的是我的unity隨機地牢遊戲的第一篇開發日記。這個遊戲的靈感**於小白的大冒險,我借鑑了它的主要玩法——也就是隨機地牢,來作為練習,提高自己的遊戲開發技術,同時也豐富一下自己的作品集。在這一篇日記中,我將詳細為大家介紹一下我所採用的隨機生成演算法,供大家學習。
廢話不多說,立馬進入正題。
在開始之前,我們先來看看效果(淺色為地面,深色為圍牆障礙)。
正如你們所看到的一樣,這個過程實際上非常的簡單:通過若干個方塊在平面內隨機移動來生成地面,之後在其周圍生成圍牆,最後去掉中間那些意義不大的單獨的牆。那麼現在我們對這個過程有了乙個基本的了解,然後我們便可以著手開始寫**了。
我們下面討論的指令碼**不會有上面中那樣的過程演示效果,是直接生成的!我們首先定義一些資料結構。顯然對於上述過程我們應該採用乙個二維陣列來標記乙個空間內的每乙個小格,而每乙個小格會有三種型別:地面,牆壁和空型別(注意,之前我們沒有提到空型別,但實際上它是存在的,地面和牆壁沒有充滿整個空間,剩餘的地方就是空的)。
// 包含空,地面和牆壁的列舉型別
private
enum grid
;// 整個可生成的正方形空間
private grid[
,] grid;
// 正方形空間的大小
// 你也可以使用public型別以便於除錯
private
int width =30;
private
int height =
30;
到這裡我們已經邁出了第一步,接下來我們將開始定義「移動」的方塊的資料結構,我們先姑且稱之為indicator。
private
struct indicator
// 乙個indicator型別的鍊錶,它將包含若干個indicators
private list inds;
// indicator轉變方向的概率
private
float chancetoturn =
0.5f
;// 生成乙個新的indicator的概率
private
float chancetospawn =
0.05f
;// 銷毀乙個indicator的概率
private
float chancetodestroy =
0.05f
;// 同時存在的indicators的最大數目
private
int maxamount =
10;
最後,我們需要指定地牢的填充比率。
填充比率指的是地面數量與整個網格空間格仔數量的比率。這裡我們不想讓地牢充滿整個空間,只讓它充滿一部分,以此增加其隨機性。
private
float fillratio =
0.4f
;
到這裡,核心演算法所需的所有變數都建立完畢了,接下來我們將具體實現核心演算法。
首先,我們需要乙個初始化函式來配置好環境。
void
init()
// 初始化indicator
inds =
newlist
<
indicator
>()
;indicator ind =
newindicator()
;// 隨機選擇初始的方向
// randomdirection()會隨機生成乙個vector2的變數,稍後會提供
ind.dir =
randomdirection()
;// 初始化生成位置
ind.pos =
newvector2
(mathf.
roundtoint
(width /
2.0f
), mathf.
roundtoint
(height /
2.0f))
; inds.
add(ind)
;}
在初始化完成之後,我們就可以開始生成地面了。
void
floorgeneration()
// 銷毀indicator
int numbers = inds.count;
for(
int i =
0; i < numbers; i++)}
// indicator變換方向
for(
int i =
0; i < inds.count; i++)}
// 生成新的indicator
int num = inds.count;
for(
int i =
0; i < num; i++)}
// 移動indicator
foreach
(indicator ind in inds)
// 避免indicator移動到邊緣,因為至少需要為牆壁預留一格的空間
foreach
(indicator ind in inds)
// 檢查迴圈結束條件,即覆蓋率if(
(float
)numberoffloors()
/(float
)grid.length > fillratio)
}}
關於clamp
的詳細介紹可以參考官方文件
生成牆壁的過程比生成地面要來的簡單,我們遍歷每乙個網格,如果某個網格是floor,那麼我們就檢查這個網格的四周,如果它周圍存在empty
的格仔,那麼我們就把這個empty
的格仔賦值為wall
。
void
wallgeneration()}}}
在上面兩個過程完成後,不可避免的會生成一些單個的(即四面都是地面)牆壁,這些牆壁並沒有很大的存在價值,所以我們現在需要寫乙個函式移除它們(當然你也可以選擇保留)。移除的方法也很簡單,檢查每乙個網格,如果乙個wall
的四周都是floor
,那我們就將他賦值為floor
。
void
removesingle()
}if(all)}}
}}
還記得我們在生成地面時遺留的randomdirection()
嗎?現在我們來將它搞定!
vector2
randomdirection()
}
現在我們已經可以生成地圖了,但是我們什麼也看不到,因為我們生成的地圖還在乙個矩陣裡,我們需要將其視覺化(不然有什麼意義呢)。這裡我採取的方法是,定義兩個public
的gameobject
型別的變數,在指令碼的inspector視窗內繫結預製體,然後利用函式將矩陣座標轉換的3d世界座標(這裡我將y軸始終設為0,便於座標轉換)並生成遊戲對像例項。這裡我簡單製作了幾個不同的地面和牆壁模型,因為我希望地圖看上不那麼單一,在這種情況下,我們還需要在生成預製體時隨機選取其中乙個。下面貼上**。
public gameobject[
] floors;
public gameobject[
] walls;
void
spawn
(float x,
float y,
gameobjct go)
// 生成地圖
void
spawnmap()
}}}// 隨機選取乙個朝向
quaternion
objorientation()
}
隨機選取地面和牆壁型別也是用上述方法,再將返回值傳遞給spawn(float, float, gameobject)
,這裡我就不贅述了。
最後我們只需要在start()
函式裡面依次呼叫這些函式就可以了。
void
start()
一路寫下來辛苦了!接下來讓我們看看成果吧!
MAS開發日記 1
我一直認為,乙個良好的軟體開發企業,必須有以下品質 1。軟體質量穩定 2。開發效率高 3。可維護性,以及持續性維護性要高 4。產品或服務針對某個細分市場,具有獨特的價值。根據我們小型軟體團隊的實際情況,我們總結了一套軟體開發的標準管理規範和操作指引,並自主研發了乙個軟體開發的平台,通過這些工作,我們...
Wcf 開發日記 1
序 這篇日記是記錄我在開發 wcf中所遇到的問題,以及解決的方法。這個專案並不是乙個從零開發的專案,某種程度上可以被看作是從 2層架構到 3層的遷移。其中遇到了很多問題,包括技術的和非技術的。這些都將會被我一一記錄。這些經驗和技術實踐可以被用作 soa專案的參考。專案的總覽 wcf和 wpf都是 m...
專案開發日記(1)
今天是2022 04 15,系統已經開發到v1.1了。我亂命名的,我也不懂怎麼給版本命名。就這樣把,v1.0也就是前兩天的事,當時把整個系統主要一點的功能做出來了,做出來就算成功!v1.1就是接著v1.0繼續把功能實現下去。亂命名的,搞的後面都不知道該是v1.2還是v1.1.1了。目前是大四馬上畢業...