Let a behavior run while the editor is open

A behavior object carries a "Run in editor" switch next to Enabled. When
it is on, the behavior ticks outside of Play and works on the saved model
directly - Unity semantics: what it moves is a real change, autosaved and
not undoable.

Input stays with the editor, so On key never fires while typing. The
editor run never bakes placed containers into the model, since that would
silently drop their instanceEdits.

syncToModel reports whether it wrote anything, and the dirty flag is only
raised on change - otherwise a behavior moving something every frame
would trigger ~60 saves a second.
This commit is contained in:
root committed 2026-09-09 17:13:07 +02:00
1 parent c74037b6e1
commit 8f31afac37
21 files changed
+718 -35

No files matched your search

+27
View File
@@ -50,6 +50,30 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
walk(app.stage);
return found;
};
/**
* Centre of the control of an inspector row, addressed by its label. A row aligns its label to a
* *minimum* width, so a label wider than the column pushes its control right – which is why a
* fixed offset from the label's own text is the wrong way to click one.
*/
const fieldOf = (label: string): { x: number; y: number } | null => {
let found: Text | null = null;
const walk = (node: Container): void => {
if (found !== null || !node.visible) return;
if (node instanceof Text && node.text === label) {
found = node;
return;
}
for (const child of node.children) walk(child);
};
walk(app.stage);
// TypeScript does not track the assignment inside `walk`, hence the cast.
let owner: Container | null = (found as Text | null)?.parent ?? null;
while (owner && !(owner instanceof Widget)) owner = owner.parent;
const row = owner?.parent;
if (!row || !owner) return null;
const control = row.children[row.children.indexOf(owner) + 1];
return control instanceof Widget ? centreOf(control) : null;
};
/**
* The visible text of the overlay layer, in drawing order – everything an open popup shows.
* Menus hide filtered-out entries, so this is also what a search box currently leaves.
@@ -134,6 +158,7 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
objectsOf,
sceneObjects: () => (state.activeScene ? objectsOf(state.activeScene.id) : []),
locate,
inspectorField: fieldOf,
textColor,
overlayTexts,
overlayRect,
@@ -188,6 +213,8 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
dropPreview: () => containerEditor.dropInfo(),
},
world: () => editor.worldSnapshot(),
/** Behaviors running without Play (`Run in editor`), and how long they have been at it. */
editorRun: () => editor.editorRunInfo(),
/** Rows the hierarchy shows – collapsed subtrees are not in here. */
hierarchyOrder: () => hierarchy.rowOrder(),
hierarchyChevron: (id: string) => hierarchy.chevronPosition(id),