【问题标题】:Dynamic Coroutine name动态协程名称
【发布时间】:2017-12-07 11:35:08
【问题描述】:

如何使 Coroutine 的名称动态化? 我用它来让目标在几秒钟后自动死亡:

void InitiateKill(int i)
{
   //i is the number of the target
   StartCoroutine(TargetDie(i, timeAlive/1000));
   //some other stuff
}

当目标在此计时器结束之前被杀死时,我显然会出错,因为它无法再次杀死目标。

这就是为什么我想停止那个特定目标的协程,但我不知道怎么做。

我试过了:

Coroutine b[i] = StartCoroutine(TargetDie(i, timeAlive/1000));

但这会产生语法错误。 b[i] 不能用于协程。

如何以正确的方式做到这一点?

更新:

这是我的 TargetDie 函数的(相关部分):

IEnumerator TargetDie(int i, float delayTime)
{
    yield return new WaitForSeconds(delayTime);
    Destroy(targets[i]);
}

当玩家杀死目标时,我会这样做:

void Damage(int i)
{
   // at this time, the first Coroutine, started in InitiateKill, should stop, because otherwise it tries to destroy the target twice
   StartCoroutine(TargetDie(i, 0));
}

【问题讨论】:

  • 您是否将目标的编号存储在某处?如果您希望它自动停止,您可以在 TargetDie 函数本身中执行此操作。值得解释一下这个函数在做什么,什么时候应该死/停止运行
  • 是的,顾名思义:target0, target1, ... 这与协程部分有什么关系?
  • @Programmer:我已经更新了我的问题
  • 因为首先您应该在目标上运行协程,以便在它死亡时自动停止。否则,您需要从目标获取一些参考,以便目标可以传播它死亡的事实。或者如果项目已死,一些循环会破坏协程。
  • 我了解如何在目标上运行协程。但是我的所有代码,包括 StartCoroutines,都在一个不同的、名为 Controller 的空游戏对象上。

标签: c# unity3d coroutine


【解决方案1】:

最简单的方法,将协程移动到对象本身上。

public class DieScript: MonoBehaviour
{
    private Manager manager;
    public void StartDeathProcess(Manager manager)
    {
        this.manager = manager;
        StartCoroutine(DieAsync(manager));
    }
    private IEnumerator DieAsync(Manager manager)
    {
        yield return new WaitForSeconds(timer);
        Destroy(this.gameObject);
    }
    public void Dmaage() // This is register as listener for death of the object
    {
         this.manager.PropagateDeath(this);
         Destroy(this.gameObject);
    }
}

public class Manager:MonoBehaviour
{
    private List<DieScript> die;
    void InitiateKill(int i)  
    {
        die[i].StartDeathProcess(this);
    }
}

试图将控制权保留在控制器上将会带来比解决方案更多的痛苦。

您可以拥有一个 IEnumerator 列表来跟踪正在运行的协程。但是你仍然需要来自对象的消息来通知控制器它已经死了。所以你错过了这部分。 您需要在目标上设置一个脚本,以便控制器知道这种类型。 控制器运行协程并引用该脚本,询问每一帧你是否正在死亡。当目标要死时,它会设置一个布尔值来通知。使用 Destroy 将对象保留到帧结束,这样它就会起作用。

但这注定以后会失败。让控制器做所有事情有点违反编程概念。您应该将其更多地视为信息的旁路。

【讨论】:

    【解决方案2】:

    您可以在销毁它之前简单地检查 null,这样您就不会收到任何错误:

    if (targets != null)
        Destroy(targets[i]);
    

    但是如果你想停止旧的协程,下面是推荐的方式。


    您可以使用Dictionary 来处理它。使用int i 作为键,Coroutine 作为值。

    调用 InitiateKill 时,将 int i 添加到字典中,当您启动协程时,将 Coroutine 也添加为字典中的值。

    当调用Damage 时,检查Dictionary 中是否存在该int 值。如果是,则使用它来检索 Coroutine 值并停止旧的协程。如果它没有退出,则启动一个新的协程,然后将其添加到该字典中。

    字典应该是这样的:

    Dictionary<int, Coroutine> dyingTarget = new Dictionary<int, Coroutine>();
    

    添加到Dictionary 的新InitiateKill 函数:

    void InitiateKill(int i)
    {
        //i is the number of the target
        Coroutine crt = StartCoroutine(TargetDie(i, timeAlive / 1000));
    
        //Add to Dictionary
        dyingTarget.Add(i, crt);
    }
    

    您的新 Damage 函数现在检查该项目是否已在字典中,然后重试它,停止协程并将其从 Dictionary 中删除。

    void Damage(int i)
    {
        // at this time, the first Coroutine, started in InitiateKill, should stop, because otherwise it tries to destroy the target twice
        StopIfAlreadyRunning(i);
    
        Coroutine crt = StartCoroutine(TargetDie(i, 0));
        //Add to Dictionary
        dyingTarget.Add(i, crt);
    }
    
    void StopIfAlreadyRunning(int i)
    {
        Coroutine crtOut;
        //Retrieve and stop old coroutine if it exist then removes it
        if (dyingTarget.TryGetValue(i, out crtOut))
        {
            StopCoroutine(crtOut);
            dyingTarget.Remove(i);
        }
    }
    

    新的TargetDie 函数在杀死Dictionary 后将其从Dictionary 中删除。它还会在销毁 null 之前检查它:

    IEnumerator TargetDie(int i, float delayTime)
    {
        yield return new WaitForSeconds(delayTime);
        if (targets != null)
            Destroy(targets[i]);
        //Remove from Dictionary 
        dyingTarget.Remove(i);
    }
    

    【讨论】:

    • 这太棒了,感谢您的回答和解释!
    • 很高兴我的回答对您有帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-11
    • 1970-01-01
    • 2010-09-06
    • 1970-01-01
    • 1970-01-01
    • 2022-11-01
    • 1970-01-01
    相关资源
    最近更新 更多