Search Unity

Question How do I start a coroutine in Bolt and wait for it to finish?

Discussion in 'Visual Scripting' started by Jonas-Neuston, Nov 26, 2020.

  1. Jonas-Neuston

    Jonas-Neuston

    Joined:
    Jun 10, 2017
    Posts:
    70
    In a Bolt flow graph, how do I start a coroutine and wait for it to finish?

    I've looked through the Bolt documentation and forum posts but I can't find a good solution.

    Bolt 1.4.12
    Unity 2019.4.5f1
     
  2. Ex-Crow

    Ex-Crow

    Joined:
    Aug 14, 2020
    Posts:
    111
    All green flow starter event nodes like Start, Update, On Enable, etc have a "Coroutine" checkbox in the Graph Inspector.

    They start and end automatically with the checkbox ticked in.

    Nodes like Timer and Cooldown have built in coroutines so they don't need to be placed inside coroutines manually.
     
  3. Jonas-Neuston

    Jonas-Neuston

    Joined:
    Jun 10, 2017
    Posts:
    70
    Thank you for replying @Ex-Crow, but that doesn't really seem to apply to my case. I'll try to describe my situation further:

    Imagine I have a component like this:
    Code (CSharp):
    1. public class CarAttackDirector : MonoBehaviour
    2. {
    3.     public IEnumerator RunOneAttack()
    4.     {
    5.         // Do stuff in here
    6.     }
    7. }

    I then want to be start the RunOneAttack method as a coroutine, from the Bolt flow graph. Later, when the coroutine finishes I want to execute some other logic in the Bolt flow graph.

    Thank you
     
  4. Ex-Crow

    Ex-Crow

    Joined:
    Aug 14, 2020
    Posts:
    111
  5. Jonas-Neuston

    Jonas-Neuston

    Joined:
    Jun 10, 2017
    Posts:
    70
    Okay, thanks for clarifying that. I'll write a custom node.
     
  6. Jonas-Neuston

    Jonas-Neuston

    Joined:
    Jun 10, 2017
    Posts:
    70
    I'm now using the following code to start coroutines from a Bolt flow graph in my upcoming game Arcano:
    Code (CSharp):
    1. using Bolt;
    2. using Ludiq;
    3. using System.Collections;
    4. using UnityEngine;
    5.  
    6. namespace Neuston.Bolt
    7. {
    8.     public class CoroutineRunnerUnit : Unit
    9.     {
    10.         [DoNotSerialize] public ControlInput start;
    11.         [DoNotSerialize] public ValueInput methodName;
    12.         [DoNotSerialize] public ValueInput subject;
    13.         [DoNotSerialize] public ControlOutput complete;
    14.  
    15.         protected override void Definition()
    16.         {
    17.             start = ControlInputCoroutine("start", RunCoroutine);
    18.             methodName = ValueInput<string>("methodName", string.Empty);
    19.             subject = ValueInput<MonoBehaviour>("subject", null);
    20.             complete = ControlOutput("complete");
    21.         }
    22.  
    23.         IEnumerator RunCoroutine(Flow flow)
    24.         {
    25.             var c = flow.GetValue<MonoBehaviour>(subject);
    26.             var meth = flow.GetValue<string>(methodName);
    27.             yield return c.StartCoroutine(meth);
    28.             yield return complete;
    29.         }
    30.     }
    31. }
     
    huulong and merpheus like this.
  7. huulong

    huulong

    Joined:
    Jul 1, 2013
    Posts:
    224
    @Jonas-Neuston Thanks!

    I combined your code with the idea mentioned in Life and Style Media's article linked above, to inherit from WaitUnit (for some reason they didn't do it themselves in the article).

    It just spares defining ControlInputCoroutine and ControlOutput yourself.

    In addition, I switched to using an IEnumerator as returned by a coroutine method, so you must now call the coroutine method first in the graph, then this unit and pass the output IEnumerator of the previous unit to this one as input. Unfortunately, I cannot call StartCoroutine without a script instance so I still needed to pass a MonoBehaviour (in theory could be another instance than the one you called the coroutine method, but if you want to ensure lifetime of subject vs method's "this" object it may be a good idea to pass the same instance).

    The advantage is that you can add a unit calling any coroutine method using the auto-completion, instead of entering the method name as a string.

    Of course, you can keep the method name as string if you prefer. Especially if you need to pass the method name dynamically.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using Unity.VisualScripting;
    4. using UnityEngine;
    5.  
    6. /// Start coroutine and wait until it is over.
    7. [UnitTitle("Run and Wait For Coroutine")]
    8. [UnitShortTitle("Coroutine")]
    9. public class RunAndWaitForCoroutineUnit : WaitUnit
    10. {
    11.     /// The coroutine to start and wait for.
    12.     [DoNotSerialize]
    13.     public ValueInput coroutineEnumerator { get; private set; }
    14.  
    15.     /// The script instance to run the coroutine on
    16.     /// (not necessarily the one where the coroutine method was called)
    17.     [DoNotSerialize]
    18.     public ValueInput scriptInstance { get; private set; }
    19.  
    20.     protected override void Definition()
    21.     {
    22.         base.Definition();
    23.  
    24.         coroutineEnumerator = ValueInput<IEnumerator>(nameof(coroutineEnumerator));
    25.         scriptInstance = ValueInput<MonoBehaviour>(nameof(scriptInstance));
    26.         Requirement(coroutineEnumerator, enter);
    27.         Requirement(scriptInstance, enter);
    28.     }
    29.  
    30.     protected override IEnumerator Await(Flow flow)
    31.     {
    32.         MonoBehaviour scriptInstanceValue = flow.GetValue<MonoBehaviour>(scriptInstance);
    33.         IEnumerator coroutineEnumeratorValue = flow.GetValue<IEnumerator>(this.coroutineEnumerator);
    34.         yield return scriptInstanceValue.StartCoroutine(coroutineEnumeratorValue);
    35.         yield return exit;
    36.     }
    37. }
     
  8. MasterSubby

    MasterSubby

    Joined:
    Sep 5, 2012
    Posts:
    252
    Nice work!

    You may also have interest in our Yield unit that's part of the Community Add-ons. It was only added a few months ago. If anything it'll teach you how to run an IEnumerator in a graph within a single unit by manually looping over the Enumerator. No need for any extra MonoBehaviour Target inputs or anything. It'll run it directly through the flow like other wait units. This requires the event that triggers it to be a Coroutine. I'll be adding an actual Coroutine unit at some point as well. So Coroutines that are made in graph can be stored as variables. This one in particular allows IEnumerator, YieldInstruction, and Coroutine types as inputs.

    The branch this exists on us UVSsupport-Dev branch. It's MIT, so use it as you please

    https://github.com/RealityStop/Bolt.../Runtime/Fundamentals/Units/Time/YieldUnit.cs

    Unity_2021-06-30_13-10-55-2.png