In GameMaker, a commonly used technique is to build a system made up of a several objects to represent an entity in your game, such as the player or enemy, in various states, such as idle, dead, shooting, jumping, running, climbing, and so forth. This is what is known as a Finite State Machine pattern.
When the time is right in the game, we change an instance from one state object to another by using the powerful instance_change()
function. Instance_change()
takes the instance and transforms it into a new type of object. Its Event behaviors will change to those defined by the new object type, but its old properties (object variables) will remain the same as before, allowing the instance to retain its variables with their current values.
The instance_change()
function takes two arguments: object
, the object the instance will turn into, and perform_events
, a boolean which controls whether the new object’s Create event will be performed or not.
Normally, the Create event is where an object initializes its variables and initiates its default behavior. When we’re dealing with a State Machine comprised of a number of objects, this can become problematic, however. Some code in the Create Event is initialization code that we may only want to execute one time, to set up the instance when a brand new instance is created, while other code in the Create event is behavioral and we may need to execute whenever an existing instance reverts back into that state again. Thus, the perform_events argument in the instance_change() function isn’t adequate for this situation — it’s too all or nothing.
For example, let’s say I have a generic object for an enemy, oEnemy. I want some visual variety to this enemy, so I’ve created a few different sprites for it. In the Create Event, I want to randomly choose one of those sprites to be the sprite for this instance. But if the instance changes into another state object, and then reverts back, if I call the Create Event, it will randomly choose a new sprite. I don’t want this, as it ruins the illusion of continuity — I need that instance to retain its sprite. But I do need the Create Event to run, whenever it re-enters this state, because I’m using it to set the instance in motion.
So, how can I elegantly select which lines of code I want to run in the Create Event?
Conditional blocks
This is the least elegant solution, but you could use if to check whether a variable exists or has a value. For example:
if sprite_index == -1 {sprite_index = choose(sprite1, sprite2, sprite3);}
This is inelegant because it adds lots of lines of code that only need to be run one time (when a brand new instance is created) but need to be checked potentially many times (any time that instance changes back into the object state). It also only checks certain, specific things, case by case. As I continue to build the state machine, I may end up introducing more features which require initialization, which would necessitate more checks, further bloating the code. I always want to write the least amount of code needed, both for reasons of performance and maintainability.
Move one-time code to an init state object.
The more elegant solution is to recognize that initialization is its own state, and we need to separate it out from the other states in the state machine. We can create an oEnemy_init object, put our one-time initialization code into it, and then the final step in the Create Event for the init object would be to change the object into the default state.
None of the other states in the state machine should put the instance back into the init state, thereby guaranteeing that the init code only executes once. Now your code is neatly separated, your states objects in your state machine are as simple as can be.
Another alternative, probably not as elegant but definitely more compact, is to use a single object instead of multiple objects, keep a variable called STATE (or whatever), initialize it to a default state in the Create event, and change it in the other events as needed. Then, whenever the object would do something if it’s in a certain state, just check the variable, and if STATE is equal to (whatever value), do whatever it is you need to do. The list of what STATE corresponds to what numeric or text value is arbitrary and should be documented in a comment, of course.
Ian Schreiber
True, it is possible to implement an entire state machine within a single object, in just the manner you are describing. I found that my earliest GameMaker projects were trying to accomplish this, and resulted in bloated, complicated objects that were harder for me to debug, with gnarly nested if blocks and other insanity. Then I remembered a Google Tech Talk video that I’d watched where some guy was talking about how
if
is often used where polymorphism is more appropriate (in OOP languages), and it got me to thinking… GML isn’t truly OOP, but GameMaker Objects are kinda analogous to OOP Classes.Generally I find that more, simpler objects are easier to maintain and troubleshoot than a single, complicated object. And often perform faster, since there’s much less branching involved in resolving their Events.
But there’s probably a balance point — I don’t know where exactly, but if you have a State Machine made out of 30 objects all working together to represent one entity, that might be on the ridiculous side. The machines I’ve seen tend to be in the single digits of states, which is pretty manageable, IMO.
chris_sanyk
Oh, I agree there’s absolutely a balance (for an object with just two states where 90% of the functionality is the same and it’s just one small difference, also probably easier to keep it all in one object than to split it in two). Your original post provides a wonderful option for people looking to implement a certain type of thing in GameMaker, I’m just offering an additional option, and let each implementer choose the appropriate implementation for their projects on a case-by-case basis. More options = good :)
Ian Schreiber
Ian, I think the point of this article was to show how instance_change() could be used to create a multi-object FSM, not to purport it as the definitive way to build FSMs
r
In part, this design pattern was inspired by this Google TechTalk: http://www.youtube.com/watch?v=4F72VULWFvc I think Misko’s presentation is more persuasive than I can be.
chris_sanyk
Hello from 3 years later! Just want to say that this response helped me develop some really simply solutions to some issues I was having with my game. I have implemented the “State” variable concept into my objects – so I simply wanted to thank you, even though you commented this all the way back in 2013!
Chris Kolar
You’re very welcome! It’s always great to hear from anyone who has been helped by my articles. If you have released any of your projects, feel free to post links to them. It’d be nice to check out your work.
Chris