這接上以前的 Locomotion 我的人形控制開發筆記(一) Locomotion,
那篇只是單純的用移動向量找出 X, Z 軸的位置資訊再輸入到 Animator 中成為移動的資料流.
只是單單解決了「計算出平地上的移動向量」
可是這裡面有好幾個大問題, 例如
- 計算出有障礙物擋著時未能達成預定的行程 Vector (如碰壁/緊貼牆壁行走)
- 計算出單單只是步腳移動的Vector (在會移動的平台上,隔離出平台移動的資訊)
本篇先論一下在 (A) 有障礙物擋著的情況
如果只依上篇的方式實作, 在跑到牆壁的時候會出現下圖這種「向牆壁跑」的不合理動作, 原因是我們直接把想跑的 Local (Vector3) 直接丟到 Animator, 而沒有考慮人物是否被阻擋的情況.
這部份因應實作的方式不同有不同方法處理.
方法一, Rigidbody 實作
Rigidbody 可以用 SweepTest 做預判, 從而預先取得 vector distance 並計算 wall normal projection.
並且自行計算本幀的移動向量, 採用這方案的話 Animator 輸入方面倒是比較簡單.
可是這個方案對複雜地形的項目非常不友善, e.g. 上下坡度, 凹凸地表, 樓梯(可踏)…etc
解決這些問題可能耗用大量的物理運算, 除非關卡設計上可以避免否則不建議.
方法二, CharacterController 實作
由於 CharacterController 會自己進行碰撞測試, 所以使用這種方式倒是可以輕鬆解決很多 AABB test 的煩惱.
但實作時也有不少需要準確到 每幀的例外操作, 編寫時對於 CharacterController 的存取架構需要下一番心思去避免多方存取問題.
以方法二, 來實作 (A) + (B) 的話有一個問題, 人物移動的距離是不可預知的因為本幀的 CharacterController.MoveTo() 還沒有被呼叫.
我想到的辦法就是拖延一幀來運算. 不過這已經是下一篇的內容, 在這暫且跳過….
現在先來看看怎樣用數學來解決用頭撞牆的問題~
要計算實際移動的向量我們需要下面的資訊.
- 上一幀人物的位置(position)
- 本幀人物的位置(position)
- 本幀的 Time.deltaTime – 用以 distance, speed, time 運算 掃盲系列 : Vector 矢量
Vector3 lastPosition; private void FindOutLocalMovement() { // the actually movement from last frame to current. Vector3 motionVector = transform.position - lastPosition; // the movement in terms of local space. Vector3 localMotionVector = transform.InverseTransfromDirection(motionVector); // since vector was presenting the distance, so (distance / time) = speed Vector3 localXYZSpeed = motionVector / Time.deltaTime; // pass to animator animator.SetFloat("LocalX", localXYZSpeed.x); animator.SetFloat("LocalY", localXYZSpeed.y); animator.SetFloat("LocalZ", localXYZSpeed.z); // finish, update last position for next calculation. lastPosition = transform.position; }
如果我們不是直接把輸入丟進去 Animator, 而是把它分離計算的話, , 動作就自然很多了.
就這樣, 行走的步距就會因應實際的移動而套用適當的步距到 Animator 上.
下一篇~ : 解決平即上的移動
請問方法二提到的 code snippets,是已經考慮拖延 1 frame 的做法了嗎?
因為我從 codes 無法看出哪步驟有考量到可能碰撞到障礙物
感謝大大的分享
是喔,lastposition就是上一幀的位置嘛,目的並不是撿測撞牆,而是計算實際移動過的距離,然後放到animator播放相對應的步距.