Control the flow of time in your project for Gamemaker.
Matrix Engine
A compact time-management engine for GameMaker that keeps alarms, timers, animation speed and time-dilation transitions working together through one global game-speed value.
Features
- One global speed variable manages game speed:
global.speed_modifier. - A persistent alarm manager object manages all alarms and timers.
- Helper functions keep alarms and timers accurate as game speed changes.
- Time-dilation functions make it easy to switch instantly or smoothly from one speed to another.
- Image-speed helpers keep animated objects visually synchronised with the current game speed.
Licence
You may use the Matrix Engine in personal or commercial projects.
Credit is not required, but is appreciated.
How to Use
Place obj_alarm_manager into a room at the start of your game, before any Matrix Engine alarms or timers are created.
The object is persistent and only needs to be created once.
obj_alarm_manager exists.Main Functions
The asset contains four related groups of functions: alarm management, timer management, time dilation and image-speed management.
Alarm Management
alarm_set_ext()
Create a new alarm or update an existing alarm.
alarm_get_ext()
Get the current remaining duration of an alarm in frames.
alarm_set_state()
Temporarily activate or pause an alarm.
alarm_get_state()
Check the current state of an alarm.
alarm_exists()
Check whether a specified alarm exists.
alarm_destroy()
Destroy a specified alarm.
alarm_check_game_paused()
Return whether the game is paused. Only alarms and timers flagged to work while paused will continue ticking.
Timer Management
timer_create()
Create a new timer or reset an existing timer.
timer_get_elapsed()
Get the total active time elapsed since the timer started.
timer_set_state()
Temporarily activate or pause a timer.
timer_get_state()
Check the current state of a timer.
timer_exists()
Check whether a specified timer exists.
timer_destroy()
Destroy a specified timer.
timer_exceeded_frames()
Check whether a timer has exceeded a specified number of frames.
Time Dilation
dilation_set_speed()
Set the game speed instantly or transition over a number of real frames.
dilation_get_speed()
Get the current game speed.
dilation_is_transitioning()
Check whether a speed transition is currently taking place.
dilation_get_target_speed()
Get the speed that the game is currently transitioning towards.
Image Speed
Object animation speeds must also be updated so that animations look correct at the current game speed.
image_speed_set()
Set the image speed an object should use at 100% game speed.
image_speed_step()
Apply the speed adjustment. This is typically called once from the object's Step event.
image_speed_set() may be called from anywhere. image_speed_step() is usually best called once per Step event.Alarm Function Arguments
Arguments marked Optional do not need to be supplied.
alarm_set_ext()
| Argument | Required | Description |
|---|---|---|
_name | Yes | A string used to label the purpose of the alarm. |
_frames | Yes | The number of frames the alarm should last when game speed is 100% (1). |
_instance_id | Optional | The instance ID that the alarm belongs to. |
_repeat_count | Optional | How many times the alarm repeats before being destroyed. |
_func | Optional | The function, or variable referencing a function, that triggers when the alarm reaches zero. |
_args | Optional | An array of arguments passed into the triggered function. |
_works_when_paused | Optional | Whether the alarm continues ticking and can trigger while the game is paused. |
_state | Optional | The initial state: alarm_state.paused or alarm_state.active. |
alarm_get_ext(), alarm_get_state(), alarm_exists() and alarm_destroy()
| Argument | Required | Description |
|---|---|---|
_name | Yes | The name assigned to the alarm. |
_instance_id | Optional | The instance ID that the alarm belongs to. |
alarm_set_state()
| Argument | Required | Description |
|---|---|---|
_name | Yes | The name assigned to the alarm. |
_state | Yes | The desired state: alarm_state.active or alarm_state.paused. |
_instance_id | Optional | The instance ID that the alarm belongs to. |
Timer Function Arguments
timer_create()
| Argument | Required | Description |
|---|---|---|
_name | Yes | A string used to label the purpose of the timer. |
_instance_id | Optional | The instance ID that the timer belongs to. |
_works_when_paused | Optional | Whether the timer continues counting while the game is paused. |
_state | Optional | The initial state: timer_state.paused or timer_state.active. |
timer_get_elapsed(), timer_get_state(), timer_exists() and timer_destroy()
| Argument | Required | Description |
|---|---|---|
_name | Yes | The name assigned to the timer. |
_instance_id | Optional | The instance ID that the timer belongs to. |
timer_set_state()
| Argument | Required | Description |
|---|---|---|
_name | Yes | The name assigned to the timer. |
_state | Yes | The desired state: timer_state.active or timer_state.paused. |
_instance_id | Optional | The instance ID that the timer belongs to. |
timer_exceeded_frames()
| Argument | Required | Description |
|---|---|---|
_name | Yes | The name assigned to the timer. |
_frames | Yes | The elapsed frame count to compare against. |
_instance_id | Optional | The instance ID that the timer belongs to. |
Time Dilation and Image Speed Arguments
dilation_set_speed()
| Argument | Required | Description |
|---|---|---|
_game_speed | Yes | The desired game speed. 0 is frozen, 1 is normal speed and 2 is double speed. |
_frames | Optional | The number of real frames used to transition to the desired speed. |
image_speed_set()
| Argument | Required | Description |
|---|---|---|
_image_speed | Yes | The image speed the object would use when game speed is 100%. |
Usage Examples
The following examples show common game scenarios for every Matrix Engine function. Function and timer names are only examples and may be replaced with names that suit your project.
Alarm Management Examples
alarm_set_ext()
Enemy area scan
alarm_set_ext("scan", MATRIX_SEC * 5, id, 3, scan_area);
This instance executes scan_area() every 5 in-game seconds. It triggers 3 times before the alarm is destroyed.
Delayed boss attack with arguments
alarm_set_ext(
"meteor_strike",
MATRIX_SEC * 2,
id,
0,
launch_meteor,
[target_x, target_y]
);
After 2 in-game seconds, the boss calls launch_meteor(target_x, target_y) once.
alarm_get_ext()
Display a reload countdown
var _frames_remaining = alarm_get_ext("shotgun_reload", id);
var _seconds_remaining = ceil(_frames_remaining / MATRIX_SEC);
Gets the remaining reload duration so it can be displayed as seconds in the user interface.
Warn before a shield expires
if (alarm_get_ext("energy_shield", id) <= MATRIX_SEC)
{
shield_flash_warning = true;
}
Starts a warning effect when the shield alarm has 1 in-game second or less remaining.
alarm_set_state()
Pause a hacking sequence
alarm_set_state("hack_complete", alarm_state.paused, id);
Pauses the hacking alarm without removing its remaining duration.
Resume a trap countdown
alarm_set_state("floor_trap", alarm_state.active, id);
Reactivates a previously paused floor-trap alarm.
alarm_get_state()
Check whether crafting is paused
if (alarm_get_state("craft_item", id) == alarm_state.paused)
{
draw_text(x, y - 32, "Crafting Paused");
}
Checks the current state before displaying a paused message.
Only allow another pause while active
if (alarm_get_state("door_close", id) == alarm_state.active)
{
alarm_set_state("door_close", alarm_state.paused, id);
}
Confirms the door alarm is active before pausing it.
alarm_exists()
Prevent duplicate enemy spawns
if (!alarm_exists("spawn_reinforcements", id))
{
alarm_set_ext("spawn_reinforcements", MATRIX_SEC * 8, id, 0, spawn_squad);
}
Creates the reinforcement alarm only when an alarm with that name and instance ID does not already exist.
Show whether a buff is active
rage_buff_active = alarm_exists("rage_buff", id);
Uses the alarm's existence as a simple way to track whether the rage buff is still active.
alarm_destroy()
Cancel a self-destruct sequence
alarm_destroy("self_destruct", id);
Immediately removes the self-destruct alarm.
Remove a cancelled quest reminder
if (quest_cancelled)
{
alarm_destroy("quest_reminder");
}
Destroys a global quest reminder when the related quest is cancelled.
alarm_check_game_paused()
Check the current pause condition
if (alarm_check_game_paused())
{
draw_text(32, 32, "Paused");
}
Returns true when the pause condition used by the Matrix Engine is currently met.
Timer Management Examples
timer_create()
Track survival time
timer_create("survival_time");
Creates a global timer that begins counting active in-game frames immediately.
Track a player's sprint duration
timer_create("sprint_duration", id);
Creates an instance-specific timer for the player currently sprinting.
timer_get_elapsed()
Display survival seconds
var _survival_seconds = floor(timer_get_elapsed("survival_time") / MATRIX_SEC);
Converts the timer's elapsed frames into whole in-game seconds.
Scale a charge attack
var _charge_frames = timer_get_elapsed("charge_attack", id);
charge_power = clamp(_charge_frames / (MATRIX_SEC * 3), 0, 1);
Increases charge power over 3 in-game seconds.
timer_set_state()
Pause dialogue reading time
timer_set_state("dialogue_read_time", timer_state.paused);
Pauses the timer without losing the time already accumulated.
Resume a race timer
timer_set_state("lap_time", timer_state.active, id);
Resumes the lap timer for the specified racer instance.
timer_get_state()
Show whether a lap timer is running
lap_timer_running =
timer_get_state("lap_time", id) == timer_state.active;
Stores whether the racer's lap timer is currently active.
Resume a paused mission clock
if (timer_get_state("mission_clock") == timer_state.paused)
{
timer_set_state("mission_clock", timer_state.active);
}
Checks the timer's state before resuming it.
timer_exists()
Create a combo timer once
if (!timer_exists("combo_time", id))
{
timer_create("combo_time", id);
}
Prevents a new combo timer from being created every frame.
Check whether a speedrun has started
speedrun_active = timer_exists("speedrun_clock");
Stores whether the global speedrun timer currently exists.
timer_destroy()
Clear a completed lap timer
timer_destroy("lap_time", id);
Removes the timer after the racer completes the lap.
Reset a failed challenge
if (challenge_failed)
{
timer_destroy("no_damage_challenge");
}
Destroys the global challenge timer when the challenge fails.
timer_exceeded_frames()
Finish a weapon reload
if (timer_exceeded_frames("rocket_reload", MATRIX_SEC * 8, id))
{
rocket_ready = true;
}
Makes the rocket launcher available after 8 active in-game seconds.
Unlock a survival achievement
if (timer_exceeded_frames("survival_time", MATRIX_SEC * 300))
{
achievement_unlock("survive_five_minutes");
}
Unlocks an achievement after the player survives for 5 in-game minutes.
Time Dilation Examples
dilation_set_speed()
Instant slow motion
dilation_set_speed(0.25);
Immediately slows the game to 25% speed.
Smooth return to normal speed
dilation_set_speed(1, MATRIX_SEC * 2);
Transitions back to normal speed over 2 real-time seconds.
dilation_get_speed()
Display the current speed percentage
var _speed_percent = round(dilation_get_speed() * 100);
draw_text(32, 32, "Game Speed: " + string(_speed_percent) + "%");
Converts the current speed multiplier into a percentage for the user interface.
Play an effect while time is slowed
if (dilation_get_speed() < 1)
{
draw_set_alpha(0.35);
draw_rectangle(0, 0, room_width, room_height, false);
draw_set_alpha(1);
}
Draws a visual overlay whenever the game is running below normal speed.
dilation_is_transitioning()
Disable repeated transition input
if (!dilation_is_transitioning())
{
dilation_set_speed(0.4, MATRIX_SEC);
}
Starts a new slow-motion transition only when another transition is not already in progress.
Show a transition indicator
if (dilation_is_transitioning())
{
draw_text(32, 64, "Time speed changing...");
}
Displays a message while the current speed is moving towards its target.
dilation_get_target_speed()
Display the target speed
var _target_percent = round(dilation_get_target_speed() * 100);
draw_text(32, 96, "Target Speed: " + string(_target_percent) + "%");
Displays the speed currently being approached by a transition.
Choose an appropriate sound effect
if (dilation_get_target_speed() < dilation_get_speed())
{
audio_play_sound(snd_time_slow, 0, false);
}
else
{
audio_play_sound(snd_time_restore, 0, false);
}
Chooses a sound depending on whether time is transitioning slower or faster.
Image Speed Examples
image_speed_set()
Set a walk animation
image_speed_set(0.2);
Set the image speed of an object to 0.2 at normal game speed and applies the current time-dilation multiplier.
Change animation speed by state
if (is_sprinting)
{
image_speed_set(0.35);
}
else
{
image_speed_set(0.15);
}
Updates the intended animation speed without assigning directly to GameMaker's built-in image_speed variable.
image_speed_step()
Keep a player animation synchronised
Step Eventimage_speed_step();
Reapplies the current time-dilation speed every frame. Should be called in the step event of an object (or parent object) that animates
Synchronise only while animated
Step Eventif (sprite_get_number(sprite_index) > 1)
{
image_speed_step();
}
Updates time-scaled image speed only when the current sprite contains more than one frame.
Object Movement and Image Speed
Time-scaled movement
Any movement that should respond to time dilation must be multiplied by SPEED_MOD. This macro represents global.speed_modifier.
At normal speed, SPEED_MOD is 1. At half speed it is 0.5, and at double speed it is 2. Multiplying movement by this value keeps objects moving at the same relative rate as the rest of the game.
Horizontal movement
Step Eventx += move_speed * SPEED_MOD;
The object moves by its regular movement speed multiplied by the current game-speed modifier.
Directional movement
Step Eventx += lengthdir_x(move_speed, direction) * SPEED_MOD;
y += lengthdir_y(move_speed, direction) * SPEED_MOD;
Both movement axes are scaled so the object remains synchronised during slow motion, pauses and increased speed.
Time-scaled image speed
Whenever an instance's animation speed should respond to time dilation, use image_speed_set(_speed) instead of assigning directly to the built-in image_speed variable.
For example, replace:
image_speed = 0.2;
with:
image_speed_set(0.2);
The value supplied to image_speed_set() is the animation speed the object should use while the game is running at normal speed.
Any object whose animation is affected by time dilation must also call image_speed_step() once every frame. The object's Step event is usually the best location:
Apply animation time dilation
Step Eventimage_speed_step();
This ensures the instance's built-in image_speed continually matches the current game speed and keeps the animation visually synchronised as time dilation changes.
image_speed_set() whenever the object's intended animation speed changes, and call image_speed_step() from code that runs once every frame.
Additional Information
- Do not set any Matrix Engine alarms or timers before
obj_alarm_managerhas been created. - When assigning an alarm or timer to an object, pass the actual instance ID rather than only the object asset name. Otherwise, the alarm or timer may be applied to the wrong instance of that object type.
Additional Information for HTML Builds
In an HTML build, the game may freeze when executing a fallback check that uses:
if (asset_get_type(_instance_id) == asset_object)This fallback is used in case an object type was passed instead of an instance ID. For HTML builds, comment out blocks that use this check.
After removing the fallback, ensure that every function receives a valid instance ID whenever an instance-specific alarm or timer is required.
Fat Pint Games