using goto for game state machine

Pages: 12
I know that everyone suggest that you should avoid using goto, but I don't care if the code is hard to read. Unless it is somehow slow to use goto or something like that then am ok with that.

The reason I want to use goto for example.

1
2
3
4
5
6
7
if(onGround){
	if(jump) {onGround = false; goto air;}
	//ground movement code here
}else{
	air:
	//air movement code here
}


Just want to know what do you think about this?
Hello darlock,

You may have learned to use the {}s the way that you did and that is fine, but for me it is hard to read and follow.

Take a look at your code in a different format:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (onGround)
{
    if (jump)
    {
        onGround = false;

        goto air;
    }

    //ground movement code here
}
else
{
air:
    //air movement code here
}

The "goto" has a limited use, but the way you are using it here does work because it is a short unconditional jump and it is easy to see your point.

There is also a possibility something like this might work:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (onGround)
{
    if (jump)
    {
        onGround = false;
    }
    else
    {
        //ground movement code here  <---  Never reached.
    }
}

if (inAir)  // <--- Or if "!onGround" or "jump". Not sure what you have to use.
{
    //air movement code here
}

Just a thought.

Andy
Yeah, I just prefer this format.
With your example it needs to check for !onGround so that's why I want to use goto.
In my example i simplified the code so it would be easier to understand.
Also what if I want to go from air movement to ground movement.
goto just makes it work without any additional conditions.
Last edited on
Perhaps
1
2
3
4
5
6
7
8
9
10
11
while ( alive ) {
  if ( onGround ) {
    if ( jump ) {
      onGround = false;
    } else {
      //ground movement code here
    }
  } else {
    //air movement code here
  }
}


> but I don't care if the code is hard to read.
Just don't expect anyone else to help you when you've tied yourself up in a mess of spaghetti code.
https://en.wikipedia.org/wiki/Spaghetti_code

haha yeah i know about spaghetti code and am ready for it
Why are you trying to put the state change and state action code into the same block. It would be cleaner to separate them (and also remove the goto).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
while (alive)
{
    if (onGround && jump)
    {
        onGround = false;
    }
    else if (!onGround && land)
    {
        onGround = true;
    }

    if (onGround)
    {
        // ground movement code here
    }
    else
    {
        // air movement code here
    }
}

I simplified code in my example so the real one is more complex. If I do it your way then it's gonna be too much conditions.
yes, it can also be slow(er). Slower may not matter, it could just be a few cpu clocks. The hardware can gloss over some jumps now, due to branch prediction, but that can only handle so many jumps at one time. Every time you jump, you risk messing up the CPU instruction pipeline and if the code is large, it may also risk a page fault.

the same is true for excessive if/else blocks.

here, what is the problem, then? Ignoring that goto is taboo (I hate this, c++ goto is quite nice all things considered)
-- you do an extra comparison and jump.
why?
consider this:
onground &= !jump; //if jump is false and onground was true, onground will now be false.
if(onground)
else //air

this may have no effect on a simple example, but in a large program with a lot of branching, it often does help.
also, do a k-map to reduce redundant compares in a large block of branches. This code snip may also break something -- its possible you needed the two booleans distinct in bigger code, and can't just assign them. In that case, though, you can still compare similar to the assignment, if the logic is truly identical for the two states in the given configuration.

also do not forget that c++ shorts comparisons when it can. So if something is false with a bunch of ands, it does not do the ands ... its still false.... you can make this work to your advantage by ordering your comparisons etc.

finally, do not forget that switch statments can do cool things to simplify some code too. You may need a state variable that represents your bools all in one, but that could save a lot of spaghetti.
Last edited on
If I do it your way then it's gonna be too much conditions.


The number of conditions should be the same either way. I think your argument is really "I already did this--I don't want to redo it"

What you are working on is a form of a state machine. Simplified, state machines have states, inputs and actions. It is simplest to start from a given state (e.g. onGround), receive an input (e.g. jump), perform an action (e.g ground movement code), and transition to a different state (e.g onGround = false;)

Whether or not you use goto statements to jump around in your code, I highly recommend separating the functionality of determining your current state from the actions you perform in that state. It will make your life a whole lot easier.

haha yeah i know about spaghetti code and am ready for it


We have all written spaghetti code. My first language was Apple BASIC. We used goto everywhere. Code was an atrocious mess. There is a reason experienced programmers direct beginners towards better code design. Sometimes it is almost impossible to maintain the code in the future.

If you ever go to a job interview and say you are ready for spaghetti code, the hiring manager should immediately scratch your name from the list of candidates. Don't plan to get a job there.

Recap: spaghetti: good to eat. VERY bad in programming.
doug4 by conditions, i meant more checking.

to get to air movement in my example
if(onGround)
if(jump)

in your example
if(onGround)
if(jump)
if(onGround)

that's what i meant and that's why i want to use goto.
Also am not trying to get a job or something.
Sorry, I did not quite understand jonnin's comment but if you are saying that it's slow to use goto, then i wont use it.
after all the reason I want to use it is only because of less checking so I thought it would be faster I guess.

here's another example with goto.
from start, think of this like
onGround = false
collision = true.
down key = true
and attack = true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
while(true){
    
    if(onGround){
        goto_ground:
        
        if(duck){
            goto_duck:
            
            if(duckAttack == 0){
                goto_noAttack:
                
                if(key_attack) {goto_setDuckAttack: duckAttack = 30; goto goto_duckAttack;}
                
                //duck movement code here
            }else{
                duckAttack -= 1;
                if(duckAttack < 10 && key_attack) goto goto_setDuckAttack;
                if(duckAttack == 0) goto goto_noAttack;
                
                goto_duckAttack:
                
                //duck attack movement code here
            }
        }else{
            if(key_down) {duck = true; goto goto_duck;}
            
            //ground movement code here
        }
    }else{
        //air movement code here
        
        if(collision) {onGround = true; goto goto_ground;}
    }
    
    //end of the frame
}


and i guess this is how doug4 suggested if am not mistaken
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
while(true){
    
    bool repeat = true;
    while(repeat){
        repeat = false;
        
        if(onGround){
            if(key_down){
                duck = true;
                if(duckAttack < 10 && key_attack) duckAttack = 30;
            }
        }
        
        if(onGround){
            if(duck){
                if(duckAttack == 0) //duck movement code here
                else{
                    //duck attack movement code here
                    duckAttack -= 1;
                }
            }else //ground movement code here
        }else{
            //air movement code here
            
            if(collision) {onGround = true; repeat = true;}
        }
    }
    
    //end of the frame
}
Last edited on
if am not mistaken

After the numerous examples of how you have treated other people in this thread I wouldn't presume to think you could come anywhere near making a mistake, @darlock
@againtry I don't understand. I did something wrong?
Sorry, I did not meant to be mean or something like that.
am really bad at conversations and english is not my first language.
I said it "could be" slow to use goto. The problem is not specific to goto, though, the problem is how the CPU runs code and what happens when you jump around inside the program instructions when you don't need to. That could be done via if/then, switch, goto, etc.

Jumping without need "could" cause a performance hit. Sometimes it does not. The more branches you have, the more likely you will have one or more of these in it.

The cost is very small for each one in 'wall clock time' But if you are looping a billionty something times, this can become a large sluggish spot in your code.

your simple example has an extra comparison and jump that can be eliminated by doing the logic better. This is a performance risk (may or may not matter) for no good reason, so best to not do it that way. Can you at least see that the comparison and jump are not necessary at all and that you are doing extra work?

lets look one more time

if(onGround){ //compare and possible jump #1.
if(jump) {onGround = false; goto air;} //compare and jump #2, redundant

vs
onground &= !jump; //assignment and bit logic, low cost
if(onground) //one compare and one jump
else //air

or, if you prefer:
if(onground && !jump) //as good as assign and compare. maybe better, depends on the rest of the code
else

you can rig it to use a goto without the extra logic; the problem IS NOT GOTO the problem is HOW YOU EXPRESSED THE LOGIC.
Last edited on
@jonnin the thing is that I need that extra comparison to set onGround and the speed.

1
2
3
4
5
6
7
if(onGround){
    if(jump) {onGround = false; yspeed = 12; goto air;}
    //ground movement code here
}else{
    air:
    //air movement code here
}


if i do it without it then where do i set onGround to false?
1
2
3
4
5
if(onground && !jump) //ground movement code here
else{
    if(onground == true) {onGround = false; yspeed = 12;}
    //air movement code here
}


Last edited on
onground &= !jump; //assignment and bit logic, low cost

the first version. I left it out of the other two, assuming you would find a place to put it. But this makes the first version that I gave maybe preferable now?

you can also do what is normally an error and assign inside an expression:
if( onground &= !jump) //the result of an assignment is jacked into 0/1 realm as bool for you
you will get a warning, because its a common error, but you can do it if you like.

ok so now you set the speed, which the original did not do.
here, it depends. if the speed is set in all branches, its still easy.
The trouble is I don't know what the glossed over ground and air code look like so placement is guesswork on my side. How about:

1
2
3
4
onground &= !jump; //assignment and bit logic, low cost
if(onground) //one compare and one jump
else //air
  yspeed = 12; //or, yspeed = min(yspeed, 12) or max or some other expression?? 


it does not require a if statement for yspeed with anything you have shown.
worst case you can say yspeed = 12*onground + yspeed*(!onground);
//here yspeed is set to 12*1 if onground is true, and unchanged if onground is false.
This is exotic branch avoidance. If you want to use an if statement instead, most people would do that. At some point being able to read the code becomes an issue if you overdo it.

it may also be possible to init yspeed = 12 and change it only if needed to .. that would also remove the logic.

the simple example is not the important part. The idea is what is important... get rid of as much excess branching and conditional testing etc as you can. Again, a k-map is very useful for that in big systems. They don't stress those enough to programmers; it was used more in wiring circuits to avoid excess wires and costs.
Last edited on
@jonnin thank you, I did not really used bit logic or anything like that before, so i was confused and I don't know what k-map is, but will look into that.

yeah that yspeed needs to be set to 12 only on first frame when onground = false so
1
2
3
4
yspeed = 12*onground*jump + yspeed*(!onground);
onground &= !jump;
if(onground) //ground movement code here
else //air movement code here 


also now yspeed and onground are set every frame. that's ok right?
since it's low cost, but what if i have more variables to set like yspeed?
how many can i afford before it's better to use if()?
i know about spaghetti code and am ready for it


No you're not. No-one is 'ready' for spaghetti code. Along with others here, I went through this pain when I started programming. Debugging and changing is worse than your worst nightmare. I've seen colleagues re-write hundreds of lines of code as this would be quicker/easier than trying to make a change to the existing code. Please, do yourself a big favour and don't use it.
What happens if the f'king duck jumps into a pond?
It goes quackers.....
Treat this thread seriously please.
Pages: 12