Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit61a33237e0

README.md

entity_hierarchy

Hierarchy helpers built on top of the entity bounds API. Implements issue #876 (entity hierarchy swap) on top of issue #874 (entity bounds API). Pure Luau — uses entity(id).hierarchyBounds(), entity.spawn, and the transform primitives. Used to replace blockout geometry with a generated/imported asset, fitting the asset to the source's bounds and inheriting transform + (optionally) name.

Exports

  • M.swap(entityId: string, assetPath: string, opts: SwapOpts?) -> (string?, string?) — replace entityId with assetPath. On success returns (newEntityId, nil); on failure (nil, errmsg).

Types:

  • SwapOpts = { fit?: string, source_origin?: string, asset_origin?: string, keep?: { string }, timeout?: number }

Options:

  • fit — "bounds" (default, uniform scale to fit inside the source's AABB), "bounds_xy" (uniform XZ, identity Y), "none" (no scaling).
  • source_origin / asset_origin — "bottom" (default), "center", "top". XZ always anchors on centre; only Y is picked.
  • keep — list of attributes to preserve onto the new entity. Currently only "name" is honoured.
  • timeout — seconds to wait for the asset's bounds to become queryable (default 5).

Usage

local EntityHierarchy = require("@builtin::modules.entity_hierarchy")

local newId, err = EntityHierarchy.swap(
    "blockout_tree",
    "@builtin::meshes.cube",
    {
        fit           = "bounds",
        source_origin = "center",
        asset_origin  = "bottom",
        keep          = { "name" },
    }
)

Notes

  • The replacement is spawned far below the scene (Y = -1000000) before bounds become queryable, then moved to the final anchored position once the asset loader populates its bounds. Avoids a one-frame visual pop at the origin.
  • awaitBounds polls entity(id).hierarchyBounds() at 20 Hz via await(delay(0.05)) — task.wait(0) cooperative yields don't always give the asset-load systems enough slack to make progress.
  • When the source has no geometry, fit is force-set to "none" (there's nothing to measure against) and the new entity is anchored on the source's localPosition.
  • The source entity is despawned at the end of swap — caller must not reference entityId afterward.
  • keep only supports "name" today; preserving component instances would require re-attaching every script-component and is out of scope.