用uinty實(shí)現(xiàn)元?dú)怛T士地圖的自動(dòng)生成

作者:Yumir
哈嘍大家好,我是yumir~
上次提到unity提供了腳本繪制Tilemap的API,今天就是報(bào)告成果的日子啦?。旖o我點(diǎn)贊)
這次Tilemap游戲物體的設(shè)置是沿用上一篇文章的,如果要做出這種立體效果的話需要到上一篇文章去看哦,這里就不重復(fù)了

1、瓦片地圖
在腳本中繪制Tilemap只需要調(diào)用SetTile()方法,用法如下:
tilemap.SetTile(坐標(biāo),瓦片);
也就是說想要實(shí)現(xiàn)自動(dòng)生成地圖只需要設(shè)置好這些坐標(biāo)點(diǎn)讓unity在對應(yīng)位置放上對應(yīng)的瓦片就可以了。
那么怎么得到這些坐標(biāo)點(diǎn)呢?我的做法是先將地圖繪制到二維數(shù)組中,用不同的數(shù)字表示不同的瓦片。
眾所周知元?dú)怛T士是roguelike游戲,而roguelike游戲的特色就是“隨機(jī)”。
為了讓系統(tǒng)自動(dòng)繪制出一張隨機(jī)的地圖(保存到二維數(shù)組中)我的制作思路是這樣的:
- 首先隨機(jī)出房間方位;
- 再在房間方位隨機(jī)房間;
最終實(shí)現(xiàn)效果如下:

2、生成房間布局
這個(gè)方法是觀(mo)摩(yu)了元?dú)怛T士的地圖之后總結(jié)出來的,稍微有丟丟不一樣但是問題不大~
首先要知道幾個(gè)要點(diǎn):
- 房間布局圖的格局是5*5的;
- 房間和房間之間一定有路;
- 起點(diǎn)在中心,終點(diǎn)在末端;
(從以上三點(diǎn)可以想到用一個(gè)樹結(jié)構(gòu)可以解決需求,元?dú)怛T士看起來是用的圖結(jié)構(gòu))
我在excel里面劃拉了一個(gè)5*5的表格來表示我們的地圖,中間的小房子就是我們每次進(jìn)入新地圖的起點(diǎn)。

接下來我們要開房間咯~在起點(diǎn)周圍有四個(gè)可選的房間可以開(紅色),隨機(jī)選中其中一個(gè)作為第一個(gè)房間(綠色)。

可以看到新的房間周圍又有三個(gè)可選,在三個(gè)可選中又隨機(jī)選中1~3個(gè)房間(淺綠色)。

在選出的房間里面隨機(jī)選擇一個(gè)房間,重復(fù)上一步操作,重復(fù)兩次(你想重復(fù)幾次都可以啦反正我重復(fù)兩次)在選出的房間里面隨機(jī)選擇一個(gè)房間作為終點(diǎn)(藍(lán)色), 房間布局就隨機(jī)出來啦~

實(shí)現(xiàn)代碼如下:
? ?/// 統(tǒng)計(jì)可選房間 ? ?public void AddRange(int roomVector)
? ?{
? ? ? ?int[] vec = new int[4];
? ? ? ?vec[0] = roomVector / 5 == 0 ? -1 : roomVector - 5;
? ? ? ?vec[1] = roomVector / 5 == 4 ? -1 : roomVector + 5;
? ? ? ?vec[2] = roomVector % 5 == 0 ? -1 : roomVector - 1;
? ? ? ?vec[3] = roomVector % 5 == 4 ? -1 : roomVector + 1;
? ? ? ?range.Clear();
? ? ? ?for (int i = 0; i < vec.Length; i++)
? ? ? {
? ? ? ? ? ?if (vec[i] != -1 && map[vec[i] / 5, vec[i] % 5] == 0)
? ? ? ? ? ?{
? ? ? ? ? ? ? ?range.Add(vec[i]);
? ? ? ? ? ?}
? ? ? ?}
? ? }
? ?/// 生成新的房間方位 ? ?///房間數(shù)量 ? ?///房間類型 ? ?public void AddRoom(int roomCount, RoomType roomType)
? ?{
? ? ? ?++num;
? ? ? ?while (roomCount > 0)
? ? ? ?{
? ? ? ? ? ?int b = Random.Range(0, range.Count);
? ? ? ? ? ?//加為子節(jié)點(diǎn),修改map值為1 ? ? ? ? ? ?map[range[b] / 5, range[b] % 5] = num;
? ? ? ? ? ?ranRoom.AddNextRoom(new Room(range[b], roomType));
? ? ? ? ? ?range.Remove(range[b]);
? ? ? ? ? ?--roomCount;
? ? ? ? }
? ? ? ?ranRoom = ranRoom.nextRooms[Random.Range(0, ranRoom.nextRooms.Count)];
? ?}
3、生成完整地圖
上文的房間布局是用一個(gè)5*5的二位數(shù)組存儲(chǔ)起來的,而完整的地圖則需要一個(gè)足夠大的二維數(shù)組,這個(gè)足夠大是多大呢,就是你最大的房間有多大,再預(yù)留一點(diǎn)空間作為走廊的大小,再乘以五就是大地圖的邊長了。
雖然是個(gè)隨機(jī)房間的游戲,但是房間的格局可不是可以隨便長的,布局信息可以通過excel設(shè)計(jì)之后轉(zhuǎn)為二維數(shù)組數(shù)據(jù),我比較懶就直接在腳本里面碼出來了(代碼在文末網(wǎng)盤),這樣一來就可以隨機(jī)房間的樣式了。
道理都懂了,怎么動(dòng)筆呢?這里就要用到之前提到的樹結(jié)構(gòu)了,我聲明了一個(gè)類,在生成房間布局的時(shí)候?qū)γ總€(gè)房間新建一個(gè)對應(yīng)的對象,這個(gè)對象又記住了自己的子房間,形成一顆樹,比如上面的房間布局生成推演可以生成出一顆這樣的樹。

這樣的好處一方面是在繪制房間之間的通道的時(shí)候直接從父節(jié)點(diǎn)向子節(jié)點(diǎn)計(jì)算路徑(其實(shí)就是畫直線)就可以了,一方面在繪制房間的時(shí)候不需要遍歷整個(gè)房間格局二維數(shù)組,直接從“起點(diǎn)”往下找每個(gè)節(jié)點(diǎn)就可以了。
? ?/// 在大地圖中繪制房間 ? ?///起點(diǎn) ? ?private void AddRoomForMap(Room room)
? {
? ? ? ?SetRoomInArray(room);
? ? ? ?if (room.nextRooms.Count != 0)
? ? ? ?{
? ? ? ? ? ?foreach (Room item in room.nextRooms)
? ? ? ? ? ?{
? ? ? ? ? ? ? ?AddRoomForMap(item);
? ? ? ? ? ?}
? ? ? ?}
? ?}
? ?/// 在大地圖中繪制路徑 ? ?///起點(diǎn) ? ?private void AddPathForMap(Room room)
? ?{
? ? ? ?foreach (Room item in room.nextRooms)
? ? ? {
? ? ? ? ? ?SetPathInArray(room, item);
? ? ? }
? ? ? ?foreach (Room item in room.nextRooms)
? ? ? ?{
? ? ? ? ? ?if (item.nextRooms.Count != 0)
? ? ? ? ? ?{
? ? ? ? ? ? ? ?AddPathForMap(item);
? ? ? ? ? ?}
? ? ? }
? ?}
起點(diǎn)和終點(diǎn)的房間樣式是固定的,其他房間則是隨機(jī)一個(gè)“其他房間樣式”,通過簡單的數(shù)學(xué)計(jì)算得到房間在完整地圖上的坐標(biāo)之后將房間樣式二維數(shù)組賦值到完整地圖的對應(yīng)位置,再在房間之間畫上路徑,一個(gè)完整地圖二維數(shù)組就生成好了。
最后遍歷完整地圖二維數(shù)組,根據(jù)數(shù)組中的數(shù)據(jù)在Tilemap上繪制瓦片就可以生成最終效果了,再換幾套貼圖~每次都有新感覺~
鏈接:https://pan.baidu.com/s/1dSxD-h7dmm0jSxH0G4jVjw
提取碼:ow9w

~~~分割線~~~
歡迎加入游戲開發(fā)群歡樂攪基:610475807
對游戲開發(fā)感興趣的童鞋可戳這里進(jìn)一步了解:http://www.levelpp.com/