第三人稱角色控制器解析


在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向量后,

  1. 將S向量由世界坐標系轉化到了自身坐標系,變為向量 OD
  2. 將向量OD 投影到地面上,獲得向量 OA
  3. 計算向量OA與 人物自身坐標系Z軸 的夾角 ∠ZOA
  4. 計算向量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插件

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM