DEMIENGINEDOCUMENTATION Everything is free
Project & file formats · Start here

Prefab documents, nesting, and override semantics.

Prefab files

A prefab is a saved, reusable piece of a game world. It holds a small group of entities — a player character, a door, a pickup — and lets you place that whole group into any scene by name. This page explains what a prefab document contains, how instance IDs are built, and exactly how overrides change prefab contents.

What a prefab is and why reuse matters

Imagine you design a player character once: a body sprite, a collider, a script. If every level copies those entities by hand, every fix has to be repeated in every file, and the copies drift apart. A prefab is the single source. Levels place instances of it; editing the prefab updates every placement.

Prefabs also nest: a house prefab can place wall prefabs inside itself. The result is a tree of reusable parts instead of one enormous flat list.

Like every DemiEngine document, a prefab file starts with format_version.

The document

{
  "format_version": 1,
  "id": "prefab://characters/player",
  "entities": [
    {
      "id": "body",
      "components": {
        "Transform2D": { "position": [0, 0] },
        "Sprite": { "texture": "asset://textures/hero" },
        "BoxCollider2D": { "size": [0.8, 1.2] }
      },
      "children": [
        {
          "id": "shadow",
          "components": {
            "Transform2D": { "position": [0, -0.1] }
          }
        }
      ]
    }
  ]
}

Field by field:

FieldMeaning
format_versionAlways required. Documents without it are rejected.
idThe prefab's stable identity as a prefab:// URI. prefab://characters/player maps to prefabs/characters/player.prefab.json.
entitiesOne list for ordinary entities and prefab placements. Ordinary entries have components; placements have prefab and optional overrides. Use children for local hierarchies.

Use entities for entity-prefab authoring, including nested prefab references. A separate top-level instances array is still accepted by the loader, but is not needed or recommended for new documents. UI prefabs have their own *.ui.prefab.json format. Entity fields behave as in Scene files.

Packages can export entity prefabs by listing their *.prefab.json source files in the package manifest's files. After installation, references resolve by the prefab document's stable id, so a package may keep a short source path while exporting a namespaced URI such as prefab://demi/vegetation/oak. A project's existing conventional prefab path takes precedence; otherwise duplicate exported identities are rejected. Nested package prefabs may reference their own declared files and packages listed in their direct dependencies. Undeclared files and unrelated installed packages are inaccessible from a package prefab. Source and cooked projects use the verified package lock and retain these boundaries after relocation.

Converting an existing hierarchy

Scene → Create prefab from selection creates a source prefab and replaces the selected hierarchy with a linked instance by default. Its existing entity IDs and transforms are retained, so Lua, saved state and other scene references keep using the same IDs. Uncheck Replace selection with a prefab instance for a copy without changing the original. Undo restores the original hierarchy and source layout; it leaves the new prefab file available for reuse.

The instance may contain an entity_ids object. Keys are expanded prefab-local IDs (including nested paths); values are stable IDs in the owning scene or prefab document. For example, "entity_ids": {"body": "habitat", "door": "airlock"} keeps those two entity identities instead of producing instance/body and instance/door. Inside another prefab, the outer instance namespace still applies. Override keys remain prefab-local, so body.Transform3D.position still edits the mapped body.

Unmapped entities retain normal prefixed IDs. The mapping uses nonempty string keys and values, every key must resolve to an expanded entity, and destination IDs must be unique across the resulting scene. Missing keys and collisions are validation errors. If a mapped entity is removed from the source prefab, update the instance mapping as part of that authoring change. Copying or duplicating a mapped instance in the editor allocates fresh mapped IDs while preserving the original instance's identities. Instance handles are separate from the runtime entities they own.

ID expansion: how instance names are built

By default, when a prefab is placed, its entity IDs are prefixed with the instance ID. This keeps two copies of the same prefab from colliding in one scene.

Worked example. The prefab above has a local entity called body. A scene places it like this:

"entities": [
  {
    "id": "player",
    "prefab": "prefab://characters/player"
  }
]

Expansion rule: instance ID + "/" + local entity ID.

Prefab-local IDInstance IDResulting scene ID
bodyplayerplayer/body
shadow (child of body)playerplayer/shadow
bodyenemy_aenemy_a/body

So the instance named player turns the local body into player/body in the running scene. Two instances of the same prefab can live side by side — player/body and enemy_a/body — without clashing.

Nested instances and deeper IDs

Prefabs can place other prefabs. Suppose prefab://props/wall has a local entity wall, and a house prefab places it:

{
  "format_version": 1,
  "id": "prefab://props/house",
  "entities": [
    {
      "id": "floor",
      "components": { "Transform2D": { "position": [0, 0] } }
    },
    {
      "id": "wall",
      "prefab": "prefab://props/wall"
    }
  ]
}

Inside the house prefab, that nested instance already expands to wall/wall ("instance wall" + local wall). When a scene then places the house:

"entities": [
  { "id": "house", "prefab": "prefab://props/house" }
]

the prefix chain continues outward:

SourceID at that levelFinal scene ID
house prefab entity floorfloorhouse/floor
wall prefab entity wall placed as instance wall inside housewall/wallhouse/wall/wall

Each nesting level adds one prefix segment. Read the final ID from right to left to see which copy you have: house/wall/wall is "the wall entity of the wall placement inside house".

Overrides

An overrides object on an instance adjusts the expanded result without editing the prefab. It is written next to the instance's prefab field:

"entities": [
  {
    "id": "player",
    "prefab": "prefab://characters/player",
    "overrides": {
      "body": {
        "components": {
          "Sprite": { "color": [1, 0.5, 0.5, 1] }
        }
      },
      "body.Transform2D.position": [3, 2]
    }
  }
]

Keys address prefab content; values describe how much to change. The rules are mechanical:

CaseWhat happensExample override
Field absent Nothing is touched. The expanded entity inherits the prefab's value. No rotation key → the prefab's rotation is kept.
Object value Nested entity overrides merge recursively: override keys win and other prefab values survive. "body": {"components": {"Transform2D": {"position": [3, 2]}}} changes position while retaining scale.
Array value Replaces the whole array. Elements are not merged one by one. "body.Transform2D.scale": [2, 2] replaces the full scale pair.
null value Removes that field from the expanded result. "body.Sprite.texture": null drops the texture field entirely.
Dotted key Assigns the value at entity.Component.field. An object assigned this way replaces that field's object rather than recursively merging it. "body.Transform2D.position": [3, 2] writes only that position.

The dotted position key writes one field. The nested body.components.Sprite object changes its color while retaining the texture and other source fields. For example, "body.GameplayData.values": {"health": 80} replaces the complete data object. Use that exact-field form when deleted data keys or literal null values must survive composition.

Use overrides for per-placement differences (start position, difficulty tint, starting health). When two placements need the same change, prefer editing the prefab or making a second prefab.

Children versus parent

Inside a prefab, organize entities with the children array whenever they share the same kind of transform and form one visual unit. The body/shadow example above does this: the shadow follows the body automatically, and no field has to name anyone.

An explicit transform parent field is for spatial links nesting cannot express, such as a relationship across prefab or scene source boundaries. Parent and child transforms must use a compatible spatial domain. Rules of thumb:

  • Same-domain, visually attached parts → children.
  • Cross-prefab or cross-scene spatial relationships → explicit parent, referring to the other entity's stable ID.

Keeping ordinary relationships in children keeps documents readable and local: you see the whole unit in one block instead of hunting for IDs.

A child can itself be a prefab placement. Here moving the house root moves both the floor and the expanded wall. IDs stay document-wide: the wall still expands to wall/wall, not house_root/wall/wall.

{
  "format_version": 1,
  "id": "prefab://props/house_grouped",
  "entities": [
    {
      "id": "house_root",
      "components": { "Transform2D": {} },
      "children": [
        { "id": "floor", "components": { "Transform2D": {} } },
        { "id": "wall", "prefab": "prefab://props/wall" }
      ]
    }
  ]
}

Validation errors

The loader refuses some shapes because they cannot produce a correct world. Know these limits:

ErrorWhy
Nested prefab cycles If prefab A instances B and B (directly or through more levels) instances A, expansion never ends. Any cycle is an error — the load fails with the chain of prefabs involved.
Mixed domains in one hierarchy A children tree or transform parent chain must use compatible spatial domains. An explicit parent does not make a 3D-to-2D transform hierarchy valid.
Hierarchy depth over 128 The current children traversal rejects depth beyond 128. Prefab-reference cycles are checked separately; this is not a limit on the number of entities or prefab copies.
Unknown prefab URI A prefab reference on an entity entry that does not resolve to a prefab file is a validation error.
Duplicate IDs after expansion If two expanded entities end up with the same ID, the placement is ambiguous and is rejected.

Run the validator from the project root to catch all of these before launch:

demi validate games/my_game

Where prefabs fit