【问题标题】:Coroutine not starting协程未启动
【发布时间】:2017-12-11 12:36:52
【问题描述】:

我正在使用闪电游戏对象预制件在我开火时产生视觉效果。当我开火时,我启用了该闪电对象,并且我有一个通用组件,该组件会在一定延迟后将其停用。

问题在于“应该等待”日志永远不会到达,而不是等待设置的延迟,而是等待更长的时间并且实际上并没有停用游戏对象。

这是 DeactivateAfter 组件的代码

public class DestroyAfter : MonoBehaviour {

    [SerializeField]
    private float delay;
    private bool firstRun = true;

    void OnEnable () {
        if (firstRun == false) {
            StartCoroutine(DestroyMethod());
        }
        firstRun = false;
    }

    public IEnumerator DestroyMethod() {
        Debug.LogFormat("Should wait; Time: {0}", Time.time);
        yield return new WaitForSeconds(delay);
        Debug.LogFormat("Should deactivate; Time: {0}", Time.time);
        gameObject.SetActive(false);
    }
}

【问题讨论】:

    标签: unity3d yield coroutine


    【解决方案1】:

    条件永远不会为真,你需要先将 firstRun 条件设置为真。

    private bool firstRun = **true**;
    
    void OnEnable () {
        if (firstRun == **true**) {
            StartCoroutine(DestroyMethod());
        }
        firstRun = false;
    }
    

    我喜欢先设置标志,然后做你想做的事:

    private bool firstRun = **true**;
    
    void OnEnable () {
        if (firstRun == **true**) {
            firstRun = false;
            StartCoroutine(DestroyMethod());
        }    
    }
    

    【讨论】:

    • 我多次启用此组件,因此第一次激活后条件确实为真。我不想在第一次激活时启动协程,这就是 if 子句的目的。
    • 你也需要初始化延迟变量
    • 如果在第一次启用时设置为 false,则第一次不执行协程。需要启用 2 次才能按照您的脚本运行协程
    • 是的,这就是我向您解释的内容。我希望它在我第一次启用它之后启动。延迟变量是从检查器中设置的。
    • 如果是私有的,你如何从检查器设置延迟变量?
    【解决方案2】:

    我认为你应该为你的武器开火使用粒子系统。无论如何,我认为您的代码不起作用,因为您正在停用游戏对象而不是停用组件。使用以下方式激活和停用您的组件:

    gameObject.GetComponent<YourComponent>().enabled = false;

    【讨论】:

    • 火焰效果是商店的资产,不能过多地修改它的实现方式。我作为一个游戏对象有这种效果,所以我宁愿禁用那个游戏对象。
    【解决方案3】:

    第一个解决方案 - 延迟实例化和销毁

    如果我理解正确的话,闪电游戏对象已经是你在触发时实例化的预制件,并且你想在延迟后销毁它。

    因此,与其使用协程解决方案并启用或禁用对象,不如在实际调用脚本后使用实例化和销毁更简单:

    GameObject obj = GameObject.Instantiate(yourPrefab, position, rotation);    
    GameObject.Destroy(obj, delay);
    

    只要您将yourGameObject 提供给调用类,就可以从任何地方调用此函数。所以你甚至不需要一个自己的类。

    第二个浮点参数是以秒为单位的延迟(参见API

    例如,你可以有一个射击类:

    public class Shooting : MonoBehavior 
    {
    
        [SerializeField]
        private GameObject lightningPrefab;
    
        [SerializeField]
        private float delay;
    
        public void OnShooting(Vector3 position, Quaternion rotation){
            GameObject obj = GameObject.Instantiate(lightningPrefab, position, rotation);
    
            /* Directly after instantiating the Object
             * you can already "mark" it to be destroyed after x seconds 
             * so you don't have to store it as variable anywhere */
            GameObject.Destroy(obj, delay); 
        }
    }
    

    然后您还可以添加一些检查以不经常生成预制件:

    [...]
    
    private GameObject lightningObject;
    
    public void OnShooting(Vector3 position, Quaternion rotation){
        if(lightningObject == null ){
            lightningObject = GameObject.Instantiate(lightningPrefab, position, rotation);            
    
            GameObject.Destroy(lightningObject, delay);
        }
    }
    

    这样一来,您的闪电对象一次总是只有一个。


    第二种解决方案 - 超时

    另一种不必一直实例化和销毁对象的方法是简单的超时而不是协程:

    public class Shooting : MonoBehavior 
    {
        [Tooltip("The lightning Object")]
        [SerializeField]
        private GameObject lightningObject;
    
        [Tooltip("The time in sec the lightningObject stays visible")]
        [SerializeField]
        private float visibleDelay;
    
        /* track the time that passed since the last OnShooting call */
        private float timePassed;
    
        /* Additional bool to not perform Update when not needed */
        private bool isVisible;
    
        /* Update is called once each frame */
        private void Update(){
            /* If the object is not visible do nothing */
            if (isVisible != true) return;
    
            /* update the timePassed */
            timePassed += Time.deltaTime;
    
            /* if delay passed since last OnShooting call 
             * deactiavte the object */
            if (timePassed >= visibleDelay){
                lightningObject.SetActive(false);
            }
        }
    
        public void OnShooting(){
            /* Activate the Object */
            lightningObject.SetActive(true);
    
            /* set isVisible */
            isVisible = true;            
    
            /* reset the timePassed */
            timePassed = 0;
        }
    }
    

    【讨论】:

    • 不可行,因为我专门为这个闪电游戏对象使用了一个对象池,以避免每次我想要一个出现时实例化和销毁它。
    • 我添加了一个更简单的解决方案,无需实例化/销毁;)
    • 我目前不再从事该项目,但我会给你一个赞成票,因为你的解决方案看起来确实是正确的。
    【解决方案4】:

    嗯。您的代码在我的项目中运行良好。

    我在做什么,一些对象有你的脚本,我在场景播放时停用然后激活游戏对象。

    您的销毁方法是公开的。 检查你的项目,可能在某个地方调用了 StopAllCoroutines() 或 StopCoroutine(DestroyMethod());

    【讨论】:

      猜你喜欢
      • 2020-01-20
      • 2023-03-22
      • 2020-04-12
      • 1970-01-01
      • 1970-01-01
      • 2021-12-28
      • 2020-07-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多