UI as a shared language between tools and play.
The editor should author UI definitions, the engine should render and manage them, and the game should supply live data plus behavior bindings. That keeps creation, execution, and meaning cleanly separated while still letting the same assets move directly from tooling into runtime.
Overview
The system should support a workflow where the editor creates and modifies UI visually, saves UI definitions as assets, the game loads those assets at runtime, and the engine provides the shared UI runtime both sides depend on.
UI should be stored as data assets, not embedded directly in the editor and not hardcoded only in game code. The editor authors structure, the runtime executes it, and the game binds it to real gameplay state.
Recommended Split
- Engine owns how UI works.
- Editor owns how UI is authored.
- Game runtime owns what the UI means.
Design Direction
- Use a retained-mode UI system.
- Store screens and windows as declarative assets.
- Render them through shared HTML, CSS, and JavaScript runtime code.
Package Layout
A package split like this keeps schema, runtime, editor, and game-facing code independent while still sharing a common vocabulary.
/packages
/ui-schema
/ui-runtime
/ui-editor
/game-runtime
ui-schema
- UI node definitions
- validation
- schema versioning
- migrations
ui-runtime
- renderer
- layout engine
- window manager
- widget registry
- theme system
ui-editor
- scene or workspace
- selection tools
- property inspector
- asset browser
- save and load flow
game-runtime
- game-specific data providers
- action handlers
- UI asset loading
- window orchestration
Engine
The engine should be the single owner of the UI runtime so the editor and game do not drift apart in behavior over time.
Responsibilities
- window lifecycle
- widget rendering
- layout calculation
- input routing
- focus management
- dragging and resizing behavior
- modal behavior
- z-order and activation
- theming and skinning
- serialization and asset loading hooks
Why It Belongs Here
- accurate editor preview
- fewer runtime and editor mismatches
- one place to fix layout and interaction bugs
- reusable widgets and themes
Core Runtime Systems
UI Manager
- open windows
- focus order
- active modal
- hovered and pressed widgets
- drag and resize operations
Window Manager
- create
- close
- minimize
- maximize
- bring to front
- move
- resize
Widget Registry
windowpanellabelbuttoninputlisttabs
Theme System
- fonts
- spacing
- borders
- colors
- states
- animation references
Runtime API
The engine should expose stable, high-level APIs that both the editor and the game can consume without caring about rendering internals.
ui.loadAsset("quest-log");
ui.createWindow(windowDefinition, bindings);
ui.closeWindow("quest-log");
ui.updateBindings("quest-log", bindings);
ui.createWindow({
definition: windowDefinition,
bindings: {
data: gameViewModel,
actions: actionRegistry
}
});
Editor
The editor should be an authoring tool for UI assets, not a place where gameplay code gets embedded.
Responsibilities
- create windows and widgets visually
- arrange hierarchy
- move and resize elements
- edit properties
- assign themes or style references
- set data-binding keys
- assign symbolic action identifiers
- preview final layout using the shared runtime
Recommended Early Features
- hierarchy tree
- canvas or workspace
- property inspector
- theme picker
- save and load
- live preview
Editor Workflow
- Create or open a UI asset.
- Add a root node such as a
windoworscreen. - Add child widgets like
panel,label,list, orbutton. - Configure properties in an inspector.
- Save the asset as JSON.
- Preview the result using the same UI runtime the game uses.
Symbolic Actions
Instead of embedding code, a button should store a stable action id and let the game resolve it later.
{
"action": "inventory.useSelectedItem"
}
Game Runtime
The game runtime consumes UI assets and supplies them with live data and behavior. It decides when a screen opens, what it displays, and how action ids map into actual game systems.
Responsibilities
- deciding when UI opens or closes
- selecting which asset to load
- supplying live data
- resolving symbolic actions into real functions
- updating bindings as game state changes
Runtime Mutability
- visibility
- enabled state
- bound text
- bound lists
- dynamic styling flags
- open and closed state of windows
Consumption Flow
- Load a UI asset.
- Validate it through the shared schema layer.
- Create a runtime instance through the engine UI manager.
- Inject data bindings and action handlers.
- Let the engine handle rendering and interaction.
Example Usage
const definition = await ui.loadAsset("quest-log");
ui.createWindow({
definition,
bindings: {
data: gameViewModel,
actions: {
"ui.closeWindow": ({ windowId }) => ui.closeWindow(windowId),
"inventory.useSelectedItem": () => inventory.useSelectedItem()
}
}
});
UI Assets
UI assets should start as serializable JSON files. The format should be stable, versioned, and expressive enough to define hierarchy, layout, binding, and symbolic actions without containing gameplay logic.
Schema Goals
- schema versioning
- stable ids
- nested hierarchy
- typed widgets
- layout properties
- style references
- data bindings
- symbolic actions
Recommended Fields
schemaVersionidtypechildrentitle,x,y,width,heightbind,visibleWhen,enabledWhen,actiontheme,variant,className
Asset Example
A typical authored window should look like structured data the engine can interpret consistently in both the editor and the game.
{
"schemaVersion": 1,
"id": "quest-log",
"type": "window",
"title": "Quest Log",
"x": 120,
"y": 80,
"width": 420,
"height": 300,
"flags": {
"draggable": true,
"resizable": true,
"closable": true
},
"children": [
{
"id": "quest-list",
"type": "list",
"bind": "quests.active"
},
{
"id": "close-button",
"type": "button",
"text": "Close",
"action": "ui.closeWindow"
}
]
}
Versioning
Every asset should declare a schema version so the format can evolve without breaking older editor output.
When the asset format changes, add migration functions in the shared schema layer. The editor can write the newest version, while the game and engine can continue to load older assets by upgrading them during validation.
{
"schemaVersion": 1
}
Long-Term Extensions
- prefabs and templates
- inherited style variants
- animation descriptors
- localization keys
- responsive layout rules
- asset references to icons and images