Quick answer: When you change rooms, GameMaker destroys every non-persistent instance and recreates them from the new room’s instance list. Mark the object Persistent in the object editor, or move the state into a persistent controller object or global variables.

You write a player object with a health variable, build two rooms, and use room_goto to walk from one to the other. The moment the player crosses into the new room, their health resets to maximum, their inventory empties, and their score goes back to zero. The bug is not your code — it is GameMaker’s default behavior, and the fix is one checkbox once you know where to look.

The Symptom

You set instance variables in the player’s Create event and modify them throughout gameplay. Then you call room_goto(rm_level_2) and:

What Is Actually Happening

GameMaker rooms are not Unity scenes that you load and unload. A GameMaker room is more like a level definition: when you call room_goto, the runtime destroys every instance in the current room (firing their Destroy events), wipes the instance pool, and then creates new instances from the new room’s asset definition. Anything stored on a destroyed instance is gone.

If obj_player is placed in both rooms, the room change creates a brand new obj_player in the second room, and the new instance has whatever variables its Create event sets. The old instance — with the player’s actual progress — was destroyed.

This is intentional. GameMaker is a 2D engine designed for arcade-style games where each room is a self-contained level, and not having to manage object lifetimes manually saves a huge amount of book-keeping. But for any game where the player should keep their state across rooms, you have to opt in.

The Fix

Option 1: Mark the object Persistent.

Open the object in the IDE and check the Persistent checkbox in the object properties panel. Now when you change rooms, GameMaker will not destroy this instance — it will carry it across to the new room with its variables intact. The Create event runs exactly once for the lifetime of the game, no matter how many room transitions you do.

Important: do not place the persistent object in every room. Place it only in the first room (or spawn it via code). Otherwise the new room’s instance list will create a duplicate, and you will end up with two players, two health bars, and twice the bullets.

Option 2: Use a controller object.

For larger games, the cleanest pattern is to have one Persistent obj_game_controller that owns all global state. Every other object reads through it. This survives every room change and is easy to save and load.

// obj_game_controller: Create event
if (instance_number(obj_game_controller) > 1) {
    instance_destroy();
    exit;
}

player_health = 100;
player_max_health = 100;
player_gold = 0;
player_inventory = [];
current_quest = "intro_01";

The first three lines are critical: they enforce the singleton pattern, so even if you accidentally place the controller in two rooms, only one instance survives.

Then in obj_player:

// obj_player: Create event
// Read state from the controller, do not store our own
hp = obj_game_controller.player_health;

// obj_player: Step event
if (hp <= 0) {
    obj_game_controller.player_health = 0;
    room_goto(rm_game_over);
}

// obj_player: Destroy event (saves state before room transition)
obj_game_controller.player_health = hp;

Option 3: Use global variables for primitives.

If you only need to keep a couple of numbers, global variables are simpler than a controller object:

// Anywhere
global.player_gold += 50;
global.player_health -= 10;

Global variables persist for the lifetime of the game and survive every room change automatically. They are also easy to serialize for save files.

Persistent Rooms vs. Persistent Instances

GameMaker also lets you mark a room as Persistent in the room properties. This is different from marking an object Persistent. A persistent room saves the state of every instance in the room when you leave, and restores them exactly when you come back. Use this when you want a hub room (a town, a base, an overworld) to remember which NPCs are alive, which doors are open, and where the player left things.

Be careful: persistent rooms can balloon save file size if you keep a lot of instances. They also do not save instances that were spawned via code unless those objects exist in the room asset. For procedural content, you usually want explicit save/load logic instead.

Verifying the Fix

Add a Draw GUI event to the player that shows hp and obj_game_controller.player_health. Walk to a room transition trigger and watch the values cross the boundary. With the bug, both values reset. With Persistent enabled, the player’s hp stays the same. With the controller pattern, the controller value stays the same and the player reads it correctly on entry.

“Every GameMaker tutorial that does not mention the Persistent flag is leaving out the most important checkbox in the entire engine. If you do not understand it, your variables will keep resetting and you will keep being confused.”

Related Issues

For room creation code that does not run, see GameMaker room creation code not running. For broader save and load patterns, see GameMaker save best practices. For audio that breaks across rooms in addition to variables, check GameMaker audio not playing on Android export.

Persistent objects only run their Create event once. If you have logic that should run on every room entry, put it in the Room Start event — not Create.