How to Create a Health System in GameMaker: Beginner Guide

A health system is one of those game mechanics that looks simple until you start building it. The player has a number, an enemy reduces that number, and the character is defeated when it reaches zero. Easy, right?

The basic idea is simple, but a good health system also needs limits, damage protection, healing, visual feedback, and reliable death logic. Without those details, the player might take damage every frame, heal beyond their maximum health, or continue moving after being defeated.

Learning how to create a health system in GameMaker gives you a useful foundation for action games, platformers, RPGs, shooters, and survival projects. The same structure can also be adapted for enemy health, shields, stamina, armour, and destructible objects.

In this tutorial, you will create health variables, reusable damage and healing functions, temporary invincibility, a graphical health bar, and a simple defeat state. The examples use beginner-friendly GML that you can expand as your project becomes more advanced.

Plan the Main Health Variables

A reliable system begins with clearly named variables. At minimum, the player needs a current health value and a maximum health value.

Open your player object and add this code to its Create Event:

max_hp = 100;
hp = max_hp;

is_dead = false;
is_invincible = false;
invincibility_steps = 30;

The Create Event runs when an instance is first created, so it is a natural place to initialise values that the instance will use throughout the game. Variables assigned inside an object event become instance variables by default, meaning every player or enemy instance can hold its own health.

Using both hp and max_hp makes the system easier to expand. You can increase the maximum value after an upgrade while still tracking how much health remains.

The Boolean variables record whether the player is dead or temporarily protected from further damage. These states will prevent several common problems later.

Create a Reusable Damage Function

You could subtract health directly inside every enemy or projectile, but that quickly creates duplicated code. A reusable function keeps all damage rules in one place.

Add this function to the player’s Create Event:

take_damage = function(_amount)
{
    if (is_dead || is_invincible)
    {
        return;
    }

    hp = clamp(hp - _amount, 0, max_hp);

    is_invincible = true;
    alarm[0] = invincibility_steps;

    if (hp <= 0)
    {
        is_dead = true;
    }
};

The function first checks whether damage should be ignored. If the player is already dead or temporarily invincible, return ends the function immediately.

The clamp() function keeps a value within a defined minimum and maximum. Here, it prevents health from dropping below zero or rising above max_hp.

You can now damage the player from anywhere that has access to its instance:

take_damage(20);

This structure is easier to maintain than repeating the same health checks in hazards, enemies, projectiles, traps, and environmental effects.

Apply Damage Through Collision Events

A straightforward way to deal contact damage is through a Collision Event. Create an enemy object called obj_enemy and give it a damage value in its Create Event:

contact_damage = 15;

Add a Collision Event with obj_enemy to the player object:

take_damage(other.contact_damage);

Inside a Collision Event, other refers to the specific instance involved in the contact. This allows the player to read the damage value from the exact enemy it touched.

This also means different enemies can use different amounts:

// Small enemy
contact_damage = 10;

// Strong enemy
contact_damage = 30;

Traditional GameMaker collisions depend on the collision masks of the two instances. If one object has no valid mask, the collision will not be detected even when its artwork appears to overlap another object.

Test the masks carefully so players do not receive damage from empty transparent areas around an enemy sprite.

Add Temporary Invincibility After Damage

Without protection, a player touching an enemy could lose health during every game step. A character with 100 HP might therefore be defeated almost instantly.

The damage function already solves part of this problem by setting:

is_invincible = true;
alarm[0] = invincibility_steps;

Now add an Alarm 0 Event to the player object:

is_invincible = false;
image_alpha = 1;

GameMaker instances contain 12 alarms. When an alarm is given a positive value, it counts down in game steps and runs its corresponding Alarm Event when it reaches zero.

If your game runs at 60 steps per second, 30 steps represents roughly half a second. You can increase or decrease invincibility_steps depending on how forgiving the combat should feel.

Give the player simple visual feedback in the Step Event:

if (is_invincible)
{
    image_alpha = 0.5;
}
else
{
    image_alpha = 1;
}

The sprite becomes partially transparent while protected. You could later replace this with flashing, colour changes, knockback, sound, particles, or controller vibration.

Create a Healing Function and Health Pickups

Healing should follow the same organised approach as damage. Add another function to the player’s Create Event:

heal = function(_amount)
{
    if (is_dead)
    {
        return;
    }

    hp = clamp(hp + _amount, 0, max_hp);
};

Because the result is clamped, collecting a large health item cannot push the player beyond the maximum.

Create an object called obj_health_pickup and add a Collision Event with the player:

other.heal(25);
instance_destroy();

The pickup restores 25 HP and then removes itself from the room.

You may also want to prevent full-health players from wasting an item:

if (other.hp < other.max_hp)
{
    other.heal(25);
    instance_destroy();
}

This small rule gives the player more control over valuable resources. In a fast arcade game, automatic collection may feel better, while a survival game may benefit from preserving unused healing items.

Draw a Health Bar on the Interface

Players need clear information about their condition. GameMaker provides draw_healthbar(), which displays a value expressed as a percentage between zero and 100.

Add a Draw GUI Event to the player object:

var _hp_percentage = (hp / max_hp) * 100;

draw_healthbar(
    32,
    32,
    232,
    52,
    _hp_percentage,
    c_black,
    c_red,
    c_lime,
    0,
    true,
    true
);

The first four values define the bar’s rectangular area. The percentage determines how full it appears, while the colour arguments create a transition from red at low health to green at full health.

The direction value of 0 anchors the bar on the left. The last two Boolean values enable its background and border. The function can also represent mana, stamina, shields, or another percentage-based resource.

You can display the numerical value below it:

draw_text(32, 60, string(hp) + " / " + string(max_hp));

For games with multiple rooms, consider placing interface drawing in a dedicated controller object. This prevents the user interface from becoming mixed with player movement and combat code.

Handle Player Death Cleanly

Setting is_dead to true prevents further damage, but the game still needs to decide what defeat means.

A simple Step Event check could look like this:

if (is_dead)
{
    image_speed = 0;
    exit;
}

The exit keyword stops the rest of the current event, preventing normal movement and attack code from running after death.

For a basic prototype, you could restart the room after a short delay. Set another alarm when the player dies:

if (hp <= 0)
{
    is_dead = true;
    alarm[1] = 90;
}

Then add this to Alarm 1:

room_restart();

A more developed game might play a defeat animation, remove player control, show a game-over menu, reload a checkpoint, or deduct a life.

Avoid placing the death response in several different locations. The health system should have one clear path from taking damage to reaching zero and entering the defeated state.

Test and Improve the System

Begin by testing obvious situations. Confirm that damage reduces health, invincibility prevents repeated hits, healing stops at the maximum, and death occurs exactly at zero.

Next, test awkward cases. What happens when the player touches two enemies simultaneously? Can a health pickup be collected during the death animation? Does the health bar still work after increasing max_hp?

Use temporary debug messages when behaviour is unclear:

show_debug_message(
    "HP: " + string(hp)
    + " | Invincible: " + string(is_invincible)
);

Once the basic system works, consider adding damage types, armour, critical hits, regeneration, shields, elemental resistance, or difficulty-based modifiers.

Do not add every advanced mechanic immediately. A small, dependable health system is more useful than a complicated one filled with overlapping rules and difficult-to-find bugs.

A basic GameMaker health system needs more than a variable that decreases. A dependable setup includes current and maximum HP, reusable damage and healing functions, value limits, temporary invincibility, visual feedback, and a clear death state.

Using clamp() prevents invalid values, Collision Events connect hazards to the player, and alarms provide a practical way to control brief recovery periods. A GUI health bar then communicates the player’s condition without exposing the underlying code.

Create the basic version first and test every interaction carefully. Once damage, healing, and death behave consistently, add your own effects, animations, pickups, armour, or checkpoint rules. That solid foundation will support almost any combat-focused 2D game.