在 unity3d 中使用刚体的最佳导航代码?

The best navigation code to using rigidbody in unity3d?

我要按一下键

我的立方体向右移动并旋转了 90 度

旋转做得很好

但是动作不太好

using UnityEngine;

using System.Collections;

public class Player : MonoBehaviour
{

    public float speed;

    public float time;

    public GameObject contactPoint;

    private Rigidbody rig;

    private void Start()
    {
        rig = GetComponent<Rigidbody>();
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.D))
        {
            StartCoroutine(RotatePlayer(Vector3.forward * 90, Vector3.right, time));
        }

        if (Input.GetKeyDown(KeyCode.A))
        {
            StartCoroutine(RotatePlayer(Vector3.back * 90, Vector3.left, time));
        }
    }

    private IEnumerator RotatePlayer(Vector3 byAngle, Vector3 dir, float inTime)
    {

        Quaternion fromAngle = contactPoint.transform.rotation;
        Quaternion toAngle = Quaternion.Euler(transform.eulerAngles - byAngle);

        for (float t = 0; t < 1; t += Time.deltaTime / inTime)
        {   
            rig.MovePosition(transform.position + (dir * speed * Time.deltaTime));
            rig.MoveRotation(Quaternion.Slerp(fromAngle, toAngle, t));

            yield return null;
        }
    }
}

你的主要问题是:你应该只在 FixedUpdate.

中做物理相关的事情,比如 Rigidbody 的移动

对于协程,Unity 提供了 WaitForFixedUpdate,它的功能与名称完全相同,确保代码在 FixedUpdate 物理调用中执行。

private IEnumerator RotatePlayer(Vector3 byAngle, Vector3 dir, float inTime)
{
    yield return new WaitForFixedUpdate();

    // here I would also use "rig" instead of "transform"
    Quaternion fromAngle = contactPoint.transform.rotation;
    Quaternion toAngle = Quaternion.Euler(rig.eulerAngles - byAngle);

    for (float t = 0; t < 1; t += Time.deltaTime / inTime)
    {   
        yield return new WaitForFixedUpdate();

        // here I would also use "rig" instead of "transform"
        rig.MovePosition(rig.position + (dir * speed * Time.deltaTime));
        rig.MoveRotation(Quaternion.Slerp(fromAngle, toAngle, t));
    }
}

除此之外,您对 not move well 的定义有点不清楚。您还应该以某种方式确保一次只有一个例程是 运行。通过终止已经 运行 例程,例如

private void Update()
{
    if (Input.GetKeyDown(KeyCode.D))
    {
        StopAllCoroutines();
        StartCoroutine(RotatePlayer(Vector3.forward * 90, Vector3.right, time));
    }

    if (Input.GetKeyDown(KeyCode.A))
    {
        StopAllCoroutines();
        StartCoroutine(RotatePlayer(Vector3.back * 90, Vector3.left, time));
    }
}

或者使用像

这样的标志阻止新例程开始,直到当前例程完成
private bool alreadyRotating;

private IEnumerator RotatePlayer(Vector3 byAngle, Vector3 dir, float inTime)
{
    if(alreadyRotating) yield break;

    alreadyRotating = true;

    ......

    alreadyRotating = false;
}