接着我的
上一篇自動尋路文章,這一次我們就來學習一下與自動尋路有關的元件吧。Unity中與自動尋路相關的元件主要有兩個:NavMeshAgent
( 又稱導航網格代理 ),Off Mesh Link( 分離網格連結
)。這兩個元件的作用與使用範圍是不同的,我們唯一可以确定的是我們必須烘焙地形,産生NavMesh(導航網格)。因為導航網格決定我們的角色(帶有導航網格代理的角色)活動的範圍。NavMeshAgent元件需要附着尋路的角色身上,比如怪物,而OffMeshLink這個元件主要是用來構造尋路角色的尋路路徑的某個部分,比如我們有時需要怪物在尋路過程中從一個固定的地方移動到另一個固定的地方,這将會在我下面的例子中清楚的看到。好了,甭廢話了,讓我們開始吧!
首先,我們先來了解一下NavMeshAgent元件,這個元件是提供的尋路系統的核心元件。官方是這樣解釋的:The NavMeshAgent
component is connection with pathfinding,and is the place to put information
about how this agent navigates the NavMesh
。意思大緻是這樣的:NavMeshAgent元件是關于尋路的,它是一個用來存放代理周遊導航網格的路徑資訊的平台。那麼代理又是什麼呢?原來,角色的移動是要依靠代理來做的,每一個附着這個元件在尋路的過程中都是利用代理進行的,這也就是這個元件為什麼叫導航網格代理的原因。每一個你需要讓它具有自動尋路功能的角色必須要附着這個元件,除非你利用其它的尋路算法,但那樣做實在是太複雜了,因為考慮的情況太多了,然而Unity為我們提供了這樣一個元件,我們為啥不用呢?我們先來舉一個例子吧,這樣學起來也好了解一些。
我接着我上一篇文章中的工程,建立了一個Scene,給了它一個名字:TestNavgation2。然後這個場景裡面也需要一個地面,我還是用Cube來做。這次我先向這個平面上建一個Spere,重命名為Hero,并且給它加上一個NavMeshAgent元件,方法:Unity菜單,Component->Navgation->NavMeshAgent。給Hero上個顔色就像下面截圖一樣:
選中Hero,在Inspector下,我們可以看到NavMeshAgent元件的各個屬性:
這幾個屬性我簡單的解釋一下:
Radius:導航代理的半徑,我們可以适當的調節一下這個值
Speed
:這個屬性代表這個導航網格代理尋路時可以達到的最大速率
Acceleration
:加速度,表示代理的速度從0加速到Speed時的最大的加速度
Angular Speed
:最高的角速度
Stopping distance :
制動距離,當代理據目的地的距離小于這個值時開始減速
Auto Traverse OffMesh Link
:自動移動并關閉OffMeshLinks,這個選項對于我們利用程式來操縱後面我要介紹的OffMeshLink很關鍵,
Auto
Repath
自動重新尋路,如果發現現有路徑已失效,那麼它将獲得新的路徑,這個選項我們一般将其勾選上
Height :
導航代理的高度。
Base Offset :
基本偏移,我們可以通過調整這個變量來調整代理自身的包圍盒
Obstacle Avoidace
Type : 代理躲避的水準,一般我們選預設的High Quality就行了
NavMesh
Walkable
:導航網格代理可以通過的網格層類型
好了這些屬性我們基本上知道了一點,但要真正了解它,我們還有很長的路要走。我們到頭來是要用腳本來控制尋路的,也就是說我們必須掌握NavMeshAgent這個類,還是老辦法,看文檔吧,記住,文檔我們必須看,這是我們學習新東西必須做的,那些出視訊,出書的沒一個不是從看文檔開始的。
我們看NavMeshAgent這個類,Unity菜單:help->Scripting
Reference 。我們找到這個類,發現裡面的變量和方法還不少呢。這次我不可能将其全部講解到 ,
因為有些我自己都沒用過,是以我隻講解我們常用的:
NavMeshAgent(導航網格代理元件所對應的類)
假使我們的主角身上添加了一個導航網格元件,我們一般在腳本中這樣定義NavMeshAgent類型的成員變量:
private
NavMeshAgent nma ;
并在Start或Awake函數中執行個體化它:
nma =
gameObject.GetComponent<NavMeshAgent>();
重要變量:
1.destination
我們可以這樣對導航網格代理設定目的地:
nma.destination
= Vector3類型的值。相當于nma.SetDestination( Vector3類型的值
)
2.stoppingDistance
這個與Inspector面闆中的Stopping
Distance對應,一下再不涉及與Inspector面闆中的屬性對應的變量
3.velocity
導航網格代理周遊時的實時速度,非常重要
4.nextPosion
顧名思義,也就是下一個位置,在Update函數中列印這個屬性,你會發現列印出的結果與這個導航網格代理周遊過的路徑一緻
5.steeringTarget(隻讀)
這個屬性是相當重要的,它指的是導航網格代理在導航網格中周遊時所經過的拐點,這對于制作尋路網遊同步角色的Transform是相當重要的,因為導航網格的尋路路線是直線,我們隻需将其尋路的拐點與旋轉角度告訴給伺服器端,有伺服器端廣播出去,然後再寫一個執行Transform同步的腳本(是特制的)綁定在非角色玩家身上就可以了。
6.desiredVelocity(隻讀)
這個屬性說實話我用的不多,指的是導航網格代理的期望速度,與其目前速度不是等價的。
7.remainingDistance(
隻讀
導航網格代理離目的地還剩的距離,如果其值為0,那麼代理已經到達了目的地了,是以我們可一個這樣判斷一個導航網格代理是否到達了目的地:
if(
nma.remainingDistance == 0
){
//執行行為
}
8.isOnOffMeshLink
導航網格代理目前的位置是否位于OffMeshLink,因為這個牽扯到了另一個元件,我會在後面說的
重要方法:
1.SetDestination(
Vector3 v )
設定目的地,與nma.destination =
v一樣的,你想怎麼用都行,隻是這個函數在設定目的地成功後傳回***e,否則傳回false,就隻比調用屬性多了一個傳回值
2.ActivateCurrentOffMeshLink(
bool activated
傳回值為空
與OffMeshLink有關,當activated為***e激活OffMeshLink,後面會講到的
3.CompleteOffMeshLink
()
讓導航網格代理完成在OffMeshLink上的周遊,後面會講的
4.Move(
Vector3 v
讓導航網格代理朝向量v的世界坐标系方向平移v的長度
5.Stop()
讓導航網格代理停止尋路,但此尋路狀态可以靠下面一個函數恢複到尋路狀态,并且目的地也與上次一樣
6.Resume()
恢複尋路狀态,此時角色會在上一次執行了Stop函數停下來後恢複當時的狀态,目的地為上一次的目的地
這8個屬性與Inspector面闆上的各個屬性并且和這6個函數我們一定要好熟練掌握,這關系到我們是否能熟練書寫尋路腳本。還有一些函數我這裡沒有介紹,就留着讀者自己研究研究吧。
此刻我相信讀者對這個元件已經有了相當深刻的認識了,但是還沒完,我們必須做的一個步驟就是烘焙場景,生成導航網格。為什麼要這樣做呢?因為Unity3d自帶的尋路系統的原理是事先通過烘焙将地形的資訊記錄起來存儲在NavMesh檔案上。我們烘焙一次看看,其實做法很簡單
,我們打開Navigation視窗,做法:Window>Navigation。然後選中Plane,出現下面截圖:
我們可以看到Navigation
Static複選框,勾選它。那它的作用是什麼呢?原來,每一個GameObject都可以标記成靜态的或非靜态的,就想這樣:
我們看到Plane的右側有一個Static屬性,展開他我們可以看到:
這裡面每一種靜态選項背後都包含一種技術,比如Lightmap
Static,用于生成光照貼圖對場景進行優化。還有Occluder Static與Occludee
Static,是關于Unity3d中與遮擋剔除技術有關的。好了,言歸正傳,導航網格代理是在導航網格上周遊的。是以我們的地面必須生成導航網格,這裡的Navigation
Static屬性框必須勾上。這時我們看到了第二個屬性框:OffMeshLink
Generation,勾選上之後我們就可以不借助OffMeshLink元件來生成OffMeshLink。那麼什麼是OffMeshLink呢?請看下圖:
看到那些個線沒有?每一條線就代表一個OffMeshLink。那麼此時我可以引入OffMeshLink元件了,這個元件其實就是自定義像上圖那樣的樣條線,但每一個OffMeshLink元件隻能形成一個樣條線。這個樣條線的作用可不一般啊,但是應用其時我們必須格外注意一些問題,不然我們即使用了這個元件也不會産生絲毫的作用的。
那好吧,我們該做點什麼了!
如上圖,我建了連個Plane,分别為Plane1,Plane2。我們一次選擇這兩個平面,在Navigation視窗将Navigation
Static 與Off Mesh Link Generation選項給勾上,并選擇Navigation
Layer為Default。然後單擊Bake按鈕,如下:
我們還建立一個圍牆,用Cube做的,我們在Navigation面闆中除了勾選Navigation
Static之外,還必須将其Navigation Layer下拉框中選擇Not Walkable,即讓我們的Hero繞過此障礙物達到目的地。Not
Walkable隻是導航網格層中的一個内建層,我們還可以建立我們自己的導航網格層。關于導航網格層,我會在我的下一篇文章中詳細為您講解。
我們之前說過,導航網格代理的活動空間隻能是導航網格,即NavMesh。但現在看來這句話可能需要修改一下了。因為導航網格代理還可能活動在OffMeshLink上面,是以我們可以在兩個平面上面建立OffMeshLink來連通兩個平面。我們現在沒有用Off
Mesh Link元件,這樣就可以生成很多的OffMeshLink。但是我們發現烘焙後的場景沒有出現Off Mesh
Link。到底是什麼原因呢?原來,我們還得設定一些參數:
我們看到了一個選項
:Jump Distance。我們将這個值調到4,再次烘焙一次,則出現了以下場面:
然後我們建立一個Cube,命名為:DS。将其放置在上圖中的白色的Cube所在的位置上,然後我寫一個腳本:
using
UnityEngine;
System.Collections;
public class SetHeroDes : MonoBehaviour
{
public Transform
ds;//目的Cube的Transform
private Vector3
origin;//存儲導航網格代理的初始位置
private NavMeshAgent
nma;//存儲導航網格代理元件
void Start ()
nma
= gameObject.GetComponent<NavMeshAgent>();
//取得導航網格代理元件
origin = transform.position;
//執行個體化origin
void OnGUI()
{
if(GUILayout.Button("Start***n"))
{
nma.SetDestination(tf.position);
//設定導航網格代理的目的地
}
if
(GUILayout.Button("Resume"))
{
transform.position = origin;
//恢複導航網格代理的位置為初始位置
我們将這個腳本綁定到Hero上,然後将DS拖拽到指定位置:
然後我們運作一下:
我們可以清楚的發現,我們的Hero越過了重重阻壑,終于到達了目的地。可是問題此時又随之而來了:如果我們将Hero中的導航網格代理元件中的Auto
Traverse Off
Ms選項給去掉,它還會越過重重溝壑到達我們的目的地嗎?實驗證明這樣做是無法成功的。官方文檔對這個勾選的解釋為:
Automate
movement onto and off of
OffMeshLinks。大緻是這樣的:自動移動并且将OffMeshLinks關閉。你想,OffMeshLink都關閉了,Hero還怎麼過來呢?
接下來我再來介紹OffMeshLink元件的用法了,操作非常簡單:
1.建立連個空的GameObject或者你用模型也行,分别取名為StartPoint和EndPoint。為了便于識别,我用的是Sphere,給它上個綠色,并調節StartPoint的位置為Plane1上面,EndPoint的位置咋Plane2上面;
2.接着我們建立一個空的GameObject,取名為:SingleOffMeshLink,并讓StartPoint與EndPoint成為其子物體,為SingleOffMeshLink加入OffMeshLink元件,做法:選中SingleOffMeshLink,然後再Unity菜單欄中:Component->Navigation->Off
Mesh Link。如下圖:
我們将StartPont拖拽到Start上,EndPoint拖拽到End上。最後烘焙一下場景(此時Plane1與Plane2在Navigation中的OffMeshLink
Generation勾選給去掉,目的是自定義我們的OffMeshLink):
看到沒,隻生成了一條樣條線。這就是我們自己做的OffMeshLink。