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