Coroutines in Command Pattern?

I’m making a turn based RPG where first you set all the actions, commands essentially, for each of your party members first then the battle plays out. However i’m using coroutines as the commands, when i go to create and execute the command nothing happens, but when using a simple command whose return type is void and just prints something to the console it works fine.

Do coroutines not work with the Command Pattern?

Code for the interface

using System.Collections;

public interface ICommand
{
   IEnumerator Execute();
}

Where the command is being created, queued and called

public void SetEnemy(int enemyIndex)
    {
        targetEnemy = enemies[enemyIndex];

        CreateCommand();
        commandBuffer.Dequeue().Execute();
    }

    public void CreateCommand()
    {
        ICommand command;

        if (battleAction == BattleAction.Fight)
        {
            command = new FightCommand(battleLogs, actingCharacter, targetEnemy);
            commandBuffer.Enqueue(command);
        }
    }

The actual command whose return type is IEnumerator

using System.Collections;
using UnityEngine;
using TMPro;

public class FightCommand : ICommand
{
    GameObject[] battleLogs;
    Character character;
    Enemy enemy;

    public FightCommand(GameObject[] battleLogs, Character character, Enemy enemy)
    {
        this.battleLogs = battleLogs;
        this.character = character;
        this.enemy = enemy;
    }

    public IEnumerator Execute()
    {
        battleLogs[0].SetActive(true);
        battleLogs[0].GetComponentInChildren<TextMeshProUGUI>().text = character.characterName;
        yield return new WaitForSeconds(0.5f);

        battleLogs[1].SetActive(true);
        battleLogs[1].GetComponentInChildren<TextMeshProUGUI>().text = enemy.characterName;
        yield return new WaitForSeconds(1.1f);

        bool critical;
        int damage = StatCalculations.CharacterFight(character, enemy, out critical);

        if (critical)
        {
            battleLogs[2].SetActive(true);
            battleLogs[2].GetComponentInChildren<TextMeshProUGUI>().text = "Crit";
            yield return new WaitForSeconds(1);
        }

        int trueDamge = damage - enemy.absorb;
        if (trueDamge <= 0)
            trueDamge = 1;

        battleLogs[3].SetActive(true);
        battleLogs[3].GetComponentInChildren<TextMeshProUGUI>().text = trueDamge.ToString();
        yield return new WaitForSeconds(1);

        enemy.currentHp -= trueDamge;
        if (enemy.currentHp <= 0)
        {
            battleLogs[4].SetActive(true);
            yield return new WaitForSeconds(1);

            enemy.transform.parent.gameObject.SetActive(false);
            //Destroy(enemy.gameObject);
        }
    }
}

This only creates the coroutine… you still gotta give it to someone to run it… someone maybe like… my old buddy StartCoroutine()!

1 Like

If you have more than one command that needs to be executed in sequence, you will also need to wait until the coroutine is complete before starting the next one. You can do this easily using nested coroutines. Here’s a good write-up on it: Nested Coroutines in Unity - Alan Zucconi

1 Like

You should take returned IEnumerator and put it into StartCoroutine. But I think you don’t want to start this coroutine every single Execute call.

In your case it should probably look like this:

public interface ICommand
{
    bool IsExecuting { get; }
    void Start();
}

public class MyCommand : ICommand
{
    private Enemy _enemy;
    public bool IsExecuting { get; private set; }

    public MyCommand(Enemy enemy)
    {
        _enemy = enemy;
    }

    public void Start()
    {
        if (IsExecuting) return;
        _enemy.StartCoroutine(DoSomething());
    }

    private IEnumerator DoSomething()
    {
        IsExecuting = true;

        //Some code

        IsExecuting = false;
    }
}

Heres more: Best way to have queued move actions for up to 10 players executing simultaneously?

Note what is called a nested coroutine in that article is not really a nested coroutine but simply a seperate coroutine and the starting coroutine just waits for the completion of that seperate coroutine. Unity actually supports nested coroutines which are more lightweight. You can directly yield an IEnumerator inside the outer coroutine. This will essentially run / iterate that nested IEnumerator inside the same coroutine. So in essence there’s only one coroutine instance and the IEnumerator instances are just run as nested iterator blocks.

We don’t know exactly how Unity implemented this on the native side, but it’s probably just a stack of IEnumerators. So every time an IEnumerator yields another IEnumerator, that yielded IEnumerator instance will become the new active one, and the outer IEnumerator is simply pushed onto the stack. When the current IEnumerator finishes, it simply pops the last one from the stack to continue the outer coroutine. At least, that’s how I would implement it ^^.

So if the coroutines should run in sequence and not in parallel, running them from inside a coroutine and yielding the actual IEnumerator would be the best solution.

Just for completeness, here’s my coroutine crash course that should cover most questions how coroutines actually work behind the scenes.

1 Like