beasty-visual-novel / getting-started / core-concepts.md

Core concepts

The mental model behind the whole package. Six ideas. Once you hold them, every other page in this documentation is just detail, and the editor stops surprising you.

Read this after Your first scene, when you have seen the pieces move.

DialogueScene - one story

A DialogueScene is the root asset of one story. Chapter 1 is a DialogueScene. So is a single conversation you jump into from a room and come straight back out of.

It holds almost nothing itself. It points at two things: the VNContext (the world this story happens in) and a root StoryGraph (the story itself). It can also carry a .vnbeasty script file, which is the same story written as text.

Create one with Create > Beasty VN > Visual Novel (Dialogue Scene), or let Tools > Beasty VN > Setup > Blank Canvas build one with everything it needs already wired.

A game normally has several DialogueScenes. They all share one world.

VNContext - the one shared world

A VNContext is everything that is true about your game rather than about one scene:

  • the cast — every character
  • the variables you defined, and the character schema (the fields every character has)
  • the dictionary — text tokens the player can edit
  • the localization table and the list of languages
  • the items and the quest catalog
  • the screens and the music config

There is a single global context. Not one per scene, not one per chapter — one. Every DialogueScene in your game points at the same VNContext, which is why a variable you set in chapter 1 is still there in chapter 7, and why the character you defined once is the same character everywhere.

The editor treats it as a singleton: Create Base Assets reuses the context that exists instead of ever making a second one. If you find yourself with two, that is a bug in your project, not a feature. See Validation and ids.

StoryGraph - a canvas of nodes

A StoryGraph is a canvas with nodes on it and edges between them. One node is the entry node — that is where playback starts.

A subgraph is a StoryGraph nested inside another one. You call it from a SubGraph node, it runs, it comes back with an outcome, and the calling node routes on that outcome. Use it when a chunk of story is self-contained: a fight, a flashback, a minigame’s dialogue. See Subgraphs.

Node - one beat

A node is one beat of the story. There are seven kinds, and that is the entire list.

NodeIts job
Dialogue NodeRuns its blocks in order, then goes to its default next node. This is the workhorse; most of your graph is these.
Choice NodeShows the player the options whose condition passes, and goes where the chosen one points.
Decision NodeRoutes automatically and invisibly. The first branch whose condition passes wins. The player sees nothing.
Flow (Mode Switch) NodeHands control out of the visual novel — to a room, to another scene. It has no successor.
SubGraph NodeCalls a nested graph and routes on the outcome it returns.
Return NodeEnds a subgraph, returning an outcome key.
Talk Menu NodeShows a character’s talk menu.

Right-click the graph canvas to create any of them. See The story graph.

Block - one instruction

A block is one instruction inside a node. A node holds a stack of them, and they run top to bottom.

Blocks are the whole authoring vocabulary. There is no scripting language underneath — a block is the smallest thing the story can do:

CategoryWhat lives there
DialogueThe Dialogue block. One spoken or narrated line. The only block with free text in it.
SceneBackdrop, Show character, Expression, Hide character, Props.
ClearCharacters, Backdrop, Props.
StateSet variable, Set dictionary, Character variable, Character name.
QuestsUpdate quest, Deliver items.
WorldWait, Advance time, Open screen, Routine override.
ItemsGive, Take, Set quantity, Use.
AudioMusic, Ambient, Voice, Sound effect, Stop channel.
InputAsk into a variable, into a dictionary token, or into a character’s name.
FlowGo to FreeRoam, Return to room, Choose room, Go to VN scene.

Three things about blocks are worth internalising early.

A Dialogue block stops. It shows its line and waits for the player. Everything else runs straight through. That is why two Dialogue blocks are two lines, and why a Backdrop block followed by a Dialogue block is one moment, not two.

A block with no asset assigned does nothing. An empty Backdrop block or a Music block with no clip is skipped — whatever is on screen stays on screen, whatever is playing keeps playing. To blank the backdrop or silence a channel on purpose, use the Clear Backdrop block or the Stop channel block. This is a deliberate design: it means you can leave a block half-filled without breaking the scene, but it also means an empty block is never an error and never warns you.

Some blocks describe the scene, others change the world. Scene blocks (backdrop, characters, props) are declarative: they say what the stage looks like, and they are simply rebuilt when the player rewinds. World-changing blocks (set a variable, give an item, advance time) fire once, and rewinding restores them from a snapshot. You do not have to think about this — but it is why rewind is correct rather than approximate.

Full list with every field: Blocks reference.

The variable store - the one that matters

This is the design decision the whole package rests on. Everything else follows from it.

There is one flat key/value store, and everything lives in it.

Not “variables live here and quests live over there”. One store:

WhatThe key it lives under
Your variablesgold, saw_intro — no prefix
Character variables@char:juan:affection
Game time@time:daypart, @time:hour, @time:day, @time:weekday, @time:season
Quests@quest:ana_m1:@state, @quest:ana_m1:@stage
Inventoryitem.potion
Dictionarythe token’s own key

The editor shows these with friendly labels — you pick time.daypart from a dropdown, not @time:daypart from memory. But underneath, they are all the same kind of thing, and that is the point.

Why it matters. Three consequences, and each one is a feature you would otherwise have to build:

  1. Time and routines can be used in any condition. A choice can require time.daypart == Evening. A door can be locked unless maya.location == Bakery. A quest can start when gold >= 100. There is no integration work between the time system and the condition system, because the time system does not have its own storage — it writes into the same store the condition system reads from.

  2. Everything is saved automatically. A save captures the store. That is one operation, and it picks up your variables, the clock, every quest’s state, the inventory and the dictionary, because they were never separate things. Add a new quest to your game and it saves correctly without you doing anything. See Saving and loading.

  3. Rewind works. Going back a line means restoring the store to what it was. Again: one operation, all of the world, no per-system undo logic that somebody forgot to write for the system you added last week.

If you take one idea from this page, take this one. When you wonder “can I use X in a condition?”, the answer is almost always yes, and this is why.

Full detail: Variables and conditions, and Variable keys for every reserved namespace.

BeastyManager - the one object

BeastyManager is the single GameObject you drag into a Unity scene. It owns every manager — the story director, the stage, the audio, the input, the save system, the free-roam controller — as hidden sub-components. You do not add them, wire them or think about them.

That is why the setup wizard’s scene has five objects instead of thirty, and why “auto-wire” is a button that works rather than a promise. See Controllers if you want to reach into it from code.

How a frame of the game flows

Put together, this is what happens when the player is playing:

  1. Playback is at a node. The graph handed it the entry node when the story started, or the previous node handed it this one.
  2. The node runs its blocks, top to bottom. Set the backdrop. Show Juan. Give the player a potion. Advance the clock two dayparts. Each block that changes the world writes into the variable store.
  3. A Dialogue block stops. The line goes on screen. The engine waits.
  4. The player advances — space, click, or auto-advance. Playback picks up at the next block.
  5. When the blocks run out, the node hands off to the next node: its default next, or the option the player chose, or the branch whose condition passed, or the outcome a subgraph returned.
  6. Back to step 2, at the new node.

Rewind is that loop run backwards: restore the store, rebuild the stage from the scene blocks, and show the previous line.

See also