检测所有骰子何时停止移动

Detect when all dice have stopped moving

这似乎是个简单的问题,但我很难解决。我正在掷一些骰子 (GameObjects) 并尝试检测它们何时都停止移动(这样我就可以计算分数)。

这是我尝试过的方法:

public class GameManager : MonoBehaviour
{
    public GameObject[] _dice;

    public Vector3 _rollStartPosition;
    public float _rollForce;
    public float _rollTorque;

    bool anyDieIsMoving = false;

    void FixedUpdate()
    {
        if (!anyDieIsMoving && Input.GetMouseButtonDown(0))
            RollDice();
    }

    void RollDice()
    {
        foreach (var die in _dice)
        {
            // Roll() adds force and torque from a given starting position
            die.GetComponent<Die>()
               .Roll(_rollStartPosition, Random.onUnitSphere * _rollForce, Random.onUnitSphere * _rollTorque);
        }

        StartCoroutine(CheckIfDiceAreMoving());

        // Calculate score and do something with it...
    }

    IEnumerator CheckIfDiceAreMoving()
    {
        foreach (var die in _dice)
        {
            var dieRigidbody = die.GetComponent<Rigidbody>();
            if (!dieRigidbody.IsSleeping())
            {
                anyDieIsMoving = true;
                yield return null;
            }
        }
    }
}

上面代码的问题在于它会在所有骰子停止移动之前立即尝试计算分数(我通过添加一堆 Debug.Log() 语句发现了这一点)。

怎样才能等到所有的骰子都停止移动后再计算分数?

您必须使 RollDice 成为一个协程函数,然后您可以让出或等待 CheckIfDiceAreMoving 函数变为 return 和 yield return。更好的是,将 if (!dieRigidbody.IsSleeping()) 变成 while (!dieRigidbody.IsSleeping()) 这样 CheckIfDiceAreMoving 功能将不会退出,直到所有骰子停止移动。此外,检查 Update 函数中的输入而不是 FixedUpdate 用于移动 Rigidbody.

这是重构代码:

public class GameManager : MonoBehaviour
{
    public GameObject[] _dice;

    public Vector3 _rollStartPosition;
    public float _rollForce;
    public float _rollTorque;
    bool doneRolling = true;

    void Update()
    {
        if (doneRolling && Input.GetMouseButtonDown(0))
        {
            StartCoroutine(RollDice());
        }
    }

    IEnumerator RollDice()
    {
        doneRolling = false;

        foreach (var die in _dice)
        {
            // Roll() adds force and torque from a given starting position
            die.GetComponent<Die>()
               .Roll(_rollStartPosition, Random.onUnitSphere * _rollForce, Random.onUnitSphere * _rollTorque);
        }

        //Wait until all dice tops moving
        yield return CheckIfDiceAreMoving();


        // Calculate score and do something with it...


        //Set doneRolling to true so that we call this funtion again
        doneRolling = true;
    }

    IEnumerator CheckIfDiceAreMoving()
    {
        foreach (var die in _dice)
        {
            var dieRigidbody = die.GetComponent<Rigidbody>();
            //Wait until all dice stops moving
            while (!dieRigidbody.IsSleeping())
            {
                yield return null;
            }
        }
    }
}

RollDice() 中,它在获取 anyDieIsMoving 状态之前计算分数。我想你展示了将计算分数移动到协程。

void RollDice()
{
    foreach (var die in _dice)
    {
        // Roll() adds force and torque from a given starting position
        die.GetComponent<Die>()
           .Roll(_rollStartPosition, Random.onUnitSphere * _rollForce, Random.onUnitSphere * _rollTorque);
    }

    StartCoroutine(CheckIfDiceAreMoving());

}

IEnumerator CheckIfDiceAreMoving()
{
    while(anyDieIsMoving){
        anyDieIsMoving = false;
        foreach (var die in _dice)
        {
            var dieRigidbody = die.GetComponent<Rigidbody>();
            if (!dieRigidbody.IsSleeping())
            {
                anyDieIsMoving = true;
                yield return null;
            }
        }
    }

    // Calculate score and do something with it...
}

除了其他两个答案所说的,我会这样改变CheckIfDiceAreMoving()(使用siusiulala的版本):

while(anyDieIsMoving){
    anyDieIsMoving = false;
    foreach (var die in _dice)
    {
        var dieRigidbody = die.GetComponent<Rigidbody>();
        if (!dieRigidbody.IsSleeping())
        {
            anyDieIsMoving = true;
            break;
        }
    }
    yield return null;
}

一旦一个骰子不动,就跳出 for 循环,因为它不再相关。原始代码将 for 循环 置于暂停状态,然后将 returns 置于 相同索引 处。毕竟,我们想要协程的是 while 循环