Getting Started
The Visual Novel system is built into every ArcadeOn Engine project. You access it through the Visual State Machine (VSM) or the Event Editor by adding a Visual Novel Scene command.
Characters
Characters are defined at the top of each VN Scene. Each character has:
- ID — a short key like
aliceornarrator - Name — the display name shown in the dialogue box
- Portrait ID — an asset ID for the character's portrait image
- Color — a hex colour for the character chip in the editor
When you add a dialogue or choice node, you select a character from a dropdown. The system automatically uses their name and portrait. You can override the portrait per-node for different expressions.
Node Types
Every VN scene is a timeline of nodes that execute from top to bottom. Here are all the available node types:
| Node | Purpose |
|---|---|
| 💬 Dialogue | Show a line of text with speaker name, portrait, and optional background change. |
| ❓ Choice | Present branching options. Each option has its own nested sub-timeline of nodes. |
| 🔀 Branch | Conditional split — check a variable or switch and run Then or Else nodes. |
| 📊 Set Variable | Set, add, subtract, multiply, or divide a variable. Use for affinity scores. |
| 🚦 Set Switch | Toggle a boolean flag on or off. Use to track story decisions. |
| 🔊 Play Sound | Play a sound effect or start looping background music. |
| 🎬 Screen Effect | Fade in, fade out, or flash the screen for transitions and drama. |
| ⏳ Wait | Pause execution for a set number of seconds. |
| 🚪 Go To Scene | Transition to another scene (chapter, location, ending). |
| 📡 Emit Signal | Fire a signal to trigger other events elsewhere in the game. |
| 🏷 Label | Mark a point in the timeline that can be jumped to. |
| ↩ Jump | Jump execution to a named label for loops or branch rejoining. |
| 📝 Comment | Editor-only note — does nothing at runtime. |
Branching Choices
The Choice node is the heart of any visual novel. When the player reaches a choice node, they see a list of options with keyboard (Up/Down + Enter) and click navigation.
Each option has:
- Text — what the player sees on the button
- Value — a number stored in a variable (optional)
- Sub-nodes — a nested timeline that runs only if this option is chosen
After the chosen branch's sub-nodes finish, execution continues to the next node after the choice. This is the implicit rejoin pattern — branches automatically merge back without needing an explicit merge point.
Storing the player's selection
Set the Store In Variable field to save the selected option's value into a scene or global variable. You can check this variable later with a Branch node to create long-term story consequences.
Affinity & Relationship Tracking
There is no special "affinity system" — affinity is simply a global variable. This keeps things simple and flexible:
affinity_alice → Add (+) → 1 → Scope: Globalaffinity_alice → Subtract (-) → 1 → Scope: Globalaffinity_alice ≥ 5 → show the romance ending. Else → show the friendship ending.Using Global scope means the variable persists across scenes and is automatically included in save files.
Backgrounds & Portraits
Upload your background images and character portraits as assets in the engine's asset panel. Then reference them by asset ID in your VN nodes:
- Set a Background field on any dialogue node to change the scene backdrop
- Leave it blank to keep the current background
- Set it to
clearornoneto remove the background - Override a character's portrait on individual nodes for different expressions (happy, sad, angry)
Sound & Music
Use Play Sound nodes to control audio:
- Set Loop to
truefor background music that plays continuously - Leave Loop off for one-shot sound effects (door slam, phone ring)
- Adjust Volume from 0 to 1
Scene Transitions
For multi-chapter visual novels, use this pattern:
- Add a Screen Effect node — Fade Out to black
- Add a Go To Scene node — pick the target scene
The fade creates a clean visual break. The scene transition loads the new scene where another VN Scene command can continue the story.
Global variables and switches persist across scenes, so affinity scores, story flags, and inventory all carry over.
Save & Load
The VN system uses the engine's existing variable and switch storage, which is already captured by the Save Game and Load Game commands. All affinity scores, story flags, and choice results are included in save files automatically.
Add Save Game / Load Game commands in your event flow (outside the VN Scene) to give players save points between chapters.
Advanced Techniques
Labels & Jumps
For complex flows where you want branches to converge at a specific point, add a Label node and then Jump to it from inside branch sub-nodes. This is useful for "skip to common ending" patterns.
Integrating with Game Events
Use Emit Signal nodes to communicate with the rest of your game. For example, emit vn.chapter1.complete from the end of your VN scene, and have another entity listen for that signal to unlock a door or start an animation.
Mixing VN & Gameplay
The VN Scene is just a command — you can use it alongside any other command in the event system. A single entity can have a state that shows a VN dialogue, then transitions to a state with movement commands. This lets you build games that alternate between visual novel sections and gameplay sections.
Data Structure Reference
For advanced users, here's how the VN Scene is stored in the project JSON:
{
"type": "vnScene",
"id": "vn_abc123",
"characters": {
"alice": { "name": "Alice", "portraitId": "asset_portrait_alice", "color": "#e0b0ff" },
"bob": { "name": "Bob", "portraitId": "asset_portrait_bob", "color": "#80d0ff" }
},
"nodes": [
{ "nodeType": "dialogue", "characterId": "alice", "text": "Hello!", "backgroundId": "asset_bg_park" },
{ "nodeType": "choice", "characterId": "alice", "text": "What will you do?",
"storeVar": "ch1_choice", "storeScope": "global",
"options": [
{ "text": "Help her", "value": 1, "nodes": [
{ "nodeType": "setVar", "id": "affinity_alice", "op": "add", "value": 1, "scope": "global" },
{ "nodeType": "dialogue", "characterId": "alice", "text": "Thank you!" }
]},
{ "text": "Walk away", "value": 2, "nodes": [
{ "nodeType": "setVar", "id": "affinity_alice", "op": "sub", "value": 1, "scope": "global" },
{ "nodeType": "dialogue", "characterId": "alice", "text": "I see..." }
]}
]
},
{ "nodeType": "branch",
"condition": { "kind": "variable", "id": "affinity_alice", "op": ">=", "value": 1, "scope": "global" },
"thenNodes": [{ "nodeType": "dialogue", "characterId": "alice", "text": "You're a good friend." }],
"elseNodes": [{ "nodeType": "dialogue", "characterId": "alice", "text": "..." }]
}
]
}