在unity的標准資源包中,包含了一個叫做 Third Person Controller的東西,這個東西是一個unity實現的角色控制器,下面來研究一下它是怎么實現的。
這個控制系統,主要由以下幾部分構成:
- Third Person User Control
- Third Person Character
- 攝像機控制腳本
下面一一講解這些部分:
首先 Third Person User Control 和 Third Person Character 都是掛到主角身上的,同時主角身上還應該有 Animator組件和 Rigidbody組件,類似於下圖:
Third Person User Control
這個腳本主要是用於檢測用戶輸入,然后將用戶輸入轉化為具體的行為數據,傳遞給Third Person Character使用。
private void FixedUpdate() { // read inputs float h = CrossPlatformInputManager.GetAxis("Horizontal");//獲取水平輸入 float v = CrossPlatformInputManager.GetAxis("Vertical");//獲取 垂直輸入 bool crouch = Input.GetKey(KeyCode.C); //獲取C鍵輸入 // calculate move direction to pass to character if (m_Cam != null)//如果當前場景主攝像機不為空 { // calculate camera relative direction to move: m_CamForward = Vector3.Scale(m_Cam.forward, new Vector3(1, 0, 1)).normalized;//獲取當前攝像機的 forward 方向,並且將其y值設為0,然后歸一化 這個變量 m_Move = v*m_CamForward + h*m_Cam.right;//移動方向為:垂直方向輸入* 攝像機前方向 + 水平輸入*攝像機右方向 } else//如果主攝像機為空 { //移動方向為 世界坐標軸前方* 垂直輸入 + 直接坐標軸右方*水平輸入 m_Move = v*Vector3.forward + h*Vector3.right; } #if !MOBILE_INPUT // walk speed multiplier if (Input.GetKey(KeyCode.LeftShift)) m_Move *= 0.5f; #endif //傳遞給 Third Person Character m_Character.Move(m_Move, crouch, m_Jump); m_Jump = false; }
這個腳本很簡單,唯一有價值的地方就是它對於前進方向的計算,它以攝像機為基准來進行方向計算,這一點可以借鑒。
Third Person Character
這個腳本稍微要復雜一些,它會涉及到 角色的移動、動畫的播放等。
首先來看看它的Move方法,它用於控制 角色的移動:
1 public void Move(Vector3 move, bool crouch, bool jump) 2 { 3 4 // convert the world relative moveInput vector into a local-relative 5 // turn amount and forward amount required to head in the desired 6 // direction. 7 8 //如果輸入的移動方向沒有歸一化,那么先將其歸一化。 9 if (move.magnitude > 1f) move.Normalize(); 10 11 /* transform.InverseTransformDirection(Vector3 dir) 12 * 將 世界坐標系下的向量 dir 轉化為 transform 自身坐標系下的向量 13 */ 14 move = transform.InverseTransformDirection(move); 15 16 //檢測是否處於地面 17 CheckGroundStatus(); 18 19 /* Vector3.ProjectOnPlane(Vector3 dir , Vector3 Normal) 20 * Normal:垂直於一個平面A 的向量,也叫作平面A 的法線 21 * 這個API的意思是,將 向量dir 投影到 法線Normal垂直的平面上, 22 * 在這里也就是將 移動向量投影到 地面 23 */ 24 move = Vector3.ProjectOnPlane(move, m_GroundNormal); 25 26 //計算旋轉角度,這里計算的是 move向量與 z軸正方向的夾角 27 m_TurnAmount = Mathf.Atan2(move.x, move.z); 28 m_ForwardAmount = move.z; 29 30 //實現平滑加速的轉向動畫 31 ApplyExtraTurnRotation(); 32 33 // control and velocity handling is different when grounded and airborne: 34 //當在地上時 35 if (m_IsGrounded) 36 { 37 HandleGroundedMovement(crouch, jump); 38 } 39 else 40 { 41 HandleAirborneMovement(); 42 } 43 44 //處理蹲下時縮放膠囊體高度 45 ScaleCapsuleForCrouching(crouch); 46 47 PreventStandingInLowHeadroom(); 48 49 // send input and other state parameters to the animator 50 //改變動畫播放 51 UpdateAnimator(move); 52 }
這個過程參見上圖: 經過 controller 計算后,得到的 方向向量 為 S,然后傳遞給 Character 的move方法,move方法接收到S向量后,
- 將S向量由世界坐標系轉化到了自身坐標系,變為向量 OD
- 將向量OD 投影到地面上,獲得向量 OA
- 計算向量OA與 人物自身坐標系Z軸 的夾角 ∠ZOA
- 計算向量OA在 人物自身坐標系Z軸 的分量 z
最終,計算到的角度值,會使用 transform.Rotate(float x,flaot y ,float z)方法進行旋轉。
而移動,則會交給Animator來控制,因為我們是使用的動畫控制移動。
接下來看看下一個方法:ApplyExtraTurnRotation()
1 void ApplyExtraTurnRotation() 2 { 3 // help the character turn faster (this is in addition to root rotation in the animation) 4 //由 180線性插值到 360,插值幅度為 前進分量在z軸上的分量,這里也就意味着,轉向越大,那么轉身速度增加得越慢 5 float turnSpeed = Mathf.Lerp(m_StationaryTurnSpeed, m_MovingTurnSpeed, m_ForwardAmount); 6 7 //m_TurnAmount =需要旋轉的角度 8 transform.Rotate(0, m_TurnAmount * turnSpeed * Time.deltaTime, 0); 9 }
至於最后一個的相機跟隨,可以做簡單的,可以做復雜的,這個可以使用 cinimatic camera插件