Unity2D中关于rigidbody2d+collider碰撞另一个collider发生的抖动问题


原文: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;
    }
  }
}


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM