
在 Unity 里做角色移动通常有两种方式:
- 设置 
rigidbody.velocity 刚体速度,通过物理引擎进行位移。 
- 设置 
transform.position 来改变物体位置。 
使用第1种方式通过刚体来运动的话,可以不用自行处理碰撞检测,但是对角色移动不容易做到精确控制。我倾向于使用第2种方式,直接修改 transform.position 来进行位移。这样的话,我们就需要处理碰撞检测的问题。
我目前采用的移动逻辑是这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
   | [RequireComponent(typeof(CapsuleCollider))] class CharacterMove : MonoBehaviour {     float radius;     float thickness = 0.05f;     Vector3 localPoint1;     Vector3 Point1 => transform.TransformPoint(localPoint1);     Vector3 localPoint2;     Vector3 Point2 => transform.TransformPoint(localPoint2);     LayerMask layerMask;
      void Awake()     {         var capsuleCollider = GetComponent<CapsuleCollider>();         radius = capsuleCollider.radius;         float halfHeight = capsuleCollider.height * 0.5f;         localPoint1 = capsuleCollider.center + (halfHeight - radius) * Vector3.up;         localPoint2 = capsuleCollider.center + (halfHeight - radius) * Vector3.down;         layerMask = LayerManager.playerMoveCastMask;
          var state = GetComponent<Animator>().GetBehaviour<Character_State_Wander>();         state.SetCharacterMove(this);     }
      public void Move(Vector3 direciton, float distance)     {         while (distance > 0)         {             if (direciton == Vector3.zero)             {                 break;             }
              direciton = direciton.normalized;             bool hit = Physics.CapsuleCast(Point1, Point2, radius - thickness, direciton, out RaycastHit hitInfo, distance + thickness, layerMask);             if (hit)             {                 float actualDis = hitInfo.distance - thickness;                 transform.position += direciton * actualDis;                 Vector3 remain = (distance - actualDis) * direciton;                 Vector3 newMotion = Vector3.ProjectOnPlane(remain, hitInfo.normal);                 if (newMotion.sqrMagnitude < Mathf.Epsilon)                 {                     distance = 0;                 }                 else                 {                     direciton = newMotion.normalized;                     distance = newMotion.magnitude;                 }             }             else             {                 transform.position += direciton * distance;                 distance = 0;             }         }     }     public void Move(Vector3 motion)     {         if (motion == Vector3.zero)         {             return;         }         Move(motion.normalized, motion.magnitude);     } }
   | 
 
这个移动方式的特点是,在玩家碰撞到障碍物时,可以按原有移动向量在障碍物表面上的分量继续移动,不会卡住。

如果不使用  thickness 参数的话,在角色贴合碰撞体的时候,经常会出现检测不到碰撞的情况,也就会造成穿墙。使用 Thickness参数相当于让碰撞体变小一点进行碰撞检测,然后在最终的位移距离上补偿回来。