4-17 Wandering NPCs
前一章我們完成了簡單的NPC系統,但是NPC在場景中亂跑,這樣的感覺很奇怪對吧?所以本章要教大家製作走路的NPC。
首先,請大家看看NPC使用的ThirdPersonAnimatorControl。
在Animator視窗點擊Grounded狀態。
從Inspector視窗會看見Motion的部分使用的Blend Tree,請點兩下開啟。
開啟後會看見如下圖的視窗,當紅點在中央下方時,角色的狀態是不會動的Idle狀態。
當紅點調整到最上面,就會發現角色開始跑起來了。
如果將紅點慢慢移到下方,角色的動作又會變得緩慢,像是在走路一樣。
所以,根據以上介紹的機制,透過調整Forward數值,可以控制角色的動作是偏向走路或者跑步。程式碼中在Character.cs中的UpdateAnimator方法,於forwardAmount中乘以一個參數,名為animatorForwardCap,這個參數會設為SerializeField。
修改後的Character.cs如下:
修改好程式碼後,大家可以參考下圖的參數進行Character的設定,我將Animator Forward Cpa設為0.3,Move Speed Multiplier設為0.3,Moving Turn Speed跟Stationary Turn Speed設為100。
執行遊戲,NPC成功在巡邏點上緩慢地行走嘍!
首先,請大家看看NPC使用的ThirdPersonAnimatorControl。
在Animator視窗點擊Grounded狀態。
從Inspector視窗會看見Motion的部分使用的Blend Tree,請點兩下開啟。
開啟後會看見如下圖的視窗,當紅點在中央下方時,角色的狀態是不會動的Idle狀態。
當紅點調整到最上面,就會發現角色開始跑起來了。
如果將紅點慢慢移到下方,角色的動作又會變得緩慢,像是在走路一樣。
所以,根據以上介紹的機制,透過調整Forward數值,可以控制角色的動作是偏向走路或者跑步。程式碼中在Character.cs中的UpdateAnimator方法,於forwardAmount中乘以一個參數,名為animatorForwardCap,這個參數會設為SerializeField。
修改後的Character.cs如下:
using System; using UnityEngine; using UnityEngine.AI; using RPG.CameraUI; namespace RPG.Character{ [SelectionBase] public class Character : MonoBehaviour { [Header("Audio")] [Range(0.0f, 1.0f)][SerializeField] float spatialBlend = 1f; [Header("Capsule Collider")] [SerializeField] Vector3 colliderCenter = new Vector3(0, 1, 0); [SerializeField] float colliderRadius = 0.3f; [SerializeField] float colliderHeight = 1.5f; [Header("Animator")] [SerializeField] RuntimeAnimatorController animatorController; [SerializeField] AnimatorOverrideController animatorOverrideController; [SerializeField] Avatar avatar; [Range(0.0f, 1.0f)][SerializeField] float animatorForwardCap = 1f; [Header("Movement")] [SerializeField] float moveSpeedMultiplier = 1f; [SerializeField] float animationSpeedMultiplier = 1f; [SerializeField] float movingTurnSpeed = 1800; [SerializeField] float stationaryTurnSpeed = 1800; [SerializeField] float moveThreshold = 1f; [Header("Nav Mesh Agent")] [SerializeField] float steeringSpeed = 1.0f; NavMeshAgent navMeshAgent = null; Animator animator = null; Rigidbody myRigidbody = null; float turnAmount; float forwardAmount; bool isAlive = true; void Awake(){ AddRequiredComponents (); } private void AddRequiredComponents(){ CapsuleCollider capsuleCollider = gameObject.AddComponent(); capsuleCollider.center = colliderCenter; capsuleCollider.radius = colliderRadius; capsuleCollider.height = colliderHeight; myRigidbody = gameObject.AddComponent (); myRigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous; myRigidbody.constraints = RigidbodyConstraints.FreezeRotation; AudioSource audioSource = gameObject.AddComponent (); audioSource.spatialBlend = spatialBlend; animator = gameObject.AddComponent (); animator.runtimeAnimatorController = animatorController; animator.avatar = avatar; navMeshAgent = gameObject.AddComponent (); navMeshAgent.updateRotation = false; navMeshAgent.updatePosition = true; navMeshAgent.speed = steeringSpeed; } void Update(){ if (navMeshAgent.remainingDistance > navMeshAgent.stoppingDistance && isAlive) { Move(navMeshAgent.desiredVelocity); } else { Move (Vector3.zero); } } void Move(Vector3 movement) { SetForwardAndTurn (movement); ApplyExtraTurnRotation(); UpdateAnimator(); } public float GetAnimSpeedMultiplier(){ return animator.speed; } public void Kill(){ isAlive = false; } public void SetDestination(Vector3 worldPos){ navMeshAgent.SetDestination(worldPos); } public void SetStopDistance(float stoppingDistance){ navMeshAgent.stoppingDistance = stoppingDistance; } public AnimatorOverrideController GetOverrideController(){ return animatorOverrideController; } private void SetForwardAndTurn(Vector3 movement){ // 將Wolrd相對的向量轉換成Local相對的向量 if (movement.magnitude > moveThreshold) { movement.Normalize (); } // 將Worldspace的方向轉換成LocalSpace Vector3 localMovement = transform.InverseTransformDirection(movement); turnAmount = Mathf.Atan2(localMovement.x, localMovement.z); forwardAmount = localMovement.z; } void UpdateAnimator() { // update the animator parameters animator.SetFloat("Forward", forwardAmount * animatorForwardCap, 0.1f, Time.deltaTime); animator.SetFloat("Turn", turnAmount, 0.1f, Time.deltaTime); animator.speed = animationSpeedMultiplier; } void ApplyExtraTurnRotation() { // help the character turn faster (this is in addition to root rotation in the animation) float turnSpeed = Mathf.Lerp(stationaryTurnSpeed, movingTurnSpeed, forwardAmount); transform.Rotate(0, turnAmount * turnSpeed * Time.deltaTime, 0); } void OnAnimatorMove(){ if (Time.deltaTime > 0) { Vector3 velocity = (animator.deltaPosition * moveSpeedMultiplier) / Time.deltaTime; // 保持y軸的速度不變 velocity.y = myRigidbody.velocity.y; myRigidbody.velocity = velocity; } } } }
修改好程式碼後,大家可以參考下圖的參數進行Character的設定,我將Animator Forward Cpa設為0.3,Move Speed Multiplier設為0.3,Moving Turn Speed跟Stationary Turn Speed設為100。
留言
張貼留言