進擊的新版NavMesh系統:看我飛檐走壁


0x00 前言

unity5.6作為Unity5最后的一個版本,的確起到了一個承上啟下的作用。除了上一篇文章《進擊的AssetBundles和它的工具們》中提到的AssetBundles-Browser,本文還會介紹另一個在Github開源的,用於Unity5.6+的新尋路功能。

0x01 曾經的痛點

Unity5.6之前的navmesh系統的確操作起來十分容易上手,門檻很低。我們只需要將場景內需要烘焙navmesh的區域勾選上Navigation Static選項,之后就可以在Navigation窗口中烘焙整個場景了。

QQ截圖20170717135759.png

但是曾經的navmesh系統卻也存在着一些性能上的和使用場景上的缺陷。
一個常見的問題,由於要預先烘焙場景的navmesh,因此我們很難方便的在運行時動態的修改navmesh。更不用說,有一些游戲的場景並非提前制作好的,需要在運行時動態的生成,這種情況下就無法使用navmesh了。
另一個問題是,如果場景過大的話,烘焙之后的navmesh也會保存很多數據,在運行時會造成一些內存上的開銷。
當然,拋開這些不談,另外一個讓我吐槽navmesh的一點就是,它竟然不支持垂直面的導航。

donkey-kong-screenshot.jpg

在做一些2d的platform游戲時,我很希望能利用navmesh來實現尋路的邏輯。(圖文無關)
但是,不幸的是,之前的navmesh是不支持的。
QQ截圖20170717151840.png

0x02 組件化的navmesh

不過還好,雖然新的navmesh系統並沒有隨着unity的正式版本一同發布。但是,我們還是可以通過github來獲取這些新的功能:

NavMeshComponents

需要注意的是,Unity的版本要求在5.6以上。

我們可以看到,其實這里只有4個高層的C#腳本文件:

QQ截圖20170717161301.png
利用這4個腳本文件,就能基本解決我們之前的煩惱了。

其中NavMeshSurface這個腳本將navmesh組件化,利用這個組件就可以很方便的烘焙掛載該組件的對象的navmesh信息,而無需打開一個navigation窗口對整個場景進行烘焙了。我們甚至可以將掛載這個腳本的GameObject烘焙后保存為一個prefab,這個帶有navmesh信息的prefab跟其他的prefab一樣。

1.gif
為對象添加NavMeshSurface組件很簡單。在這里我們可以看到和之前navigation窗口類似一些設置,但是請注意,這里已經不是整個場景烘焙了。navmesh已經組件化了,它只會烘焙掛載它的對象。

2.gif

只要點擊一下這個組件下的Bake按鈕,掛載它的對象就被烘焙好了。
那么GameObject能否掛載多個NavMeshSurface組件呢?這一種需求也的確存在,例如怪物和玩家的尋路策略不同,有些地方玩家能通過而怪物卻不能通過。
這的確也是可以的,同一個GameObject能夠同時掛載多個NavMeshSurface組件,並且烘焙不同的navmesh供不同的角色使用。

5.gif

這樣,我們針對不同的角色的NavMeshAgent組件設置不同的agent type並和烘焙好的兩個navmesh匹配好就可以了。

0x03 飛檐走壁

好了,借助NavMeshSurface組件我們實現了navmesh的組件化。那么是不是我們就能很方便的實現在垂直面上烘焙navmesh了呢?各位想想我們是否能很輕松的讓一個游戲對象的角度改變呢?答案是是的。那么這個游戲對象上如果有navmesh信息的話,我們只需要把這個游戲對象從水平變為垂直是否就行了呢?是的。
因此實現游戲角色的在垂直面上飛檐走壁的功能就變得十分簡單了。

120111111.gif
當然了,在水平面的navmesh和垂直面的navmesh之間我們還會用到NavMeshLink這個組件來鏈接二者。各位自己在實踐的時候需要留意一下這一點。

0x04 在運行時烘焙navmesh

接下來就讓我們看看新的navmesh系統帶給我們的新的驚喜——在運行時烘焙navmesh。
這是一個很現實的需求,例如一些動態生成場景的游戲,我們無法在一開始就確定這個場景到底是什么樣子的,所以也無法使用之前的navmesh系統,因為以前的navmesh只能在editor內烘焙。但是現在我們使用新的navmesh系統就能夠很方便的在運行時烘焙navmesh了。

120111.gif
如圖,這是一個空場景,在游戲運行之后場景才生成出來場景內的各種道路,此時單擊鼠標,navmesh就生成了。
其實在新的navmesh系統內,實現這個機制十分簡單。只需要調用游戲對象上掛的NavMeshSurface組件的BuildNavMesh()方法。

void Start()
{
	surface = GetComponent<NavMeshSurface>();
}

void Update()
{
	if (Input.GetMouseButtonDown(0))
    {
		surface.BuildNavMesh ();
    }
}

既然navmesh已經可以在運行時創建了,那么我們能否也在運行時實例化一個navmesh的prefab,實時的影響場景內的尋路策略呢?
答案是當然可以。

0x05 場景太大不用愁

自己做過尋路算法的童靴可能會意識到一個問題,就是在做尋路時如果場景過大的話,尋路的數據可能會比較消耗內存。同樣在navmesh中,如果場景過大,或者玩家的視野范圍有限,一些對玩家當前位置影響不大的場景的其他位置的navmesh數據就有可能造成一些無謂的消耗。
在新的navmesh系統中,我們同樣可以優化這個問題,只烘焙玩家周圍的navmesh。
EADTwTIM3A7bpEsOJ6nCkQgV2rQRzzVh.gif
這里同樣需要NavMeshSurface組件,在inspector視窗我們可以選擇collect object中的volume,之后設定size的值就可以值烘焙這個范圍內的navmesh了。之后隨着玩家的移動,再動態烘焙新的navmesh就可以了。

0F5wYAckNZOcc3vNPSKD6GqAmj09MKYt.gif

ref:

【1】High-level NavMesh Building Components
【2】Unite Europe 2017 - Finding the path: New navigation features

各位如果覺得有趣的話,歡迎點個贊。

-EOF-
最后打個廣告,歡迎支持我的書《Unity 3D腳本編程》

歡迎大家關注我的公眾號慕容的游戲編程:chenjd01


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM