Chapter Events
Chapter Events are scripted sequences that trigger when a Player reaches a certain spot on the map.
Goals
Facilitate the creation of narrative scenes with reusable actions and a flexible structure.
How to create a Chapter Event
The first step to creating a Chapter Event is to create a ChapterEventPlayer
and a ChapterEventTrigger
.
All levels contain a proposed structure for setting up Narrative nodes:
- Level
- Narrative
- Triggers: where
ChapterEventTrigger
s should be set - Paths: where
Path2D
s should be set - Players: where
ChapterEventPlayer
s should be set
- Triggers: where
- Narrative
ChapterEventPlayer
To create a ChapterEventPlayer
, simply add a new Node to the scene and choose ChapterEventPlayer
.
In the inspector, you may change some of this event's behavior by modifying these properties:
- ID: Must be a unique id (unique in the whole game), as it identifies the event in the player's save data. Following the placeholder suggestion (
chapter_00/room_00/scene_00
) should be enough; - Play Once: If true, this event will never trigger again after finished;
- Is Interactive: If true, Player can interact (move, attack, etc.) during the event. If false, Player input is blocked and cutscene vignettes are triggered;
- Trigger Path: Must point to this event's trigger.
Every child of this node must be a type of Event.
ChapterEventTrigger
To create a ChapterEventTrigger
, instantiate it from the scene res://src/Narrative/ChapterEvent/ChapterEventTrigger.tscn
- this scene has the base class with proper collision set.
You will need to add a CollisionShape2D
node as child of the Trigger and set the area for triggering that event.
Adding Events
Events are added as nodes to the ChapterEventPlayer
. You can see the full list of Events by opening the Create New Node dialog and searching for Event
.
Every class that is a child of BaseChapterEvent
can be used:
EventAddNode
Adds a Node to the scene. Expects a path to a placeholder Position2D node and a PackedScene to instantiate.
EventCameraMove
Moves GameplayCamera along a curve. Expects a Path2D node that defines the movement.
EventCameraReturnToPlayer
Returns GameplayCamera to Player.
EventCameraSetPosition
Moves GameplayCamera instantanely to a set position. Expects a Node2D that defines the position.
EventChainParallel
Chains multiple events, playing them at the same time. Finishes when all events are finished.
Every child of this node must be a type of Event.
EventChainSequence
Chains multiple events, playing them in sequence, one after the other. Useful if you need to define a sequence inside of a EventChainParallel.
Every child of this node must be a type of Event.
EventNode2DMove
Moves any Node2D along a curve. Expects a Path2D node that defines the movement. Meant to be used to prototype scene while we don't have NPC or Enemy events.
EventPlayInnerMonologue
Plays an inner monologue.
EventPlayerMove
Moves the Player along a curve. Expects a Path2D node that defines the movement. Player will walk at its own speed.
EventRemoveNode
Removes a Node from the scene.
EventWaitForTimer
Waits for a timer.
How it works
A ChapterEventPlayer
is responsible for executing a sequence of events in order.
It must be connected to a ChapterEventTrigger
. When its triggered
signal is emitted, the ChapterEventPlayer
will play its sequence.
When playing, ChapterEventPlayer
iterates through its child nodes (each should extend BaseChapterEvent
) and executes them.
If an event isn't finished, the system will wait for a finished
signal from the event node before proceeding to the next.
Both ChapterEventPlayer and BaseChapterEvent have started
and finished
signals if you need to track their execution.
All event scripts must have a custom class name - so that they are exposed in the "Create New Node" dialog - and should start with Event...
.
Creating a Custom Event
To create a new event, extend BaseChapterEvent
and implement your _play()
method.
This method must return true
or false
, signaling if the event is completed.
Example of an instantaneous event:
class_name EventRemoveNode
extends BaseChapterEvent
export var node_path: NodePath
func _play() -> bool:
var node: Node = get_node(node_path)
node.get_parent().remove_child(node)
node.queue_free()
return true
If an event performs an action that takes time (e.g., animations), it must return false
and call _finished()
when complete.
Example of an asynchronous event:
class_name EventWaitForTimer
extends BaseChapterEvent
export var duration: float = 1.0
func _play() -> bool:
var timer := get_tree().create_timer(duration)
timer.connect("timeout", self, "_on_timer_timeout", [], CONNECT_ONESHOT)
return false
func _on_timer_timeout() -> void:
_finished()
With what script it communicates
Events can control: Player, GameplayCamera
Events can trigger: Inner Monologue
ChapterEventPlayer interacts with SaveDataHandler, to save if an event has been played yet.