首页 > 解决方案 > 为什么我的 Unity 挂在一个可能是真的 while 语句上?

问题描述

这是我怀疑会导致问题的代码。MovePath() 仅在 Start() 函数中调用一次。

private void MovePath(Vector2[] path)
{
    foreach (var t in path)
    {
        while (location != t)
        {
            Move(t);
        }
    }
}

private void Move(Vector2 target)
{
    var position = (Vector2) transform.position;
    var direction= target - position;
    direction.Normalize();
    
    transform.right = (target - position) * -1;
    var endPosition = position + (direction * moveSpeed * Time.deltaTime);
    rb.MovePosition(endPosition);
    location = endPosition;
}

我很感兴趣,为什么 Unity 会为我挂起,即使位置t最终可能相等......

标签: c#unity3d

解决方案


为了澄清我的评论,有两个主要问题。首先是该移动不应该在Update、 aCoroutine或任何其他等效项之外进行。这样做的原因是因为正常的函数被调用并运行直到完成。Update并且Coroutines随着时间的推移以小增量运行,这通常是应该如何进行的运动。使用您当前的设置,移动将在一帧中发生。

第二个问题是你的比较。由于 aVector2只是两个浮点数,因此有可能出现浮点值never quit being equal。要解决此问题,您可以将两个Vector2值之间的距离与足够接近 0 的值或所谓的 epsilon 值进行比较,而不是直接比较。在这样做时,您可以确保该值足够接近并且可以终止您的循环。

将所有这些结合到一个答案中,我建议您使用 aCoroutine因为它与您当前的设置相似,只是稍微调整以确保移动发生在多个帧上。我还建议使用Vector2.MoveTowards过电流MovePosition,因为 MovePosition 通常放置在其中,FixedUpdate因为它是基于物理的函数。

[SerializeField] private Vector2[] path;
[SerializeField] private float moveSpeed;
private Coroutine movePath = null;

private void Start()
{
    movePath = StartCoroutine(TraversePath(path));
}

private IEnumerator TraversePath(Vector2[] p)
{
    int pathIdx = 0;
    while (pathIdx < p.Length)
    {
        yield return StartCoroutine(MoveToTarget(p[pathIdx]));
        pathIdx++;
    }

    movePath = null;
}

private IEnumerator MoveToTarget(Vector2 target)
{
    while (!Mathf.Approximately(Vector2.Distance(target, transform.position), 0.0f))
    {
        transform.position = Vector2.MoveTowards(transform.position, target, moveSpeed * Time.deltaTime);
        yield return null;
    }

    transform.position = target;
}

推荐阅读