Game in Game Lesson
How to create a game within a game using the Game Engine.
How the Game-in-Game Feature Works
One of the most interesting parts of this project is the game-in-game system: the ability to launch a completely new game level from inside an existing one. Think of it like opening a door and stepping into a new world, then returning to where you started when you’re done.
How it Works
The game is built around a class called GameControl. Every time the game runs a level, it uses a GameControl instance to manage it. The game-in-game trick works by creating a second GameControl instance while the first one is still running, like one game nested inside another.
Below is the full level chain in our game:
GameLevelMaze
└── GameLevelMazeSub (climb the platforms)
└── GameLevelDoors (pick the right door)
└── GameLevelForest
└── GameLevelForestSub (the fork in the path)
├── GameLevelForestDeath (went left, bad ending)
└── GameLevelForestWin (went right, good ending)
Each indented level is launched from inside the level above it, triggered by talking to an NPC and selecting a dialog option.
Step-by-Step: What Happens When You Enter a Sublevel
Every transition in the game follows the same 5-step pattern. Here’s the code from the Gate Keeper NPC in the first level, GameLevelMaze, broken down:
Step 1: Fade to black
A black <div> is created and placed over the entire page, then faded in. This hides the level swap happening underneath so the player doesn’t see an empty transition.
const fade = document.createElement('div');
Object.assign(fade.style, {
position: 'fixed', top: '0', left: '0',
width: '100%', height: '100%',
backgroundColor: '#000', opacity: '0',
transition: 'opacity 0.8s ease-in-out',
zIndex: '9999', pointerEvents: 'none'
});
document.body.appendChild(fade);
requestAnimationFrame(() => { fade.style.opacity = '1'; ... });
Step 2: Pause the parent game
The currently running game is paused so it stops updating in the background, preserving the player’s data and position. Without this, both games would be running at the same time and fighting over the canvas and could lead to issues.
primaryGame.pause();
Step 3: Launch a new GameControl
A new GameControl is created with just the sublevel’s class and started. Notice the parentControl option, which saves a reference back to the parent game so we can return to it later.
const gameInGame = new GameControl(gameEnv.game, [GameLevelMazeSub], {
parentControl: primaryGame
});
gameInGame.start();
Step 4: Returning to the parent game
When the sublevel eventually ends, gameOver is called automatically. The code overrides it here to resume the parent game instead of just stopping.
gameInGame.gameOver = function() {
primaryGame.resume();
};
Step 5: Fade back in
The black overlay fades back out, revealing the new sublevel to the player.
The parentControl Chain
As the game goes deeper (maze → doors → forest → fork), each new sublevel stores a reference to the one above it via parentControl. This is important because deeper sublevels need to reach all the way back to the top-level game to do certain transitions.
You can see this in GameLevelMazeSub when transitioning to GameLevelDoors:
const topGame = primaryGame?.parentControl || primaryGame;
topGame.levelClasses = [GameLevelDoors];
topGame.transitionToLevel();
The ?. is optional chaining, as it safely checks if parentControl exists before trying to use it. If it doesn’t exist, it just uses primaryGame directly. This prevents crashes when the code runs at the top level where there is no parent.
Why This Matters
This pattern of launching a child process from a parent, then returning control when it finishes is actually a fundamental concept in computer science. You see it in:
- Operating systems: when a program launches a subprocess
- Recursive functions: a function that calls itself and returns when done
In our game, GameControl is doing the same thing. The parent pauses and waits, the child runs to completion, and then control is handed back. It’s a clean, reusable pattern that each team member used independently to build their own level then chained together seamlessly using game-in-game features.