处理角色移动

在 Unity 里做角色移动通常有两种方式:

  1. 设置 rigidbody.velocity 刚体速度,通过物理引擎进行位移。
  2. 设置 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参数相当于让碰撞体变小一点进行碰撞检测,然后在最终的位移距离上补偿回来。

搜索引擎于2020年 Animator Controller 与状态机
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×