原文:https://blog.csdn.net/jk_chen_acmer/article/details/106945419
Player增加组件:rigidbody2D+circlecollider2D
树增加组件:boxcollider2D
我发现如果使用常规的方法去更新transform.positon的值,当Player和树发生碰撞时,会抖动,有点儿像抽搐触电的感觉。
这是因为如果我们使用修改transform.position来实现的话,因为unity在第一时间更新了GameObject的position值,而这时,其实rigidbody2D的position还没有被更新。
那么当下一次更行rigidbody2D的position时,组件rigidbody2D久会发现,欸?我怎么在碰撞体里面了??于是会发生一个位移,就是把Player从碰撞体中挤出去。
这个问题的主要矛盾,其实就是游戏对象的position先于rigidbody的检测更新了,导致游戏对象的碰撞体和另一个碰撞体产生了交集。
那么我们可以得出的结论就是,只要让rigidbody的检测发生在position更新之前,就行了。即:我先判断前方有没有碰撞体?有,则position不能更新;无,则更新position
那么我们就不能在代码中直接去更改transform.position的值,且,不能直接更改rigidbody2D.position的值。我们必须通过一个接口,告诉rigidbody,我要移动到这个位置!rigidbody组件收到消息后,他知道,哦,我要移动到这个位置,但是他同时也要兼具一个责任——仿真物理效果(即,不能让collider1和collider2产生交集)。那么rigidbody2D组件内部久会自行运转,他会先判断会不会有collider交集的发生,然后才是移动行为。
如果我们这样去实现,就不会出现有一个物体突然出现在另一个物体的内部,从而被rigidbody给挤出去的情形。你想象一下,如果说现实中,你的手去拿水杯,突然你的手进入了水杯的内部!这河里吗?这布河里!
所以根本的逻辑是,你要移动这个对象,可以!但是不能直接去更改他的position,而是通过物理引擎实现移动,是你告诉物理引擎,我要移动到这里,而不是直接把这个物体瞬移过去。
那么解决方法就简单了,调用Rigidbody2D的接口函数MovePosition
一个例子(这是Player脚本,挂载在Player对象身上):
public class Player : MonoBehaviour
{
// Player的刚体组件
private Rigidbody2D Rigidbody2D;
// Player的移动速度
public float Speed;
// Player的移动协程 它必须保证在进程中唯一存在,不允许同一时间存在多个
private Coroutine MoveCoroutine;
private void Awake()
{
// 数据初始化
Rigidbody2D = GetComponent<Rigidbody2D>();
Speed = 2;
}
private void FixedUpdate()
{
// 如果按下鼠标左键
if (Input.GetMouseButton(0))
{
// 获得一个从屏幕鼠标位置垂直于游戏画面的射线 ray
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit raycastHit;
// 在Plane(地面)上得到一个交点 raycastHit
Physics.Raycast(ray, out raycastHit, 100.0f, 1 << LayerMask.NameToLayer("Plane"));
// 让Player移动到鼠标位置
IMove(new Vector2(raycastHit.point.x, raycastHit.point.y));
}
}
// 移动接口 保证移动协程只能存在一个,如果有第二个移动指令,则立马终止上一个移动协程,开启新的移动协程
private void IMove(Vector2 position)
{
if (MoveCoroutine != null)
{
StopCoroutine(MoveCoroutine);
MoveCoroutine = StartCoroutine(IEMove(position));
}
else
{
MoveCoroutine = StartCoroutine(IEMove(position));
}
}
// 移动协程
private IEnumerator IEMove(Vector2 position)
{
while (Vector2.Distance(Rigidbody2D.position, position) > 0.05f)
{
Vector2 currentPos = Rigidbody2D.position;
Vector2 direction = position - currentPos;
RaycastHit2D raycastHit2D = Physics2D.CircleCast(Rigidbody2D.position, 0.01f, direction, 0.01f, 1 << LayerMask.NameToLayer("Tree"));
if (raycastHit2D.collider != null)
{
break;
}
Vector2 destination = new Vector2(currentPos.x + direction.x * Time.fixedDeltaTime * Speed, currentPos.y + direction.y * Time.fixedDeltaTime * Speed);
Rigidbody2D.MovePosition(destination);
yield return null;
}
}
}