158 lines
9.2 KiB
Markdown
158 lines
9.2 KiB
Markdown
# 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
|
|
|
|
1. Select one or more authored actor roots.
|
|
2. Choose **File > Save Selection As Prefab...** and save under `assets/prefabs/`.
|
|
3. 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:
|
|
|
|
1. the source prefab document;
|
|
2. each nested link's own overrides;
|
|
3. the containing variant document and its authored local structure;
|
|
4. 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 `MaterialDesc` parameters.
|
|
- **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 `MaterialSlot` data; `MaterialDesc` is 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:
|
|
|
|
1. Start with a linked prefab instance and author its instance override layer.
|
|
2. Add local children or nested prefab instances below its root/local structure.
|
|
3. 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](https://git.spacetrainclubhouse.com/Falling-Metal-Interactive/Blacksite/issues/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](../adr/0027-stable-prefab-ownership-and-variants.md) for the ownership, stable identity,
|
|
layering, and source-write contract.
|