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:

image

states

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!

protect_ui_events

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.

mouse_events_are_unique

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.


raptor declares two children of the StatefulObject: The RaceTable and the RaceObject which grants the benefit of random content being automatically stateful too.

Reacting on sprite broadcasts

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... image

...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;
	}
);

Reacting on input events

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!

The pre-declared events

image

A real example

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 through vk_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!

⚠️ **GitHub.com Fallback** ⚠️