第三人称角色控制器解析

在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插件

原文地址:https://www.cnblogs.com/leiGameDesigner/p/9072168.html