【问题标题】:How to make a coroutine wait on a second coroutine to finish before resuming? [duplicate]如何让协程在恢复之前等待第二个协程完成? [复制]
【发布时间】:2020-11-07 10:41:55
【问题描述】:

我正在 Unity 中制作足球模拟游戏,并且我有一个基本的 Player_Agent 类来处理非智能动作,如跑步、跳跃等。我有一个协程 Dive(Vector2 a_direction),代理应该旋转以面对 a_direction然后向前移动,直到它的高度回到它的起始高度。但是,每当我执行这个协程时,它都会旋转到面向 a_direction 的方向,然后完全退出 Dive 协程。

我发现的每篇文章都建议按照我的实现方式进行操作,使用yield return StartCoroutine(TurnTo(Vector2 a_direction));,如果我将其更改为StartCoroutine(TurnTo(a_direction)); yield return null;,那么它将同时执行两个例程,这不是预期的结果。我用 StartRoutine() 和没有它都试过了,有点迷路了。我没有尝试正确实现这一点,还是只是语法错误?

MVP:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player_Agent : MonoBehaviour
{

    // Dynamic Variables
    //--------------------------------------------------

    public float w_speed;
    public float w_height;
    public Vector2 w_direction;

    // Member Variables
    //--------------------------------------------------

    /// <summary></summary>
    public float m_speed; // Set to 5 in unity editor
    public float m_acceleration; // Set to 1 in unity editor
    public float m_vertical; // Set to 1 in unity editor

    public float m_height; // Set to 2 in unity editor

    // --------------------------------------------------
    //  GAMEPLAY METHODS
    // --------------------------------------------------
    #region Gameplay Methods


    /// <summary>
    /// Turns the agent towards the target direction
    /// </summary>
    /// <param name="a_direction">A unit vector representing the desired result for w_direction </param>
    /// <returns>A coroutine of the agent turning </returns>
    public IEnumerator TurnTo(Vector2 a_direction)
    {
        // Initialize helper variables
        Vector2 t_direction_0 = w_direction;
        float t_time = Time.deltaTime;
        float t_angle = Mathf.Atan2(a_direction.y, a_direction.x) * Mathf.Rad2Deg;
        Quaternion t_quat_0 = transform.rotation;
        Quaternion t_quat_1 = Quaternion.AngleAxis(t_angle, Vector3.forward);

        // While not facing the correct direction
        while (w_direction != a_direction)
        {
            // Use slerp to dynamically rotate and change w_direction
            Quaternion t_quat = Quaternion.Slerp(t_quat_0,t_quat_1,t_time);
            transform.rotation = t_quat;
            w_direction = (t_quat * t_direction_0).normalized;

            yield return null;

            // Update time elapsed
            t_time += Time.deltaTime;
        }

        // Ensure that there is no error
        w_direction = a_direction.normalized;

    }

    /// <summary>
    /// Turns the agent towards the target direction
    /// </summary>
    /// <param name="a_angle">A float between 0 and 360 representing the turning angle </param>
    /// <returns>A coroutine of the agent turning </returns>
    public IEnumerator TurnTo(float a_angle)
    {
        // Initialize helper variables
        Vector2 t_direction_0 = w_direction;
        float t_time = Time.deltaTime;
        Quaternion t_quat_0 = transform.rotation;
        Quaternion t_quat_1 = t_quat_0 * Quaternion.AngleAxis(a_angle, Vector3.forward);

        Debug.Log("Turning");

        // While not facing the correct direction
        while (transform.rotation != t_quat_1)
        {
            // Use slerp to dynamically rotate and change w_direction
            Quaternion t_quat = Quaternion.Slerp(t_quat_0, t_quat_1, t_time);
            transform.rotation = t_quat;
            w_direction = (t_quat * t_direction_0).normalized;

            yield return null;

            // Update time elapsed
            t_time += Time.deltaTime;
        }

    }

    public IEnumerator Dive(Vector2 a_direction)
    {
        // Initialize helping variables
        float t_height = m_height + m_vertical * Time.deltaTime;
        float t_time = Time.deltaTime;
        Vector2 t_position_0 = (Vector2)transform.position;
        Vector2 t_position = new Vector2(t_position_0.x, t_position_0.y);
        bool hasJumped = false;
        Coroutine t_coroutine;

        // If not facing a_direction
        if (w_direction != a_direction)
        {
            // Turn towards a_direction
            yield return StartCoroutine(TurnTo(a_direction));
        }

        
        // While the agent hasn't jumped and is stil in the air
        while ((t_height - m_height) > 0.01f || hasJumped == false)
        {
            // Update w_height and position
            w_height = t_height;
            transform.position = t_position;

            yield return null;

            // Update t_time and t_height
            t_time += Time.deltaTime;
            t_height = m_height + 2.5f * m_vertical * 0.7071f * t_time - 4.9f * Mathf.Pow(t_time, 2);
            t_position = t_position_0 + (m_vertical + w_speed) * t_time * a_direction;
            Debug.Log(t_height);
            // Flip hasJumped flag
            if (!hasJumped) { hasJumped = true; }
        }

        // Reset w_height
        w_height = m_height;
    }

    #endregion


}

注意:显然不是完成或完善的设置,但这是我在其他几个计划方法中出现的模式,我想确保在我这样做之前我正在实施它。

【问题讨论】:

    标签: c# unity3d coroutine


    【解决方案1】:

    有一些方法可以做到。

    1 在第一个协程中调用第二个协程

    IEnumerator routine1(){
        print("routine1: Calling routine2");
        yield return routine2();
        print("routine1: routine2 ended");
    }
    
    IEnumerator routine2(){
        print("routine2: Waiting 1 second");
        yield return new WaitForSeconds(1);
        print("routine2: 1 second passed");
    }
    

    输出

    routine1: Calling routine2
    routine2: Waiting 1 second
    routine2: 1 second passed
    routine1: routine2 ended
    

    2 在不同的时间给他们打电话

    bool routine2ended = false;
    
    IEnumerator routine1(){
        print("routine1: Let's wait routine2");
        yield return new WaitUntil(() => routine2ended);
        print("routine1: routine2 ended");
    }
    
    IEnumerator routine2(){
        print("routine2: Waiting 1 second");
        yield return new WaitForSeconds(1);
        print("routine2: 1 second passed");
        routine2ended = true;
    }
    

    输出

    routine1: Let's wait routine2
    routine2: Waiting 1 second
    routine2: 1 second passed
    routine1: routine2 ended
    

    【讨论】:

    • 其实连内部协程都不需要启动..直接yield返回IEnumeratoryield return routine2();
    • 如果您确定这一点,请随时编辑我的答案。我不在 Unity atm 中,我总是像我写的那样使用顺序协程(使用 yield return StartCoroutine(routine2()))。
    • 两者基本上做同样的事情,是的;)一般来说,yield 返回另一个 IEnumerator(甚至不是 Coroutine 和 Unity 特定的)你基本上只是扩展你的枚举:)
    • 我知道我迟到了一年,但感谢您的精彩回答!
    猜你喜欢
    • 2017-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-30
    • 2020-02-23
    • 2021-01-01
    相关资源
    最近更新 更多