Coroutine are making my code architecture miserable

Hi all.

I’m currently penning out a spell system and I’m running into an issue: A lot of my spells have animations and camera movements that need to wait on the previous to be in place before starting.

Example:

-> Move camera 
    -> Once that's done start doing a part of the spell 
        -> Now trigger an event 
            -> Then pan camera to another unit 
                -> Have them do something
                    -> Once they're done doing something, pan camera again
ect ect. 

And I’m planning on making a lot of different and unique spells. All of them share the same basic camera movements - just not always in the same order, or same event triggers/data changes. The coroutine ladder is making it difficult to organize a good modular system with re-usuable functions. As it stands, for each spell I’ll have to re-write the entire ladder start to finish, each calling up slightly different functions inside the script. What’s a good setup to avoid making this giant mess?

The current solution I’m thinking of is to make my spells individual scripts, and have their calls in the update function. Then I can use bools to trigger different functions instead of coroutine. Re-usuable functions are stored in some master-list function. This sounds like it would work but feels very hacky. Additionally I don’t think I can serialize a list of different scripts, so having easy unit creation using SO’s where I can just drag and drop the different spells they can use would go out the window.

We don’t know how you actually chain your coroutines. Do you actually nest them? or do you call them in sequence? Keep in mind you can wait for a coroutine’s completion inside another coroutine

yield return StartCoroutine(SomeOtherCoroutine());
// this is executed when "SomeOtherCoroutine" is completed

Also keep in mind that you can pass parameters to coroutines. So a single “spell” coroutine could look like this:

IEnumerator Spell1()
{
    yield return StartCoroutine(MoveCamera(Camera.main, target));
    yield return StartCoroutine(PlaySpellAnimationPart1());
    // do something in between
    yield return StartCoroutine(PanCameraTo(Camera.main, otherTarget));
    // ....
}

How you design your coroutines is up to you. Though there are other ways to implement such logic. You could also use the Mecanim statemachine system. It can execute callbacks in your scripts while also driving the animations. If the spells are highly dynamic (might be interruptable) a state machine may be the better approach.