Skip to content

World Model & Spatial System

Michael Voitovich edited this page Jun 20, 2026 · 30 revisions

In the Scanline Engine, the scene object hierarchy serves two distinct purposes: visual rendering (geometry) and the text parser. To avoid compromising either system, the engine uses two separate but closely related models: the Raw Spatial Hierarchy and the Semantic Spatial Projection. What the player actually sees and interacts with at the level of Point-n-Click and text commands is a synthesis of these two different worlds.

3layers

1. Raw Spatial Hierarchy (The Logical-Spatial Model)

The Raw Spatial Hierarchy defines the logical-spatial relationships of objects in the scene, which serves as the foundation for the language layer (the spatial model). Every scene object can have an optional spatial placement defined by two properties:

  • parentNodeId: Points to the parent object.
  • relation: Describes the spatial relationship to the parent.
spatial

The engine supports five raw spatial relations: in (inside), on (on top of), under (beneath), behind, and near (proximity). Crucially, the raw spatial hierarchy does not dictate what the player actually sees on the screen. The visual rendering of an object is strictly determined by its X/Y coordinates and properties like Layer and Parallax. For example, a book might have a parentNodeId pointing to a "Cabinet" with the spatial relation in. To the text parser, this book is logically inside the cabinet. However, technically, if the book's X/Y coordinates are set to the other side of the level, the game will render it floating in mid-air far away from the cabinet. This disconnect is exactly why Containers (Surface and Inventory components) were invented. Containers bridge the gap between logical relationships and visual correctness, allowing Actors to place items securely on, under, in, or behind objects (see bellow).

2. Semantic Spatial Projection (The Text-Facing Model)

Because we use invisible technical nodes (like a specific Surface assigned to a desk) to make objects look correct on the screen, relying purely on raw geometry would expose ugly implementation details to the player. We don't want players forced to type commands for an invisible desk_surface. To provide a natural language experience, the engine dynamically calculates a Semantic Spatial Projection via a read-only system called the SceneTextLayer. This is the world model that the text parser, LLM Game Master, and player actually interact with. The projection works by filtering the raw hierarchy using Semantic Anchors:

  • Titled Objects: Any object with a Title text asset is considered semantically meaningful and becomes a Semantic Anchor.
  • Technical Nodes: Objects without a Title are considered technical or geometric nodes.
  • Collapsing: The SceneTextLayer collapses untitled technical nodes in the text model. If an untitled object has titled descendants, those descendants are projected upward and visually attached to the nearest titled ancestor.
spatial_1

Example: Imagine a desk (Titled: "Desk") containing a technical surface (Untitled), which in turn holds a key (Titled: "Key").

  • Raw Truth: Key is on -> Technical Surface is in -> Desk.
  • Semantic Projection: The parser ignores the technical surface. To the player, the Key is simply in the Desk.

If the technical surface contained a titled "Drawer", the Drawer would become a new Semantic Anchor, and the Key would be described relative to the Drawer instead of the Desk.

spatial_2

3. The Anchor-Relative Rule

Because technical nodes collapse, spatial queries (LOOK IN, TAKE FROM) evaluate spatial relations relative to the specific anchor being queried. This is known as the Anchor-Relative Rule. The rule dictates that the effective relation of an object is determined by the first semantic relation on the path from the queried anchor to the descendant. Internal relations between deeper nested objects are preserved but do not override their relationship to the outer anchor. The Classic Example: Assume a Cabinet (Titled) contains Book A (Titled) in it, and Book B (Titled) is placed on Book A.

  • Relative to the Cabinet: Both books are considered in the Cabinet, because the first relation from the Cabinet to the chain is in. Asking LOOK IN CABINET will successfully show both Book A and Book B.
  • Relative to Book A: Book B is considered on Book A, because the first relation from Book A to Book B is on. Asking LOOK ON BOOK A will show Book B.

This ensures that players can naturally say TAKE BOOK B FROM CABINET or TAKE BOOK B FROM BOOK A, and both commands will perfectly resolve the target without confusing the parser.

4. Visibility, Storage, and near

The semantic projection also strictly separates visual proximity from actionable storage:

  • Container Requirements: A spatial relation of in or on does not automatically make an object an interactive storage. Storage must be explicitly granted by attaching an Inventory or Surface component to the object (container component). Containers allow players and NPCs to store items in them. The type and quantity of items (capacity) are specified in the container settings.
  • Spatial placement without container: For flexible scene creation, you can spatially place objects in the editor by attaching them to any semantically meaningful parent object without a container, but this will result in them only being able to be picked up and/or used in the game, but not placed back.
  • The near Relation: The near relation is recognized by the parser for proximity and text grouping, but it is strictly forbidden to be used as a storage slot. The SceneSpatialValidator will reject near if it is configured inside an Inventory or Surface component, because an item cannot be "stored" in the abstract space "near" something.
spatial_container

Visibility & Actionability: Managing Access and Hidden Items

In the Scanline, the text parser and semantic world model strictly distinguish between whether an object is visible (known to the player) and whether it is actionable (accessible for interactions like TAKE, PUT, USE, or EXAMINE). An object might be visible but too far away, or visible but physically blocked by a transparent container. To build puzzles and interactive environments, developers can manage this access using Blocker components, Switch objects, and the hidden state of entities.

1. Restricting Access: Blockers and Switches

Access to nested spatial descendants can be restricted using specific components that act as semantic barriers.

The Blocker Component

A Blocker is an always-active semantic barrier. It prevents the player from interacting with objects that are spatially contained within or around it.

The Switch Component

A Switch acts exactly like a Blocker, but its state can be toggled between closed (State 1) and open (State 2). When a Switch is closed, it blocks access to its contents; when open, the contents become fully visible and actionable. Even if the object holding the Switch component has no Title (meaning it is invisible to the parser), it still acts as a logical barrier for its visible descendants.

subscene2

Why two types of components if a non-openable Switch is sufficient? They actually serve different functions. The Switch component gives the scene object it's attached to the ability to detect mouse clicks and toggle visible scene objects (for example, swapping a closed drawer sprite with an opened one). This is its primary function, while blocking access is secondary, preventing objects from being accessed in closed drawers, cabinets, etc. The Blocker, on the other hand, is simply a spatial placeholder to restrict access without requiring the extensive functionality provided by the Switch.

Configuring Barriers (transparent and blockedRelation)

Both Blocker and Switch components rely on two main properties to determine how they restrict access:

  • blockedRelation: Specifies which spatial relation is being blocked (in, on, under, behind, or none). For instance, a desk drawer might block in, while a heavy rug might block under.
  • transparent: Determines whether the player can still see the blocked objects.
    • Opaque (transparent: false): The blocked descendants are completely hidden from the semantic world model and are inaccessible.
    • Transparent (transparent: true): The blocked descendants remain visible for LOOK commands, but are excluded from actionability scopes (they cannot be taken, examined, used, or opened). The parser will respond acknowledging the object's presence but stating it cannot be interacted with.
blocker_inv

The clearlyOpenable Flag (Switches Only)

For closed, transparent containers, the engine needs to know how to phrase the rejection when a player tries to interact with the contents. The clearlyOpenable flag handles this:

  • If true, the engine explicitly states the container is closed (e.g., "The drawer is closed." or "You can't reach that while it is inside something closed.").
  • If false, the engine gives a more generic rejection (e.g., "You can't reach it.").

2. Discovering Hidden Items (lookable and examinable)

Sometimes you want an object to be completely absent from the parser's world model until the player actively discovers it. Titled objects feature a hidden semantic field that manages this discovery process. Directly typing LOOK <hidden object> or EXAMINE <hidden object> before it is discovered will result in a "not found" response, preventing players from blindly guessing an object's existence. The object must be revealed through its spatial anchor (the visible parent object it relates to).

hidden: "lookable" A lookable object remains invisible until the player explicitly looks at the spatial area where it is hidden.

  • How to reveal: The player must perform a relation-aware look on the anchor object (e.g., LOOK UNDER DESK), or reveal the object via the mouse title reveal.
  • Messaging: Upon the first successful discovery, the engine uses a special discovery phrasing (e.g., "Under the desk you discover: a key."). Once revealed, it becomes a standard visible object for the remainder of the runtime session and subsequent checks will simply say "_you see ...".

hidden: "examinable" An examinable object requires closer inspection and will not be revealed by a simple LOOK command.

  • How to reveal: The player must successfully EXAMINE the visible spatial anchor that holds the object. For example, if "audio cables" are hidden behind a "boombox" with the examinable property, the player must EXAMINE BOOMBOX to find them.

First-Level Semantic Reveal Rule

When revealing spatial contents via LOOK or EXAMINE, the engine only reveals and describes first-level direct titled children. If an anchor contains a titled box, and inside that box is a hidden key, examining the anchor will only reveal the box. Grandchildren or deeply nested hidden items must be discovered by inspecting their immediate titled parent.

Save/Load Persistence

Because the discovery of hidden objects represents actual gameplay progress, the revealedHiddenEntities state is tracked as runtime progress and will be serialized with the game-state save files, rather than being part of the static scene authoring data.

Subscenes: Activating Close-Up Modal Views, Virtual Spatial Nodes, and Item Scaling

In the Scanline Engine, Subscenes provide a way to create detailed, close-up inspections of specific areas or objects (like looking inside a drawer, a computer terminal, or a tabletop). They function both as UI modal views and as structural nodes within the engine's semantic spatial hierarchy. Here is a comprehensive guide to understanding and configuring Subscenes.

subscene

1. Activating Close-Up Modal Views

A Subscene is a trigger component that overlays a zoomed-in perspective of specific objects over the main gameplay scene.

  • Activation: A Subscene is activated either by clicking on the designated TriggerBox with the mouse or by typing the EXAMINE <Name> command in the parser.
  • Visual Behavior: When active, the main scene is visually darkened or blurred in the background, drawing the player's focus entirely to the close-up objects.
  • Player Restrictions: While a Subscene is open, standard player movement is blocked.
  • Exiting: To close the modal view and return to the main scene, the player simply clicks anywhere outside the boundaries of the objects contained within the active Subscene group.

2. Managing Virtual Spatial Nodes and Nesting

Subscenes act as virtual spatial nodes within the engine's raw spatial hierarchy. This means you can nest objects or even other Subscenes inside them.

Note: Nested subscenes haven't been tested yet, and their functionality is actually questionable.

Direct vs. Indirect Nesting & Activation Depth When a Subscene is activated, the engine only enables objects that are directly (first-level) nested inside it.

  • If Subscene A contains Subscene B, opening Subscene A will activate the TriggerBox for Subscene B so the player can see it.
  • However, the actual contents inside Subscene B remain inactive until the player explicitly opens Subscene B.

Note: While the legacy targetGroupId method for activating objects is supported, the spatial hierarchy is now the engine's single source of truth for Subscene contents.

3. Parser Visibility and Actionability

The text parser handles objects inside Subscenes with a strict distinction between visibility (semantic knowledge) and actionability (physical access).

subscene1
  • Semantic Visibility: Titled objects inside an inactive (closed) Subscene remain part of the semantic world model. The parser knows they exist and can recognize player commands directed at them.
  • Restricted Actions: Even though the parser knows about the objects, being disabled inside a closed Subscene prevents direct interactions. Commands like TAKE, PUT, or USE will fail until the Subscene is actually opened.
  • Auto-Open Attempts: If a player issues a command (like LOOK, TAKE, OPEN) targeting an object inside a closed Subscene, the parser will first silently attempt to open the Subscene using the same access rules as EXAMINE. If it cannot be opened, the player receives a standard failure message and the action aborts.
  • Distance Checks: When a Subscene is active, PUT and DROP commands aimed at targets inside the Subscene bypass the standard world-distance checks. If the storage target is inside the currently active Subscene, the player is considered close enough to interact with it, regardless of their avatar's distance from the object in the main scene.
subscene2

4. Handling Subscene-Specific Item Scaling (itemScale)

When transitioning items from the main scene (or the player's inventory) into a close-up Subscene, objects naturally need to appear larger to match the zoomed-in perspective. This is managed by the Subscene's itemScale property.

  • Runtime Scaling: itemScale is an optional runtime multiplier applied to any entity with an Item component that is placed inside the active Subscene.
  • Placement and Animation Math: When an item is placed or dropped onto a Surface within an active Subscene, the item is immediately added to the Subscene's runtime set and inherits the itemScale multiplier. This happens before the engine calculates the placement fit or the drop animation, ensuring that capacity limits and visual flights look completely natural in the close-up view.
  • Drop Prioritization: When a player uses a generic DROP command without specifying a target, an active Subscene will automatically prioritize dropping the item onto a Surface within that Subscene over main-scene floors or walkboxes.

5. Automatic State Management (Switch Auto-Reset)

To prevent logical desynchronization between a close-up view and the main room, Subscenes enforce a strict auto-reset rule for nested Switch components (like doors, covers, or latches). If a Switch object is manipulated inside an active Subscene, it will automatically reset to State 1 (Closed) and play its closing sound effect (sound_1) the moment the player exits the Subscene. This guarantees that a container cannot remain "open" in the close-up view while simultaneously appearing "closed" on the main scene's background sprite.