首页 > 解决方案 > 在 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;
        }
    }
}

标签: unity3drotationpositionmovephysics

解决方案


您的主要问题是:您应该做与物理相关的事情,例如Rigidbody仅在FixedUpdate.

对于 Coroutines,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;
}

推荐阅读