• Hey! Guest! The 40th (!!!) GMC Jam will take place between February 25th, 12:00 UTC to March 1st 12:00 UTC. Why not join in this very special anniversary jam! Click here to find out more!

State Machines

Hey guys,
First time using GMS2 and posting. I'm running into issues with my state machine. I'm not getting any syntax errors, but the game isn't loading the states. Basically, I can't move after implementing the state machine. Any advice or ideas on what it might be? The project can't be attached as a file on here for some reason either?
 

kburkhart84

Firehammer Games
You would need to post code from where you are switching the states. We have no idea at all how you implemented this stuff so we have no idea what could have gone wrong.

Before you do that though, I recommend you take a look at show_debug_message() and the debug log in the IDE. You can sprinkle debug messages all over your code as needed. Then you can know if code is actually being reached or not. You could put it where you first enter a state, just to confirm you actually did enter the state. You can also put it in the code that causes you to change state, just to see if that is happening properly. Finally, you can also output any variables you want, just to see if the values you have are what you expect them to be. This technique will get you quite far, and give you a better idea what is actually happening in your code, and then you will have a better idea of what code is doing, what it isn't doing, and what you need to ask for help on more specifically.
 
You would need to post code from where you are switching the states. We have no idea at all how you implemented this stuff so we have no idea what could have gone wrong.

Before you do that though, I recommend you take a look at show_debug_message() and the debug log in the IDE. You can sprinkle debug messages all over your code as needed. Then you can know if code is actually being reached or not. You could put it where you first enter a state, just to confirm you actually did enter the state. You can also put it in the code that causes you to change state, just to see if that is happening properly. Finally, you can also output any variables you want, just to see if the values you have are what you expect them to be. This technique will get you quite far, and give you a better idea what is actually happening in your code, and then you will have a better idea of what code is doing, what it isn't doing, and what you need to ask for help on more specifically.
Is there a way I can post the project? There would need to be pictures of essentially everything since the states exists in tons of sections.
 

samspade

Member
Is there a way I can post the project? There would need to be pictures of essentially everything since the states exists in tons of sections.
Not really. You shouldn't post picture either. You should post code using the code tags (per forum rules). You can select and copy past into the forum after hitting the code button. You probably don't need to post 100% of all code that in any way touches a state. Start with just the actual state code. Are you using method variables, script functions, switch statements, a combination?
 

hogwater

Member
When I was first getting my state machine together I found it very helpful to draw the state variable (and others) to the screen, so I could see what was going on in real time.
 

Director_X

Member
When I was first getting my state machine together I found it very helpful to draw the state variable (and others) to the screen, so I could see what was going on in real time.
I also draw some text for my states so I know what is happening - very helpful when things go wrong.



One thing to note is when you SET one state to TRUE, you need to UNSET the other states to FALSE.
ALSO, sometimes, if a state is true, you may need to EXIT the block of code so it doesn't carry on executing the function.
 

kburkhart84

Firehammer Games
Is there a way I can post the project? There would need to be pictures of essentially everything since the states exists in tons of sections.
This is EXACTLY why I made my suggestion of learning how to use debug messages. The others' suggestions of drawing text to the screen can also work, though it may be more difficult than quicky debug messages(though it is better in the long run for bigger projects). Once you dig in with a little debugging skill like I'm suggesting, if you can't figure it out, you will at the least have isolated the cause, and then you can post just the offending code that you don't know what has wrong with it.
 

Rob

Member
Is the state variable initially set to the correct state that you want?
Are you using enumerators/numbers or text? If text, then there may be typing mistakes.
Using show_debug or draw_text as already suggested will probably help you bug-fix most of all.
 
Last edited:

Slyddar

Member
One thing to note is when you SET one state to TRUE, you need to UNSET the other states to FALSE.
If you're using enums and a states variable, all you need to do is assign the state variable to a state. There's no need to UNSET anything to false, or even set anything to true. The variable holds the current state you are in.
 
Create Code
hp = 50;
hp_max = hp;

healthbar_width = 870;
healthbar_height = 45;

healthbar_x = (1010);
healthbar_y = ystart - 280;

hitByAttack = ds_list_create();

enum PLAYERSTATE{
RF_FREE,
RF_PUNCH,
RF_KICK,
RF_FIREBALL,
RF_DP,
RF_HIT
}

state = PLAYERSTATE.RF_FREE;

Step Code
RFKeyLeft = keyboard_check(vk_left);
RFKeyRight = keyboard_check(vk_right);
RFKeyPunch = keyboard_check(1);
RFKeyKick = keyboard_check(2);
RFKeyDP = keyboard_check(3);
RFKeyFireball = keyboard_check(0);

switch (state) {
case PLAYERSTATE.RF_FREE: RF_free(); break;
case PLAYERSTATE.RF_PUNCH: RF_punch(); break;
case PLAYERSTATE.RF_KICK: RF_kick(); break;
case PLAYERSTATE.RF_FIREBALL: RF_fireball(); break;
case PLAYERSTATE.RF_DP: RF_dp(); break;
case PLAYERSTATE.RF_HIT: RF_hit(); break;
}

Again, not sure if this is helpful due to the fact that lots of other factors can't be seen. Nothing appears when I use the debug feature. My character simply doesn't move.
 
One thing to note is when you SET one state to TRUE, you need to UNSET the other states to FALSE.
Totally not, bro. Cases in a switch are exclusive. if your player_state = state and then you switch(player_state) only one case will ever run (or potentially none if you have no default).
 
  • Like
Reactions: Tyg

Tyg

Member
You would set the player to idle at first, when a key is pressed set it moving, say a spacebar jumping then falling , no movement idle
the switch(statevar) executes the case statement that its value is, in that case statement you determine other states that can be from the state it is in
the movement code just checks if a key is pressed and assigns it to a var, no where are you setting the state or even doing anything with the vars that check for movement
there is no logic flow

state is a variable best to use a string like "RUN", "JUMP", "FLY" otherwise your dealing with 0,1,2,3 which can get cryptic
not a boolean or you just have a boolean, and you dont set states to true or false, that is not a state machine
the switch statement will determine which case statement it needs to execute run it and exit
if you do this there is no need for enums as the name of the state is right in the case statement and logic code can be easily modified
but that state has to have a logical flow to another state, draw the logic flow out out paper and it will make it much more easier to code

state = "SWIM";

step
switch(state)
case "punch": do the punch routine when done back to idle or blackeye..lol
case "run": move the player then change the state to idle when the key is let up

i think you are just trying to overcomplicate the player
i would put all the movements on an input controller object
then all the player has to process is its state and draw the matching animations
or just make a state machine object that only handles the state, less for the player object to process
each enemy can be at a state at a different time so you would put the enemy state machine on the parent enemy, "Biting", "Growling",
 
Last edited:

Rob

Member
Create Code
hp = 50;
hp_max = hp;

healthbar_width = 870;
healthbar_height = 45;

healthbar_x = (1010);
healthbar_y = ystart - 280;

hitByAttack = ds_list_create();

enum PLAYERSTATE{
RF_FREE,
RF_PUNCH,
RF_KICK,
RF_FIREBALL,
RF_DP,
RF_HIT
}

state = PLAYERSTATE.RF_FREE;

Step Code
RFKeyLeft = keyboard_check(vk_left);
RFKeyRight = keyboard_check(vk_right);
RFKeyPunch = keyboard_check(1);
RFKeyKick = keyboard_check(2);
RFKeyDP = keyboard_check(3);
RFKeyFireball = keyboard_check(0);

switch (state) {
case PLAYERSTATE.RF_FREE: RF_free(); break;
case PLAYERSTATE.RF_PUNCH: RF_punch(); break;
case PLAYERSTATE.RF_KICK: RF_kick(); break;
case PLAYERSTATE.RF_FIREBALL: RF_fireball(); break;
case PLAYERSTATE.RF_DP: RF_dp(); break;
case PLAYERSTATE.RF_HIT: RF_hit(); break;
}

Again, not sure if this is helpful due to the fact that lots of other factors can't be seen. Nothing appears when I use the debug feature. My character simply doesn't move.
Ok so at this point script RF_Free is running every step.

Show the contents of that script.

I get why people use scripts to run for every state and sometimes I will do something similar if the system is dynamic but you definitely lose some readability and it's probably harder to debug.
 

Rob

Member
Create Code
hp = 50;
hp_max = hp;

healthbar_width = 870;
healthbar_height = 45;

healthbar_x = (1010);
healthbar_y = ystart - 280;

hitByAttack = ds_list_create();

enum PLAYERSTATE{
RF_FREE,
RF_PUNCH,
RF_KICK,
RF_FIREBALL,
RF_DP,
RF_HIT
}

state = PLAYERSTATE.RF_FREE;

Step Code
RFKeyLeft = keyboard_check(vk_left);
RFKeyRight = keyboard_check(vk_right);
RFKeyPunch = keyboard_check(1);
RFKeyKick = keyboard_check(2);
RFKeyDP = keyboard_check(3);
RFKeyFireball = keyboard_check(0);

switch (state) {
case PLAYERSTATE.RF_FREE: RF_free(); break;
case PLAYERSTATE.RF_PUNCH: RF_punch(); break;
case PLAYERSTATE.RF_KICK: RF_kick(); break;
case PLAYERSTATE.RF_FIREBALL: RF_fireball(); break;
case PLAYERSTATE.RF_DP: RF_dp(); break;
case PLAYERSTATE.RF_HIT: RF_hit(); break;
}

Again, not sure if this is helpful due to the fact that lots of other factors can't be seen. Nothing appears when I use the debug feature. My character simply doesn't move.
Ok so at this point script RF_Free is running every step.

Show the contents of that script.

I get why people use scripts to run for every state and sometimes I will do something similar if the system is dynamic but you definitely lose some readability and it's probably harder to debug as a newbie.
 

Yal

🍋 *lemon noises*
GMC Elder
I get why people use scripts to run for every state and sometimes I will do something similar if the system is dynamic but you definitely lose some readability and it's probably harder to debug as a newbie.
I like to call this approach "infinite state machine" (as opposed to finite state machine) because it lets you do basically anything. My state machines basically boil down to
GML:
if(state != undefined){
  state();
}

With that said, you can still debug-draw the current state with
draw_text(x,y,script_get_name(state))

and this helps detect state-transition issues.

I also don't have idle/movement states be different, I have a single "ordinary" state which has the same behavior but animates differently if you're idle. It cuts down on redundant state transitions a ton, and I think this helps a lot on keeping the game feeling responsive.



Anyway, some things that could go wrong here:
  • State not actually reading inputs
  • RF_Free doing literally nothing
  • RF_Free not having code that handles movement
  • You don't use the RF_key variables but you removed the old hardcoded input events
 
Ok so at this point script RF_Free is running every step.

Show the contents of that script.

I get why people use scripts to run for every state and sometimes I will do something similar if the system is dynamic but you definitely lose some readability and it's probably harder to debug as a newbie.
function RF_free(){
if (RFKeyLeft) RightFighter.x -= 20;
if (RFKeyRight) RightFighter.x += 20;
}
 
Top