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