- Home /
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
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 :)
Answer by Bunny83 · Feb 24 at 10:46 AM
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.
Your answer
Follow this Question
Related Questions
2D Animation does not start 1 Answer
How do I fix yield not working in while loop? 1 Answer
Increase variable for a few seconds 2 Answers
Invincibility Power Up 1 Answer