Different execution rates in IEnumerator

Hello everyone!

I’m working on a 2D game in which the main mechanic is programming robots through a drag & drop block interface. I’m stuck in a problem and I would ask for your help.

Let’s say that I have these two behaviours
193055-3-block.png

193056-5-block.png

And the code which executes the behaviours is this one:

        public IEnumerator ExecuteBehavior() {
            // Execute the first block
            BehaviourBlock currentBlock = GetBlock();

            //Debug.Log("Execute -> " + currentBlock.GetBlockLocation().ToString());

            BlockLocation nextBlockLocation = currentBlock.Execute(); 

            yield return null;

            // Now, we go through each block in the behaviour until we reach the end of it
            while(nextBlockLocation.GetIndex() <= this.maxIndex) {
                yield return null;

                // Get the next block
                currentBlock = GetBlock(nextBlockLocation.GetIndex(), nextBlockLocation.GetIndentation());

                //Debug.Log("Execute -> " + currentBlock.GetBlockLocation().ToString());

                if(currentBlock != null) {   
                    // Execute the block and get the next block location
                    nextBlockLocation = currentBlock.Execute();
                }
            }
        }

As you can see, I’m processing one block per frame. And this is causing that in the first case, the robot is executing the MOVE block once per 3 frames, and in the second one once per 5 frames. This is causing the robot’s speed to differ in the two cases.

I can’t manage to figure out a solution that will make the two speeds the same.

Thank you for your time :slight_smile:

You should not yield after every step in your own statemachine processing. You should yield at actual actions that are relevant depending on what “block” was executed. So the best solution would be to allow each block to define if it requires a delay and provide a way for a block to provide a yield value itself.

Note: if you don’t yield inside a pure loop, and the player creates an infinite loop, your game could get stuck and soft-lock / hang since such a “programming” error could always happen. So inside your coroutine it’s always a good idea to have a “max iteration count without yielding”. So just have a counter that counts up and that is reset to 0 whenever you hit a block that does its own yield request. If that counter hits a threshold (say 5000), you may stop the coroutine with an error telling the user the program was stuck.

You may auto-yield one frame at the end of a loop instruction which would probably solve those issues as well. However usually such games would have certain commands / instructions which requires a longer delay. If you think about the computercraft mod for minecraft (in case you know it), which allows robots (turtles) to move around in the world. However a movement takes some time and is essentially an atomic operation in a sense. So the program should only continue once the smooth movement from one block to the next has completed.

Technically computercraft is completely event based which is a great approach as well. CC uses lua coroutines to handle all this logic and a movement instruction actually just queues a movement request in the actual game and the lua program waits for a certain event / message to be returned before continuing. It also requires the lua program to yield regularily, otherwise it is terminated. Though lua coroutines are much nicer than C# / Unity coroutines since they allow yielding from nested methods. Though this is kinda irrelevant here.

So I would suggest that instructions like MOVE, FLIP and maybe GOTO and JUMP issue a yield on their own while all your conditional instructions do not yield at all and you just go to the next block.