Recommended resources
The freely available resources this lesson will direct you to.
Hello! Welcome to the next lesson in our JRPG development journey.
In the previous lesson, we gave our EventInterpreter a memory by implementing a system for game flags (switches and variables). It can now intelligently select which event "page" to run based on the game's state. However, we left a crucial question unanswered: what actually starts an event?
Today, we will bridge that gap. This lesson directly addresses the learning outcome: Implement event triggers for player interaction, automatic execution, and map entry. We will build the mechanisms that connect our event system to the game world, allowing events to be triggered in various ways, just like in classic JRPGs. This is the final architectural piece needed to create a fully interactive world.
1. A Conceptual Model for Event Triggers
Before we write any code, let's establish the "what" and "why" of event triggers, using RPG Maker's battle-tested design as our model. An event's trigger defines the condition that causes its command list to execute.
To get a clear overview of these concepts, please read the following article from the official RPG Maker blog. It provides excellent descriptions of the primary trigger types we'll be implementing.
This blog post, 'Event Priorities and Triggers', clearly explains the different ways an event can be activated. It's the perfect conceptual foundation for the system we're about to build.
Please read the sections titled 'What does the Event Trigger do?', 'action button', 'player touch', 'event touch', 'Autorun', and 'parallel'. Focus on the use case for each trigger type.
To summarize and structure our implementation, we can categorize these triggers into three main groups:
| Trigger Type | Activation Condition | Common Use Cases | Player Control |
|---|---|---|---|
| Action Button | Player faces an event and presses the action key (e.g., Z). | Talking to NPCs, opening chests, inspecting objects. | Maintained |
| Player/Event Touch | Player or Event moves onto the other's tile. | Doors, traps, map transitions, area-based cutscenes. | Maintained |
| Autorun | Event page conditions are met (e.g., upon map entry). | Cutscenes, story sequences. | Blocked |
| Parallel | Event page conditions are met. Runs continuously. | Timers, checking complex quest conditions. | Maintained |
The key difference between Autorun and Parallel is that Autorun events freeze player control until they are finished, making them ideal for cutscenes. Parallel events run in the background while the player retains control.

2. Architectural Design: Integrating Triggers into the Game Loop
Now, let's design how these trigger checks fit into our existing engine architecture. Your experience with front-end frameworks and their update cycles will be very relevant here. We'll be hooking into specific moments within our main game loop to check for these trigger conditions.
Our Game class's game loop looks something like this:
// A simplified view of our game loop
gameLoop(timestamp) {
// ... calculate delta time ...
this.update(deltaTime); // Update game state
this.render(deltaTime); // Draw everything
requestAnimationFrame(this.gameLoop.bind(this));
}
The update method is where our new logic will live. It's responsible for processing input, moving characters, and—now—checking for event triggers.
Here is the logic flow we will implement:
- Is an event already running? If a blocking event (like an Autorun) is active, we should pause most other game updates (like player movement).
- Process Player Input & Movement: Handle the player's directional and action key presses.
- Check for Action Button Triggers: If the action button was just pressed, check the tile in front of the player for an event with an "Action Button" trigger.
- Check for Touch Triggers: After the player successfully moves to a new tile, check that tile for any events with a "Player Touch" trigger.
- Update Parallel Events: Continuously run any active parallel events.
3. Implementation: Coding the Trigger Logic
Let's translate the design into code. We'll need to modify our Game class and introduce a way to manage events on the map.
Step 1: Update the Event Data
First, we need to add the trigger type to our event JSON structure. Let's update the treasure chest example from our last lesson.
// In your events.json file for a specific map
{
"id": 5,
"x": 10,
"y": 8,
"pages": [
// ... pages from last lesson
],
"trigger": "action_button" // <-- NEW PROPERTY
}
Possible values will be action_button, player_touch, autorun, and parallel.
Step 2: Enhancing the Game Map and Game Loop
Our Game_Map needs to be aware of the events on it so we can query them by location. The main Game class will orchestrate the checks.
// Game_Map.js (Conceptual)
class Game_Map {
// ...
constructor(mapData) {
// ...
this._events = mapData.events; // Load events from our JSON data
}
eventsAt(x, y) {
return this._events.filter(event => event.x === x && event.y === y);
}
autorunEvents() {
return this._events.filter(event => event.trigger === 'autorun');
}
}
// Game.js - The main class
class Game {
constructor() {
// ... from previous lessons
this.map = new Game_Map(mapData);
this.player = new Player(...);
this.interpreter = new EventInterpreter(this);
}
update(deltaTime) {
// If a blocking event is running, only update the interpreter
if (this.interpreter.isRunning()) {
// The interpreter updates itself internally (e.g., waits, processes)
return;
}
// Otherwise, run the normal game update
this.player.update(deltaTime); // Handles input and movement
// After player logic, check for triggers
this.checkActionTrigger();
// Note: The touch trigger check should be called from the player's
// movement code, specifically when a move to a new tile is completed.
}
checkActionTrigger() {
// Assume an InputManager tells us if the action button was just pressed
if (Input.isTriggered('action')) {
const player = this.player;
const targetPos = player.getFrontPosition(); // A helper to get {x, y} based on direction
const events = this.map.eventsAt(targetPos.x, targetPos.y);
const actionEvent = events.find(e => e.trigger === 'action_button');
if (actionEvent) {
// We found an event to run!
// The 'actionEvent' here is the JSON definition.
// We also pass the event object itself as the instance context
// for self-switches, as we designed in the last lesson.
this.interpreter.start(actionEvent, actionEvent);
}
}
}
// This method should be called by the player class upon completing a move to (x, y)
checkTouchTrigger(x, y) {
if (this.interpreter.isRunning()) return; // Don't trigger new events if one is running
const events = this.map.eventsAt(x, y);
const touchEvent = events.find(e => e.trigger === 'player_touch');
if (touchEvent) {
this.interpreter.start(touchEvent, touchEvent);
}
}
// This is called once when the map is first loaded
setupMapEvents() {
// Find the first valid autorun event
const autorunEvent = this.map.autorunEvents().find(event => {
const page = this.interpreter.findValidPage(event, event);
return page !== null;
});
if (autorunEvent) {
this.interpreter.start(autorunEvent, autorunEvent);
}
// We would also initialize parallel events here, but we'll defer their
// complex implementation for now to focus on the core triggers.
}
}
Step 3: Map Entry and Automatic Triggers
The "map entry" trigger is simply an Autorun event. When we load a new map, we'll call a setupMapEvents method. This method will scan for any Autorun events on the map. If it finds one whose conditions are met, it will immediately start the interpreter.
// In your Scene_Map class, or wherever you handle map loading
async function loadMap(mapId) {
const mapData = await AssetManager.loadMap(mapId);
// ... create game objects ...
const game = new Game(mapData);
// After everything is set up, check for autorun events
game.setupMapEvents();
// ... start the game loop ...
}
This simple addition gives us powerful functionality for cutscenes that start the moment you enter a town, a dungeon, or a new room.

Test your understanding!
Imagine a tile on your map has two events on it:
- An invisible event with a
player_touchtrigger (like a trap). - A visible statue event with an
action_buttontrigger.
The player character walks onto this tile. What happens? Then, while standing on the tile, the player presses the action button. What happens? Describe the sequence of checks the engine performs.
Show answer
-
Player walks onto the tile:
- The player's movement logic finishes, updating their position to the new tile's coordinates.
- The
checkTouchTrigger(x, y)method is called. - It finds the invisible event with the
player_touchtrigger. - The
EventInterpreterstarts running the "trap" event. The statue is ignored because its trigger isaction_button.
-
Player presses the action button (after the trap event is finished):
- The player is standing on the tile, but their facing direction matters. Let's assume they are facing away from the statue's tile.
checkActionTriggeris called. It calculates thetargetPosin front of the player.- Since the player is facing away,
targetPosis a different tile. The method finds no events there. Nothing happens. - Now, assume the player turns to face the statue and presses the action button.
checkActionTriggeris called.targetPosnow matches the statue's coordinates.- The method finds the statue event and confirms its trigger is
action_button. - The
EventInterpreterstarts, running the statue's dialogue. The invisible event is ignored because its trigger isplayer_touch.
Conclusion
Congratulations! You have now implemented the complete trigger system for your JRPG engine. By hooking into specific moments of the game loop—input processing, movement completion, and map loading—we can initiate scripted events in a variety of powerful and intuitive ways.
Key Takeaways:
- Event Triggers are properties (
action_button,player_touch,autorun) that define how an event is started. - Action Button triggers are checked after player input, by looking at the tile in front of the player.
- Touch triggers are checked after a character completes a move onto a new tile.
- Autorun triggers are checked once upon map load, providing the perfect mechanism for entry cutscenes. They typically block player control.
- Our
EventInterpreteris now fully integrated into the game loop, acting as the engine for all scripted sequences, which are now activated by player actions and world state.
Preview of the next lesson:
We now have all the necessary components: an event data structure, a stateful interpreter that can make decisions, and a trigger system to start events. In the next lesson, "Script a treasure chest event that uses a game flag to prevent re-opening," we will put all these pieces together. You will write the JSON data for a classic treasure chest, combining an action_button trigger with a self_switch to create your first complete, stateful, and interactive object.