StatefulObject - Grisgram/gml-raptor GitHub Wiki
StatefulObjects
are Saveable
as they are normally your living objects and items in the game and should be part of your savegames.
They add three more variable definitions:
All children of this object automatically own a StateMachine
. The variable's name for this StateMachine is states
, and it already exists when your Create
event runs since it is declared in the Variable Definitions
.
Caution
Do not rename this variable to anything else! The name states
is used internally in many places and in several classes and objects!
The flag protect_ui_events
prevents all mouse_*
events from being sent to your object, if one of these conditions is true:
- it is not visible
- a popup layer group is open and your object is not in any of the popup layers
- it is not enabled (
is_enabled
is set to false)
This is true
by default to avoid, that your buttons in the normal UI layer receive click events while a popup message (like a MessageBox
) is shown to the user.
The flag mouse_events_are_unique
ensures, that the object won't receive a mouse event, when it is already in the state, the event would set.
The event only arrives, if the object is currently in a different state than the one that would be set by the event.
Tip
If you use Shared States, you can edit the defined variable in your child of StatefulObject
to load the predefined states when the object is created.
Simply change the new StateMachine(self)
to a constructor call that contains the predefined states, like new StateMachine(self, standard_idle, standard_spawn, standard_die)
or something similar.
Read more on the concept of Shared States.
One often underrated feature of GameMaker is its ability to broadcast string messages from sprites (and sequences), while they are running.
Those can be very valuable to synchronize sounds to frames and other things, they are like frame triggers on animations. Raptor's animation system is kind of a replacement of the sequences, so the StatefulObject
will react only on sprite broadcasts, not on sequence broadcasts.
If you placed some broadcasts in a sprite like here...
...then, like with keyboard and mouse events, you will receive a Broadcast state
. It's optional, like the input events, but you may listen to a sprite broadcast in your object, if you want.
Tip
Sprite broadcasts are prefixed with bc:
from the StatefulObject
, followed by the string of the broadcast.
In the example above, the state you receive, would be bc:footstep
The code reacting on this, could look like this:
states
.add_state( // ... some other states ... )
.add_state("bc:footstep",
function(sdata, prev_state) {
play_sound(SFX_Knight_Walk1);
return prev_state;
}
);
A simple single variable definition would not be enough to justify an entire object; there must be more in a StatefulObject
. And there is!
Much, much more.
When you click the Events
buttons in GameMaker Studio on a StatefulObject
, it shows a huge list of defined events on the screen.
No worries, this will not cost you performance! Most of those events fire rarely (or never). The StatefulObject
just implements all of them with a one-liner call to set_state(...)
when one of these events occurs. If you look closely at the picture, you can see in the description next to each event which state will be set.
This is the great interactivity feature of the StateMachine
. You can receive inputs as states! And you can directly change to the follow-up state based on the input.
See how this works and why this is a really cool time-saver when you develop your game in the example below!
So, how can we use this?
// This is the Create event - you do not need to override ANY other event!
states
.add_state("idle", function() { sprite_index = my_idle_animation; })
.add_state("jump", function() { sprite_index = my_jump_animation; })
.add_state("run", function() { sprite_index = my_run_animation; })
.add_state("walk", function() { sprite_index = my_walk_animation; })
.add_state("ev:key_press_W", function { vspeed = -4; return keyboard_check_pressed(vk_shift) ? "run" : "walk"; })
.add_state("ev:key_press_A", function { vspeed = 4; return keyboard_check_pressed(vk_shift) ? "run" : "walk"; })
.add_state("ev:key_press_S", function { hspeed = -4; return keyboard_check_pressed(vk_shift) ? "run" : "walk"; })
.add_state("ev:key_press_D", function { hspeed = 4; return keyboard_check_pressed(vk_shift) ? "run" : "walk"; })
.add_state("ev:key_press_vk_space", function { direction = 45; speed = 6; return "jump"; })
.set_state("idle);
So, what's going on here?
We created event states. These are states that trigger when an event occurs. Their name always starts with ev:
followed by the event name.
You can see in the screenshot above which events can be used and how they are named.
Let me explain the key_*
events explicitly:
- The name is
key_press_*
- The
*
is either an alphanumeric character, as shown above, - Or any of the
vk_*
constants, GameMaker offers. See a full list here in the GameMaker manual - Function keys are named
vk_f1
throughvk_f12
Of course, mouse events can be done in the same way. mouse_enter
and mouse_leave
are especially very useful because the object might want to change its rendering (or switch to an outlined view, maybe through the Outline Shader Drawer?) when the mouse comes and goes.
Now let's drive this idea one step further.
What if we could define such event states only once and have all the objects switch to an outlined view when the mouse enters them and back to normal when the mouse leaves them?
What if I have some more complex mixtures of animations, particle effects, and shaders going on in a state and I don't want to recode that several times?
Well... Let me introduce you to Shared States!