9.2 KiB
Prefab Authoring
Blacksite prefabs are schema-versioned .scn.ron documents under assets/. A placed prefab stays
linked through its authored PrefabInstance; entities loaded from the source are runtime-owned
members and are never flattened into the scene that contains the instance.
Create and place a prefab
- Select one or more authored actor roots.
- Choose File > Save Selection As Prefab... and save under
assets/prefabs/. - Place the resulting prefab from the Asset Browser. The placed root exposes a Prefab Instance component with its stable asset ID and source path.
Saving expands the selected authored hierarchy. Hydrated prefab members and composed-scene members
are excluded from the prefab file. Missing or duplicate authored ActorId values are repaired before
a direct prefab save; a failed save restores the original in-memory IDs.
Stable identity and layering
An override target is not a runtime Entity or display name. PrefabActorPath stores the target
actor's stable ActorId plus the ActorId chain of every nested prefab anchor needed to reach it.
Two nested copies may therefore contain the same source actor IDs without sharing override identity.
The hydrated result is resolved from least to most specific:
- the source prefab document;
- each nested link's own overrides;
- the containing variant document and its authored local structure;
- the overrides on the placed instance.
The override schema and runtime application live in crates/shared, so standalone and packaged game
builds apply the same data without loading the editor plugin.
Instance overrides
Select a generated prefab member to edit its override layer. The current inspector exposes:
- Property overrides for reflected authoring-component fields, including Transform fields and
brush-only legacy
MaterialDescparameters. - Component overrides for adding, replacing, or removing registered authoring components, plus
visibility on direct or nested generated actors. Primitive and mesh material assignments live in
their owning
MaterialSlotdata;MaterialDescis not a general mesh/primitive material path. - Structural overrides for removing a source actor or reparenting generated actors within the same instance layer. Hierarchy Unparent override writes the same stable structural operation.
These edits update PrefabInstance.overrides_ron; they do not immediately edit the source asset.
Use Revert at field scope, Revert component overrides, Revert actor overrides, or
Revert all at instance scope. Each edit/revert is one undoable prefab-state command.
Legacy root transform/material data and name-keyed visibility data still load. Current operations
use the full stable PrefabActorPath, so repeated leaf ActorId values in sibling nested instances
remain distinct.
Apply to source
Apply to source is an explicit source-asset write, not another name for storing an instance override. It is available for a field, component, generated actor, or all operations on the placed instance.
For a direct target, Blacksite patches the linked source document. For a target reached through a
nested instance_chain, it moves the operation into the immediate nested link recorded by that
source layer. This preserves the base/variant boundary instead of unexpectedly mutating the deepest
leaf prefab.
Before writing, Blacksite compares the recorded base with the current source value, serializes the patched document, validates the complete dependency graph, and performs an atomic same-directory write. The history entry stores exact before/after source bytes and the old/new instance state. Undo or redo is blocked if another process changed the source after the command; Blacksite reports the block instead of overwriting that external work.
Source status and conflict recovery
The Prefab Instance card combines hydration state, transitive graph validation, recorded source revision, stable target resolution, and base-value checks:
- Loading source - wait for hydration before unpacking or editing generated targets.
- Ready - the graph is valid, the recorded revision matches, and overrides resolve cleanly.
- Base not tracked - a legacy instance has no revision. Accept current base records it without changing override values.
- Base changed - the source graph changed but no semantic conflict was found. Inspect the result, then Accept current base or relink.
- Override conflict - a target disappeared or a source property/component/parent changed from the base captured by an override. Keep overrides rebases the stored bases, while Take source removes only semantically conflicting operations. Remove stale overrides cleans unresolved legacy visibility keys; Relink... selects another valid source.
- Broken source - a source is missing or its graph/path/identity validation failed. Hydrated generated state is removed, the authored anchor remains repairable, and Retry source or Relink source... starts one clean hydration attempt.
- Invalid overrides - the serialized override payload is malformed. Discard invalid data resets that payload explicitly.
Accept, keep/take, stale cleanup, relink, and source Apply participate in undo/redo according to their scope. Reload and Retry refresh hydration without creating an authoring history entry.
Local children and hierarchy overrides
Authored local actors may be created or reparented below a linked instance root or another authored local actor. Nested linked prefabs may be placed there as well. Those entries remain normal scene-owned content: they can be selected, transformed, reordered, saved, and undone.
Rows carrying HydratedPrefabMember are generated context. Their Transform/Material fields are
edited through overrides rather than direct source mutation. Generated actors may be removed or
reparented only within the same owning instance and nested layer. Cross-instance, cross-layer, and
composition-boundary drops are rejected. Between-row ordering of generated siblings is not modeled,
and authored local children cannot yet attach directly to a generated source member.
Save a variant
Variants use normal linked-prefab composition rather than a separate asset type:
- Start with a linked prefab instance and author its instance override layer.
- Add local children or nested prefab instances below its root/local structure.
- Select the linked root and choose Create Variant... in the Prefab Instance inspector, or use File > Save Selection As Prefab....
The saved prefab contains the base PrefabInstance link, its stable overrides, and local authored
structure. Hydrated base members are excluded. Placing the variant therefore evaluates base, nested
layers, variant, and placed-instance overrides in order without duplicating generated actors.
Applying an override on a nested source actor writes it to the immediate link in the variant/source document; open the leaf prefab itself when the intended change should affect every use of that leaf.
Unpack and convert
Unpack Layer removes only the selected outer PrefabInstance/PrefabRef ownership link and
turns its hydrated members into authored local actors. Nested prefab instances remain linked, and
outer overrides targeting them are folded into those retained links transactionally. Undo restores
the outer link and removes the local copy; redo restores the captured authored result.
Convert to Local recursively removes the selected outer link and every generated or authored
local nested prefab link below it. Every converted hydrated source actor receives a fresh local
ActorId, including actors from different nested documents that originally reused an ID. The
operation is one undo step and redo uses captured snapshots, so it does not depend on the source
files still being present.
Validation and remaining acceptance
Prefab save, editor load, project inspection, and cargo validate-levels recursively inspect linked
PrefabInstance components. Validation rejects missing sources, unsafe paths (including symlink
escapes), non-.scn.ron sources, cycles (including path aliases), empty asset IDs, incomplete link
anchors, malformed outer or inner override payloads, missing authored ActorId values, and
duplicate IDs within a document. The committed assets/prefabs/example_base.scn.ron ->
example_nested.scn.ron -> example_variant.scn.ron chain is the healthy production-format
fixture for this gate.
The shared format can apply registered reflected property/component operations, but the current authoring UI exposes the Transform and Material fields/components listed above. Generated sibling ordering and direct local-child attachment to a generated member are not yet override operations. Variants remain ordinary composed prefab documents rather than a separately named asset type.
Production acceptance closed in Gitea issue #43 after the complete workspace suite, recursive headless validation, packaged release startup, and live editor placement/inspection regression passed against the committed fixture chain.
See ADR 0027 for the ownership, stable identity, layering, and source-write contract.