前言
這篇更像是一個理程碑的東西, 算是為自己在公在私的電腦 AI 特別是敵人Enemy 或 NPC 的一個小總結.
NavMeshAgent 是一個內建的尋路系統 Pathfinding System U3D 5代之後的還是相當可靠的, 這邊要額外的宣傳一下 Unity 有一個小專案 NavMeshComponents 來加強套件的功能.在你想使用任何其他的 Pathfinding 方式之前最好先了解一下這個小專案.
按 : 在 U3D 2018 版後 NavMeshComponents 大部份功能已經變為內建. Yes,可以透過 NavMeshBuilder 來進行 runtime map bake.
怎樣開始?!
為 NPC 們套動畫以經驗來說我們經常需要取得一個 GameObject 的下以資訊
- 上一幀
- 位置 (Position)
- 面向 (Rotation)
- 移動向量(Vector)
- 方向 (Direction)
- 速度(Speed)
- 距離(Distance)
- 代表站立平面的代表 (Vector -GroundNormal)
在寫複雜 AI 的時候如果經常計算以上的東西是很花資源的,
e.g. vector 的 distance 算式中的 平方根(Square Root Calculations) 是較慢的函數計算方式
但是如果考慮到其泛用性的話, 每幀只計算一次再於多個程序中使用的話倒是沒有甚麼大問題.
綜合以上的需要我自己寫了一個便當式的數據集合庫來計算並暫存特定 GameObject 的相關資訊, 有這個做基層的話其後需要使用時直接引用這個元件就可以跳過大部份的運算來取得答案.
PS: 當中的 Gizmos 是自己用來繪畫線條的 API, 用作畫面 debug 用途.
一些基礎
這其實一般程序都沒有特別在意這擋事, 但在製作 A 級大作的時候是很重要的一環. 除非~ 你能夠接受 NPC 的腳步在地面上漂移.
先說明一些簡單的單位概念, 還真的滿多人不知道的.
首先 U3D 引擎的 1 Unit 其參考就是 1 米. 在 Editor 中建立一個預設的 Cube 就是 1m x 1m x 1m 的大小.
而 NavMeshAgent
Steering
- Speed 亦是這個 AI 在沒有干擾之下所能行走的最高速度 (假設直線及沒有任何物理因素)
- Angular Speed 是 AI 最快的旋轉速度.
- Acceleration 是加速度嘛, 靜止加速, 轉角漂移 會看這個值.
- Stopping Distance 到達目的地有多精確.
- Auto Braking 差不多後達的距離會有減速的情況, 來避免超越終點 Overshoot
一個物件跑多快?!
要知道NavMeshAgent 跑多快才能知道要放甚麼動畫. (廢話), Speed, Time, Distance 的換算個人認為是必修的.
綜合以上, 我們只要記錄以下的資訊即可
- 時間 – 由上幀的 Update 到現在的 Update 的時間, 就是 Time.deltaTime, 這是系統給的直接用就可以
- 方位 – 由上幀到現在的的移動距離 lastPosition – currentPosition, 這一支 Vector3 就含有方向資訊.
- 距離 – 由”方位”換算出的 length 就是移動距離. 即是 (lastPosition – currentPosition).magnitude
Vector3 lastPosition; private void Update() { float time = Time.deltaTime; Vector3 currentPosition = transform.position; Vector3 movement = lastPosition - currentPosition; float distance = movement.magnitude; float speed = distance / time; // // Update the lastPosition at the end lastPosition = currentPosition; }
憑藉 distance, time, speed 的運算我們可以做很多事.
包括動畫的切換及插放, 一些遊戲的觸發機制等等.
怎樣為 AI 設一個簡單的 Locomotion.
Locomotion 簡而言之就是一套基於不同方向下的基礎行走動畫組 (詳見 我的人形控制開發筆記(一) Locomotion)
嗯, 跳 (jump) 和攻擊 (Attack) 是不包括在內的.
在現令的 3D 遊戲來說, 全方向 Locomotion 已經是基本了.
有 8 個不同方向的骨骼行走動畫就可以在 Animator 裡做到下面的效果.
(PS: 如果不知道這個畫面, 自己到 Animator 裡找一下如何加入 Blend Tree)
然後就如同前面所提到的把 Speed 放進去就可以.
基本上把前面的代碼改一點..
Vector3 lastPosition; private void Update() { float time = Time.deltaTime; Vector3 currentPosition = transform.position; Vector3 movement = lastPosition - currentPosition; Vector3 localMovement = transform.InverseTransformVector(movement); Vector3 local3DSpeed = localMovement / time; animator.SetFloat("LocalX", local3DSpeed.x); animator.SetFloat("LocalZ", local3DSpeed.z); // X & Z = Horizontal movement; animator.SetFloat("LocalY", local3DSpeed.y); // Up / Down movement // // Update the lastPosition at the end lastPosition = currentPosition; }
local3DSpeed 就是基於移動物本身的 3 軸速度,
假設 Animator 的 Blend Tree 設定沒有出錯的話, 這支 Vector 就代表了人物的水平移動速度 (X + Z) 軸.
到此就可以建構一個有基本 360度行走的怪物.
如果對 Vector 概念不熟悉的可以再溫習一下掃盲系列 : Vector 矢量