找回密碼
 注冊帳號

掃一掃,訪問微社區

士郎 用Unity蓋房子(二)——《勇者斗惡龍:建造者2》地形生成和人物控制

15
回復
1131
查看
打印 上一主題 下一主題
[ 復制鏈接 ]
排名
1
昨日變化

8045

主題

8603

帖子

3萬

積分

Rank: 16

UID
1231
好友
186
蠻牛幣
12139
威望
30
注冊時間
2013-7-29
在線時間
4113 小時
最后登錄
2019-8-16

活力之星原創精華達人突出貢獻獎財富之證游戲蠻牛QQ群會員蠻牛妹VIP

馬上注冊,結交更多好友,享用更多功能,讓你輕松玩轉社區。

您需要 登錄 才可以下載或查看,沒有帳號?注冊帳號

x
前言
接著上篇繼續:(點擊此處https://()
上篇用一個相對好理解方式簡單的把基本功能實現了,但想要達到最終目的,僅在此基礎上擴展還不夠,這篇會把地圖的邏輯修改部分,用更為高效的方式來實現。那么之前說好的兩篇完結這個系列估計篇幅太長,暫定擴展到三篇。

當然,這不是說第一篇的內容就是無用功,大家之前對于實現方式的思考也不是白費的,其實這表現了真實開發過程中的一個常態,完善的代碼邏輯都是要通過反復思考和工程迭代得來,極少有人能一步到位。



地圖邏輯的重構
不知道大家有沒有想過,為什么說在上一篇文章的基礎上難以為繼?
之前使用的方式是在每一個方塊的中心點生成一個空物體,根據它的坐標為基礎生成mesh數據。這種方式的好處在于很直觀又容易保存每個方塊的相鄰關系,且在處理不同朝向的方塊時可以方便的把世界坐標系下的方塊頂點轉換為本地坐標系的向量。

雖然說了這么多好處,但還有一個致命問題讓我們不得不放棄這種方法,那就是效率問題。
現在地圖規模還太小,不太感覺的出來。一旦規模稍微大一點,比如生成一個長寬高皆為100個小方塊長度的區域,那么總的小方塊個數就是100X100X100=100萬個!這么小一點區域在實際游戲中的大小微不足道,但生成這么一小塊區域就已經是在場景中實例化100萬個物體的巨大效率問題,就別說生成完整的地形了。

不用舍不得老代碼,直接新起一個工程重新開始。雖說之前的代碼全部廢棄了,但仍有一些思路是可以復用的。


其實之前的思路方向大體沒問題,現在只是要稍做一些修改。我們不再把單獨的一個方塊看做一個實例,而是把多個方塊的集合體看做一個整體塊(chunk),然后用更小的數據體去存每個方塊的具體信息。簡而言之就是用一個能裝很多個小方塊的“大方塊”來替代原來小方塊的位置。


為方便計算,大方塊也應該是正方形的.所以只用存每個邊長小方塊的個數,同時小方塊的自己的邊長不再限制為1,改設為一個常量。之前的方塊坐標計算也做相應的修改。這里設置的每個方塊的邊長為2,每個chunk的邊長為16個方塊的長度。



現在重新來寫mesh生成的邏輯。這次不同于之前的工程,我們把mesh生成單獨拆開一個腳本.讓其只用輸入坐標和三角形就能生成任意形狀,方便之后的擴展。大部分邏輯與之前相同,為縮減篇幅,這里就只貼出大致的結構,不貼出完整代碼了。




在chunk中也給它一個自定義的坐標結構體用來定位,并修改為屬性,同時在傳入時同時修改chunk的實際位置和名字。



chunk的預制體,用Gizmos顯示它的范圍






接下來就是根據信息來生成mesh,這里要修改的地方就比較多了。
從上面的代碼可以看到,用的是一個byte類型的數組來存儲cube的數據,也就是每一個cube的數據為一個字節。可能有同學會好奇,一個字節的容量似乎太少,存不了什么東西。但我們可以用位運算的方式可以把需要的基本信息進行壓縮,達到節省存儲空間的目的。


簡單來說就是,把一個字節內的八位分別用一位存儲是否在場景中存在的信息,一位存是否是邊緣透明的信息(這個會在之后用到),兩位用來存朝向信息,四為用來存種類信息。這樣我們就能保存四種朝向和16種方塊類型,這對于我們目前的小工程來說,也足夠用了。




其實這里沒有方塊也可以看做是方塊的類型,可以合在一起存儲,這樣一來方塊的種類就達到了31種

然后用一個結構體和幾種枚舉寫出相互轉換的方法,方便讀取和存儲數據。


當然這里的方法不是唯一的,自己做簡單的數據映射也是一種很有效的信息壓縮方法,大家可以使用自己習慣的方式。


接著就是根據chunk內存儲的數據生成mesh,這里的邏輯與第一篇里的大致相同,只有兩個地方需要修改。





首先,我們在CubeMetrics里存儲的方塊的頂點是相對于方塊自身坐標系的,而現在方塊的朝向是有四種可能變化的。在上一篇文章中,我們在修改方塊朝向的同時也修改了transfrom自身的朝向,然后使用transform.InverseTransformVector()方法把頂點轉換為自身坐標系,達到修改生成的mesh朝向問題。





但現在不存在這個實體了,這一步我們沒法偷懶使用現成組件的方法了,因此只能手動去寫一個針對我們這四個朝向的矩陣變換方法,并在生成mesh時對每一個頂點進行轉換。




cube數據化帶來的另外一個問題是,沒法直接保存每個cube的相鄰引用了。所幸每個cube的相對位置可以通過其在數據數組中的下標算出來。





然后提前把chunk的相鄰引用保存下來,在越界的時候去檢測相鄰的chunk內是否存在方塊。




于是就可以通過方塊的面對方向算出這個面是否需要隱藏。



后面就不截出來了,邏輯是一樣的



接著寫一點測試代碼,根據噪聲圖去生成地形數據。如果沒有現成的噪聲紋理,unity里的mathf庫里是有封裝好的API的,可以直接使用。




現在還沒設置UV,但不妨礙通過mesh的形狀驗證邏輯是否正確。




現在我們把UV貼上。這次我終于認識到了自己不是搞美術材料的事實,老老實實的去挑了一份還看得過去的紋理圖。



這里可以根據距離地形的深度,在每個地質層以隨機概率生成一些礦石之類的東西,就好像真的地形一樣。



看上去還像是那么回事地形動態生成

現在根據指定的坐標生成地形是沒問題了,但在真正的游戲中,人是可以移動的。我們不能在一開始就把地圖生成很大的范圍,那樣的話加載時間會拉到很長,內存占用也是極大。所以只能使用折中的辦法,讓地形隨著人物位置生成,大部分開放世界的邏輯都是如此。



首先思考一下實現方式。讓人物每移動一點距離就把周圍地圖刷新一次顯然不太合適,這樣相當于只要人物在移動時一直都在不停刷新地圖,性能肯定不允許。





理想中的方法是人物在一個區域內移動時地圖不會刷新,但一旦超過這個區域的位置時刷新區域的坐標會根據人物當前位置更新,換句話說就是根據移動距離有一個閾值來判斷刷新。我們可以直接實時獲取人物坐標,以此為基準計算,但吃不準到底刷新區域的大小和刷新頻率設置成多少比較好。因此最好能專門寫一個方法,能方便的調整測試,我們新建一個類去試試。



用Unity內置的Bounds(包圍盒)類來表示刷新的區域,讓其中一個包圍盒的中心點實時的跟隨人物位置的中心點移動。設置一個比例參數去調整跟隨人物移動的包圍盒與刷新區域包圍盒之間的大小比例。





邏輯其實很簡單,當較小包圍盒(人物身上的)的最大點和最小點不在較大包圍盒(刷新區域)里時,調用刷新方法。




寫這個刷新方法之前,先處理一下根據移動坐標刷新的大包圍盒中心的方法。由于我們的chunk在世界中的坐標是根據邊長的固定比例計算的,所以最好把處理后的坐標點與此同步。





接下來就是根據包圍盒的位置和大小去找到需要刷新哪些chunk。




在chunk的根節點上新建一個Grid腳本,存儲和管理所有的chunk和其數據。按我們的邏輯,在處于刷新區域內時,刷新并顯示當前所有處于其內的chunk,而在離開時把可能改動的數據存儲在以坐標為key的字典中。在刷新時優先讀取以存在的數據,沒有時才根據自身坐標生成數據。否則我們辛辛苦苦蓋的房子出去走了一圈回來就不見了可就不太對勁了。順便使用對象池來管理chunk,進一步減少GC,這是很常見的優化方法,就不把這部分貼出來了。





最后,把需要刷新的chunk放在一個容器中,在協程中以每幀一個的速度刷新到場景中。這里使用的容器是棧(stack),主要是考慮在移動速度可能過快的情況下,優先刷新后改變的chunk,也就是面朝移動方向的先刷新。





最后驗證一下效果:



可以看到速度過快時刷新速度會有些跟不上,但是加快刷新速度又會明顯降低幀數。這里有很多辦法去優化,比如在靜止不動時刷新更大的區域給予更多緩沖區域之類的。但對我們的 demo來說,只要限制速度,也足夠用了,這部分的優化留給大家自己發揮。當務之急是檢測一下對地圖修改后的數據能不能正確保存下來。
在地上挖一個大坑,看看出去一圈有沒有變化。




可以看到這部分邏輯也沒問題,那么現在對我們來說,只要內存允許,我們的地圖就是“無限“的。可以針對這部分做的優化還有很多,比如增加進入游戲的加載時間讓開始的地圖變得大一些、優化刷新邏輯使其更流暢、地圖數據轉存到硬盤實現存檔功能等等,不過這些都是后話了。




簡單的人物控制
經過前面的邏輯實現,這部分已經沒什么好寫的了。功能與方法的接口在前面都已經搭好了,照著去調用就是了。
關于人物的移動和視角還有個很有意思的地方。大家都知道,大部分的類似方塊建造的開放世界游戲大多使用的是第一人稱視角,但DQ2卻是第三人稱的!雖然也能切回第一人稱視角就是了。我能大概理解可能是基于RPG要素的考慮設計了這么一個操作模式,但是在游玩的時候覺得有些需要精確建造的位置確實有點不方便,很難把視角對準。但當我嘗試去復現的時候,才發現DQ2的第三人稱視角已經是做過許多設計和功能上優化了(以玩家角度很難感覺到開發者的苦啊),完全復現實在是工作量太大。所以抱歉的容我在這偷個懶,用第三人稱移動和第一人稱建造的混合模式去糊弄過去(這其實有點像DQ2后期的建造師手套功能,也算是某種程度上的還原吧)。


首先是人物,二頭身的3D模型還加動畫,這個要求分明就是在刁難胖虎,不過還好找了很久之后,終于找到一個水手服雙馬尾蘿莉,還不要錢。(我沒有在開車,你們也沒證據)





現在人物有了,下一步就是人物動畫,移動控制和相機控制,這部分任何游戲都可能會遇到,與今天的主題沒多大直接關系,不在此贅述了,對這部分有興趣的同學自己下載工程吧。


總之,在掛上控制腳本后,把之前的刷新地圖腳本也掛載在人物身上,我們的小蘿莉就可以在無限寬廣的世界中任意遨游了。



這里還需要說一下一個我遇到的小問題,mesh刷新時這里是直接把原來的緩存數據的mesh清空,讀值之后再給meshCollider賦值的。這樣會帶來的問題是當賦了一個沒有頂點的空mesh之后,這個chunk的meshCollider就會失效,之前測試時我的小蘿莉經常跑著跑著就掉下去了。


后經查閱,推測是在給meshCollider賦值時,如果引用不變,無法觸發set屬性達到刷新碰撞的目的,因此這個地方稍作修改就行了。





還沒完,現在還需要讓小蘿莉能蓋房子,不然都對不起這個標題。這個時候另外8個剛才沒用到的方塊紋理就派上用場了,先做個簡單的UI,用toggle組件把圖標與放置方塊時的類型一一對應起來。







根據鼠標位置發射一條射線,把打倒chunk的坐標點換算到chunk的自身坐標里,再去修改方塊的數據。





最后,在人物的update里實時去刷新這個坐標的值,僅在點擊時調用setData方法。為了方便觀察邏輯是否正確,為當前射線擊中坐標添加一個預覽方塊,并添加一點粒子效果表示chunk上數據的修改。




就別吐槽這個雞啄米的動畫了... 免費素材能用的動畫就那么幾個。




另外提一句預覽框的效果怎么做。png格式的圖片是可以保存alpha通道信息的,但是直接把這種鏤空紋理的圖拖入材質球是沒效果的,還要把標準著色器里的渲染模式改成Cutout。



結束
現在基本邏輯都大概還原了,總算是站在了最先想要實現功能的起點上,老實說在一開始的時候真沒想到要繞這么大一個圈。這期的內容可能會有些多,但是以文章為載體我沒辦法說的很詳細,所以感興趣的同學還是下載工程研究。


知乎@沈琰


1.jpg (47.88 KB, 下載次數: 8)

1.jpg
回復

使用道具 舉報

7日久生情
2919/5000
排名
2230
昨日變化

1

主題

1893

帖子

2919

積分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
119154
好友
0
蠻牛幣
3260
威望
0
注冊時間
2015-8-21
在線時間
395 小時
最后登錄
2019-8-16
沙發
2019-7-11 08:41:56 只看該作者
謝謝樓主大大、。
回復

使用道具 舉報

7日久生情
2092/5000
排名
4092
昨日變化

0

主題

1371

帖子

2092

積分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
254705
好友
1
蠻牛幣
1906
威望
0
注冊時間
2017-11-16
在線時間
359 小時
最后登錄
2019-8-16
板凳
2019-7-11 08:42:53 只看該作者
6666666666666666666666666666
回復 支持 反對

使用道具 舉報

4四處流浪
316/500
排名
12479
昨日變化

2

主題

124

帖子

316

積分

Rank: 4

UID
310955
好友
0
蠻牛幣
306
威望
0
注冊時間
2019-1-7
在線時間
130 小時
最后登錄
2019-8-16
地板
2019-7-11 09:20:47 只看該作者
哇,用的是我最喜歡的方塊概念材質唉!(?????)
回復 支持 反對

使用道具 舉報

排名
64933
昨日變化
1

0

主題

28

帖子

49

積分

Rank: 1

UID
303137
好友
0
蠻牛幣
12
威望
0
注冊時間
2018-11-6
在線時間
19 小時
最后登錄
2019-7-30
5#
2019-7-11 10:01:22 只看該作者
66666666666666666
回復 支持 反對

使用道具 舉報

排名
64933
昨日變化
1

0

主題

43

帖子

57

積分

Rank: 2Rank: 2

UID
215482
好友
0
蠻牛幣
3
威望
0
注冊時間
2017-3-30
在線時間
12 小時
最后登錄
2019-8-16
6#
2019-7-11 10:09:21 只看該作者
頂頂頂頂頂頂頂頂頂頂頂頂頂頂頂頂頂頂頂
回復 支持 反對

使用道具 舉報

0

主題

3

帖子

9

積分

Rank: 1

UID
163271
好友
0
蠻牛幣
4
威望
0
注冊時間
2016-8-20
在線時間
6 小時
最后登錄
2019-7-12
7#
2019-7-11 20:04:05 只看該作者
6666666666666666666666666
回復 支持 反對

使用道具 舉報

7日久生情
2092/5000
排名
4092
昨日變化

0

主題

1371

帖子

2092

積分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
254705
好友
1
蠻牛幣
1906
威望
0
注冊時間
2017-11-16
在線時間
359 小時
最后登錄
2019-8-16
8#
2019-7-15 15:47:22 只看該作者
6666666666666666666666666666666
回復 支持 反對

使用道具 舉報

7日久生情
2376/5000
排名
1387
昨日變化

0

主題

741

帖子

2376

積分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
135463
好友
0
蠻牛幣
48
威望
0
注冊時間
2016-1-23
在線時間
743 小時
最后登錄
2019-8-17
9#
2019-7-16 15:24:52 只看該作者
666666666666666666666666666hhhhhhhhhhhhh
回復 支持 反對

使用道具 舉報

5熟悉之中
717/1000
排名
6394
昨日變化

2

主題

46

帖子

717

積分

Rank: 5Rank: 5

UID
125626
好友
1
蠻牛幣
84
威望
0
注冊時間
2015-10-15
在線時間
457 小時
最后登錄
2019-8-14
10#
2019-7-17 18:27:06 只看該作者
66666的一批
回復

使用道具 舉報

5熟悉之中
739/1000
排名
10706
昨日變化

0

主題

485

帖子

739

積分

Rank: 5Rank: 5

UID
301976
好友
1
蠻牛幣
1102
威望
0
注冊時間
2018-10-31
在線時間
156 小時
最后登錄
2019-8-16
11#
2019-7-19 10:28:37 只看該作者
MeshPro...
回復

使用道具 舉報

7日久生情
2181/5000
排名
601
昨日變化

3

主題

140

帖子

2181

積分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
44272
好友
3
蠻牛幣
6152
威望
0
注冊時間
2014-9-10
在線時間
658 小時
最后登錄
2019-7-22
QQ
12#
2019-7-22 11:55:35 只看該作者
干貨~~可以哈。~!
回復

使用道具 舉報

排名
28931
昨日變化

0

主題

38

帖子

91

積分

Rank: 2Rank: 2

UID
21931
好友
0
蠻牛幣
121
威望
0
注冊時間
2014-4-18
在線時間
41 小時
最后登錄
2019-8-16
QQ
13#
2019-7-22 23:11:29 只看該作者
學習學習
回復

使用道具 舉報

4四處流浪
325/500

2

主題

61

帖子

325

積分

Rank: 4

UID
156657
好友
0
蠻牛幣
1129
威望
0
注冊時間
2016-12-22
在線時間
254 小時
最后登錄
2019-8-16
14#
2019-7-24 09:26:33 只看該作者
有些圖掛了啊
回復

使用道具 舉報

5熟悉之中
595/1000
排名
5175
昨日變化

0

主題

105

帖子

595

積分

Rank: 5Rank: 5

UID
192868
好友
0
蠻牛幣
493
威望
0
注冊時間
2016-12-16
在線時間
212 小時
最后登錄
2019-7-31
15#
2019-7-26 18:12:28 只看該作者
學習mark
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 注冊帳號

本版積分規則

捕鱼王怎么进不去 1万人棋牌 欢乐生肖人工计划网站 江苏时时预测软件 八大胜网址网 快乐时时官网下载手机版 彩宝彩票官网安卓 双人斗地主游戏 抢庄牌九平台官网 即时比分直播 pk10人工免费计划 天津时时彩开奖结果 无错六肖王财神 华夏国际娱乐 快三大小单双玩法技巧规律 必赢客计划 推二八杠赢钱技巧手法