GMS 2.3+ Structs and State Machines?

TheJoe

Member
Hi everyone,

I think I understand at a high level object-oriented language concepts but GML is really the only language I've spent significant time with. I believe I'm grasping the use for Structs / Constructors, but the examples I've found seem to show them as better ways to build, contain, and templatize objects.

Before I go and build a state machine like I've always done, should I try to do them using Structs now? Like each struct is a state maybe? I'm guessing it might be situational on the project, but I'm wondering if there's best practices or if I'd be saving myself a lot of future grief by learning & implementing Structs in this way.

Any pointers or even a hypothetical example would be much appreciated!
 
  • Like
Reactions: Rob

kupo15

Member
enums would be a better way to create a state machine

GML:
enum state {
idle,
running,
hurt
}

if stateIndex == state.walk
{
}

if stateIndex == state.running
{
}
 
Last edited:

samspade

Member
Hi everyone,

I think I understand at a high level object-oriented language concepts but GML is really the only language I've spent significant time with. I believe I'm grasping the use for Structs / Constructors, but the examples I've found seem to show them as better ways to build, contain, and templatize objects.

Before I go and build a state machine like I've always done, should I try to do them using Structs now? Like each struct is a state maybe? I'm guessing it might be situational on the project, but I'm wondering if there's best practices or if I'd be saving myself a lot of future grief by learning & implementing Structs in this way.

Any pointers or even a hypothetical example would be much appreciated!
I wouldn't call them a better way to make objects than using GM objects, just a different way with different pros and cons. Their primary pro is that they don't come with all the 'baggage' a GM object does. They don't have 50 built in variables, they don't have events that are automatically running, etc. and their primary con is that they don't come with all the 'baggage' a GM object does, they don't have all the built in variables or a bunch of built in events for you to use (and yes I understand I just said the same thing twice).

So I wouldn't make a state machine out of structs just because you can. But you can. Still the old method of switch statements or scripts I think is very viable. Except that in either case I'd be using method variables.
 

kupo15

Member
I wouldn't call them a better way to make objects than using GM objects, just a different way with different pros and cons. Their primary pro is that they don't come with all the 'baggage' a GM object does. They don't have 50 built in variables, they don't have events that are automatically running, etc. and their primary con is that they don't come with all the 'baggage' a GM object does, they don't have all the built in variables or a bunch of built in events for you to use (and yes I understand I just said the same thing twice).

So I wouldn't make a state machine out of structs just because you can. But you can. Still the old method of switch statements or scripts I think is very viable. Except that in either case I'd be using method variables.
Oh yeah completely forgot about that! Sean made an amazing point and example out of using scripts as state machines without needing to use enums or switch statements. Completely forgot about it!

Start around 7:30
 

rytan451

Member
enums would be a better way to create a state machine

GML:
enum state {
idle,
running,
hurt
}

if state.walk
{
}

if state.running
{
}
The code is really wrong. state.running resolves to 1, which is a truthy value, and so the first if statement will always run.

Enums are a good way to represent a finite and definite set of states. Structs are a good way to store disparate data in a structured manner (they're actually called "structures", as in "data structures", but are often shortened as "structs"). So, you would use a struct to store data about a type of weapon (damage, speed, what sprite it uses, etc.); it's like a collection of variables. Enums are efficient named values.
 

matharoo

Udemy Instructor
I used structs to store the 4-directional images of my character in each state. Here is an example, although it will be badly written because I'm on my phone -- sorry!

GML:
states = {
idle : {
  left: sIdleLeft,
  right: sIdleRight,
  // up and down
},
walk : {
  // repeat
}
}

state = states.idle;
This way you can easily retrieve the sprite from the current state struct, and you can also run a switch statement on it!

GML:
switch (state) {
case states.idle:
case states.walk:
}

if (movingleft) sprite_index = state.left;
else // other directions
Now you have to realize that there is no best way to do this, there are a lot of ways with their own pros and cons. This is just one way of using structs for states. The pro here is that state sprites are easier to set up.
 

kupo15

Member
The code is really wrong. state.running resolves to 1, which is a truthy value, and so the first if statement will always run.

Enums are a good way to represent a finite and definite set of states. Structs are a good way to store disparate data in a structured manner (they're actually called "structures", as in "data structures", but are often shortened as "structs"). So, you would use a struct to store data about a type of weapon (damage, speed, what sprite it uses, etc.); it's like a collection of variables. Enums are efficient named values.
my bad, I forgot to add the variable that keeps track of the state itself. Corrected!

if stateIndex == state.running

This is what I use for keeping track of what screen I'm on in my mobile apps
 

Gizmo199

Member
Another thing with the enums that you can do (which is what I do to keep more organized) is setup the enum and call event_user functions
Create event:
GML:
enum STATE{
PAUSED,
PLAY
}
player_state=STATE.PLAY;
Step event:
GML:
event_user(player_state);
Event user 0
GML:
// paused
Event user 1
GML:
// play

This also works great for tabs and having each event user as a separate "window" and sub-tabs with separate enums
 

kupo15

Member
Another thing with the enums that you can do (which is what I do to keep more organized) is setup the enum and call event_user functions
Create event:
GML:
enum STATE{
PAUSED,
PLAY
}
player_state=STATE.PLAY;
Step event:
GML:
event_user(player_state);
Event user 0
GML:
// paused
Event user 1
GML:
// play

This also works great for tabs and having each event user as a separate "window" and sub-tabs with separate enums
I feel like I need to utilize user events more...or at all. I think I understand what they are used for but not exactly, and I'm afraid to use them bc I always thought they were "hacky" if you don't use the step events. Am I wrong with that? Are user events something other languages use a lot too? What's the difference between using a user event and calling a script instead? Also, if you have to use the user_event event then you are limited to 16 which might be a problem too
 

Gizmo199

Member
I feel like I need to utilize user events more...or at all. I think I understand what they are used for but not exactly, and I'm afraid to use them bc I always thought they were "hacky" if you don't use the step events. Am I wrong with that? Are user events something other languages use a lot too? What's the difference between using a user event and calling a script instead? Also, if you have to use the user_event event then you are limited to 16 which might be a problem too
So afaik other languages dont have them. I like them mainly because it gives me a separate window to hold all my code in and in the step even I just have to call event_user(state). There isn't really a difference using them over a script except the enumeration. So I can use an enum for pause as 0 and just call event_user(state.pause) and It will automatically execute that user event, where as if you wanted to do the same thing with scripts you'd have to set the enum to the script you want to call. Also scripts used in this way are global scope where as user events are local. Now with the function() support there isn't a huge need for user events anymore but I think they have a huge organizational advantage over keeping up with scripts. Plus if you are calling the user event in the step event it's no different than calling a script. You can use the same idea with a switch statement inside of user events as well. So if you want say a play state, and a cutscene state you can have multiple enums, functions, or scripts also called within the user events. I just think for organization purposes they are fantastic and super easy to set up with an enumerate since they themselves are enumerated. It's nice (for me) to have an entire window only dedicated to movement.

Edit: and yes you can only have 16 but basically imagine having 16 step event windows at your disposal at anytime for whatever purpose you want. That's my thinking anyway. Obviously you have to call them or they won't automatically run but that goes for any script/function

Tl;dr they have imo a huge organizational advantage as a separate window and are enumerated locally without need for any variable setting.
 
Last edited:

drandula

Member
Just to throw my coin here. Structs can hold methods, which can be executed.

For example, every state-struct has methods "Start", "Step", "End". Parent 'default'-struct
has these too, and when every state-struct inherits this default, they do atleast have these empty methods (and can be called without causing error). The Step-method does the most of work: does the actual action for state, but also checks if state needs to be changed (or make it own method, which Step calls when needed). Now before state is changed, method End for current status-struct is called, and then call Start for next state-struct.

Now entity has State-variable, which holds reference for state-struct (which can be any). In entity's step function it keeps calling only State.Step(); . Status-structs itself change which struct entity holds in Status-variable.

Edit. This is just off my head, haven't done it like this yet.
Edit2. Also status-structs need to know which is calling. Or maybe entity call is like "Status = Status.Step();" and in the end of struct method self or target status-structs id is returned.
 
Last edited:

xenoargh

Member
Structs are awesome.

You can put methods in them. You can make them inherit from one another, using one as a master template, etc. Access to them is fast and lightweight. They can store static or dynamic data. You can test them to see if they have a given field, instead of crashing.

If the process of inheritance was made cleaner / simpler (seriously, why can't inheritance be just, struct B = inherit(struct A) or something that simple, so that making a fast, deep copy is straightforward?) they'd be really amazing, rather than just "really, really useful".

I'm mainly using them to store big chunks of static data that would otherwise require a bazillion globals or Objects that would be referenced but never ever instantiated. They've replaced a huge amount of logic with a simple, fast switch statement (i.e., "get the right struct for Foo") and then some logic ("get Foo.Bar and do more stuff").
 

Rob

Member
I wouldn't call them a better way to make objects than using GM objects, just a different way with different pros and cons. Their primary pro is that they don't come with all the 'baggage' a GM object does. They don't have 50 built in variables, they don't have events that are automatically running, etc. and their primary con is that they don't come with all the 'baggage' a GM object does, they don't have all the built in variables or a bunch of built in events for you to use (and yes I understand I just said the same thing twice).

So I wouldn't make a state machine out of structs just because you can. But you can. Still the old method of switch statements or scripts I think is very viable. Except that in either case I'd be using method variables.
I'd really like to know what people replace with structs if you'd care to share here or elsewhere. I think I understand them but I don't know why I should use them over something else that I do currently.
For example, if I want a lot of stats in the game, I'd usually have .csv files and turn them into a grid. I'd then have objects within the game that would have their own instance data structures (list/array) that would take their data from the grids. Somebody mentioned RPG's would really benefit from Structs, but not why. What about menu systems or data management in general? Some solid examples would be of great benefit to me and others I'm sure.
 

samspade

Member
I'd really like to know what people replace with structs if you'd care to share here or elsewhere. I think I understand them but I don't know why I should use them over something else that I do currently.
For example, if I want a lot of stats in the game, I'd usually have .csv files and turn them into a grid. I'd then have objects within the game that would have their own instance data structures (list/array) that would take their data from the grids. Somebody mentioned RPG's would really benefit from Structs, but not why. What about menu systems or data management in general? Some solid examples would be of great benefit to me and others I'm sure.
I'm still fairly new to structs, but I think they are useful when you want to have data and logic mixed together and as of 2.3.1 might be versatile enough to replace maps (I haven't spent much time with 2.3.1 yet). So Drandula's struct state machine idea would be a good use of structs for state in my, somewhat uninformed, opinion as those states are essentially their own little machines with things. There would be a reason to use structs there as the state machine itself essentially has an internal state (starting, running, stopping) which needs to be tracked. So I at least partially take back my earlier comments if you want to do something like that structs actually seem great. If all states did though was run, I'd probably still just be using functions or switch statements.
 
  • Like
Reactions: Rob

TheJoe

Member
Thanks all for the replies and the discussion - really helpful hear everone's takes on this stuff. To my original question, it sounds like there is a way to do (like @drandula idea) but I'm of a similar mind as @Rob and @Gizmo199 that there seems like there's pre-existing ways of doing states and stats and at least for my purposes the organizational benefits of building state machines through user events seems better than shoehorning them into a struct. I've also implemented Sean's script method but it felt less organized that user events (and I never hit the user event limit).

Thank you @matharoo for chiming in - side note I like your tutorials and I've watched your Structs one 20 times to try and wrap my head around Structs đŸ˜„ Your example at the last few seconds is really the only use-case for Structs that makes sense to me and I think maybe this is the sort of thing @xenoargh is doing(?):

GML:
global.Settings = {
    sfxVolume: 100,
    musicVolume: 80,
    
    graphics : {
        resolution: 1080,
        textures: "high",
        framerate: 30
    }
}
This seems like an improvement both in initializing but organization and having the ability to execute self-contained methods / functions to boot. If anybody's keen I'd love more examples like this (the vector stuff goes over my head unfortunately).

Would it be correct to say that structs are more of a parity feature for people that are used to other languages and have felt limited by data structures and the reliance on globals?
 

matharoo

Udemy Instructor
Would it be correct to say that structs are more of a parity feature for people that are used to other languages and have felt limited by data structures and the reliance on globals?
Yes -- programmers who're used to OOP languages really benefit from structs & constructors, but it doesn't just stop there. They're an objectively useful addition to GML for anyone, and you can only harness the full power of GML by learning to use structs and constructors. I recommend looking into game-dev-related tutorials for other languages and reading about OOP implementations of simple game features, so that you can apply all of that in GML. Since GML itself just became OOP, there aren't enough resources out there for you to understand why an object-oriented language is good to have -- which is why you should also read outside material.
 
  • Like
Reactions: Rob

kupo15

Member
Thanks all for the replies and the discussion - really helpful hear everone's takes on this stuff. To my original question, it sounds like there is a way to do (like @drandula idea) but I'm of a similar mind as @Rob and @Gizmo199 that there seems like there's pre-existing ways of doing states and stats and at least for my purposes the organizational benefits of building state machines through user events seems better than shoehorning them into a struct. I've also implemented Sean's script method but it felt less organized that user events (and I never hit the user event limit).

Thank you @matharoo for chiming in - side note I like your tutorials and I've watched your Structs one 20 times to try and wrap my head around Structs đŸ˜„ Your example at the last few seconds is really the only use-case for Structs that makes sense to me and I think maybe this is the sort of thing @xenoargh is doing(?):

GML:
global.Settings = {
    sfxVolume: 100,
    musicVolume: 80,
   
    graphics : {
        resolution: 1080,
        textures: "high",
        framerate: 30
    }
}
This seems like an improvement both in initializing but organization and having the ability to execute self-contained methods / functions to boot. If anybody's keen I'd love more examples like this (the vector stuff goes over my head unfortunately).

Would it be correct to say that structs are more of a parity feature for people that are used to other languages and have felt limited by data structures and the reliance on globals?
Structs are awesome! Now I wish I understood more how methods work and what they actually do. The are probably things I could be using them for that makes things even better.

Here is a great example of one of the things I used structs for

 
Top