Log inGet started

entity

The entity namespace — the engine's Luau API reference for entity.

The entity namespace — 70 functions.

entity/__batchReadIds

entity.__batchReadIds(target, component?, field?, sink?) -> {any?} | number

Read a component-field across many entities in one FFI crossing. Polymorphic on the shape of target and sink:

  • entity.batchRead(ids) / (ids, comp) / (ids, comp, field) — returns one value per entity (table or scalar). Three sigs: ids-only is a per-entity snapshot, ids+comp is one component table per entity, ids+comp+field is one field value per entity. Missing entities/components/fields surface as nil at that slot.
  • entity.batchRead(binding, comp, field, buffer) — reads each entity's field directly into a typed CPU substrate buffer (substrate.createBuffer({type="vec3"}) etc.). Returns the count of successful reads. No JSON, no per-entity Lua table allocation. Vec3/Quat field shapes today; per-entity work is pointer arithmetic + 12/16-byte memcpy.

target accepts an entity-id array or a world.bindEntities(ids) handle. Buffer sinks require a binding (the typed kernel is binding-only). The dispatcher errors loudly when the shape combination is invalid.

entity/__batchReadToBuffer

entity.__batchReadToBuffer(binding, component, field, buffer) -> number

FFI primitive — backs entity.batchRead(binding, ..., buffer). Prefer the unified entity.batchRead (auto-dispatches by argument shape); this entry stays for power users / debug code, also reachable as entity.__batchReadToBuffer. Reads each entity's component field directly into a typed CPU substrate buffer (no JSON, no per-entity Lua table allocation). After the call, read the buffer via buf:read(0, count*stride). Vec3/Quat field shapes today.

entity/__batchWriteBound

entity.__batchWriteBound(binding, component, field, values) -> number

FFI primitive — backs entity.batchWrite(binding, ...). Prefer the unified entity.batchWrite (auto-dispatches by argument shape); this entry stays for power users / debug code that wants to skip dispatch overhead, also reachable as entity.__batchWriteBound.

entity/__batchWriteFromBuffer

entity.__batchWriteFromBuffer(binding, component, field, buffer) -> number

FFI primitive — backs entity.batchWrite(binding, ..., buffer). Prefer the unified entity.batchWrite (auto-dispatches by argument shape); this entry stays for power users / debug code, also reachable as entity.__batchWriteFromBuffer. Caller fills a typed substrate buffer (substrate.createBuffer({type="vec3"})) once via buf:write(flat_floats), then the substrate memcpys 12 (Vec3) or 16 (Quat) bytes per entity into the component field. Buffer count and binding count should match — mismatch processes min(both). Vec3/Quat field shapes today.

entity/__batchWriteIds

entity.__batchWriteIds(target, component, field, source) -> number

Write a single component-field across many entities in one FFI crossing. Polymorphic on the shape of target and source:

  • entity.batchWrite(ids, comp, field, values) — per-call entity-id resolution; values is a Lua array the same length as ids (nil slots are skipped). Use for one-shot writes.
  • entity.batchWrite(binding, comp, field, values) — binding handle from world.bindEntities(ids); skips per-call id resolution. Use for per-frame writes against a stable entity set.
  • entity.batchWrite(binding, comp, field, buffer) — typed CPU buffer source (substrate.createBuffer({type="vec3"}) etc.). Eliminates per-entity {x,y,z} table allocation; per-entity work is pointer arithmetic + 12/16-byte memcpy. Vec3/Quat field shapes today.

Returns the count of successful writes. Buffer sources require a binding (the typed kernel is binding-only). The dispatcher errors loudly when the shape combination is invalid.

entity/batchAddComponent

entity.batchAddComponent(ids, type_name, data?) -> number

Add the same component type to many entities in a single FFI crossing. Mirrors entity.batchSpawn: type validation, init-data conversion, and scene-context resolution all happen once instead of per-id, and the per-entity loop runs in Rust so very large N does not stress the Luau VM string interner. Returns the count of entities the component was added to (entities already carrying an unnamed instance of the same type are skipped to avoid duplicate-add #727).

entity/batchDespawn

entity.batchDespawn(ids) -> number

Despawn many entities in a single FFI crossing. Mirrors entity.batchSpawn for teardown: the per-id resolve + lock check + despawn queue runs in Rust, so very large N costs one Luau crossing instead of N. Locked or unresolvable entities are skipped. Returns the count queued for despawn.

entity/batchProxy

entity.batchProxy(ids: {string}) -> {entity}

Resolve an array of entity ids to proxies in a single FFI call. Each output slot is the standard entity(id) proxy; ids missing from the frame cache surface as nil at that index. Use when iterating over a snapshot of entities so per-id metatable lookups don't dominate the hot path.

entity/batchRead

entity.batchRead(target, component?, field?, sink?) -> {any?} | number

Read a component-field across many entities in one FFI crossing. Polymorphic on the shape of target and sink:

  • entity.batchRead(ids) / (ids, comp) / (ids, comp, field) — returns one value per entity (table or scalar). Three sigs: ids-only is a per-entity snapshot, ids+comp is one component table per entity, ids+comp+field is one field value per entity. Missing entities/components/fields surface as nil at that slot.
  • entity.batchRead(binding, comp, field, buffer) — reads each entity's field directly into a typed CPU substrate buffer (substrate.createBuffer({type="vec3"}) etc.). Returns the count of successful reads. No JSON, no per-entity Lua table allocation. Vec3/Quat field shapes today; per-entity work is pointer arithmetic + 12/16-byte memcpy.

target accepts an entity-id array or a world.bindEntities(ids) handle. Buffer sinks require a binding (the typed kernel is binding-only). The dispatcher errors loudly when the shape combination is invalid.

entity/batchReadToBuffer

entity.batchReadToBuffer(binding, component, field, buffer) -> number

FFI primitive — backs entity.batchRead(binding, ..., buffer). Prefer the unified entity.batchRead (auto-dispatches by argument shape); this entry stays for power users / debug code, also reachable as entity.__batchReadToBuffer. Reads each entity's component field directly into a typed CPU substrate buffer (no JSON, no per-entity Lua table allocation). After the call, read the buffer via buf:read(0, count*stride). Vec3/Quat field shapes today.

entity/batchSpawn

entity.batchSpawn(count, name_prefix?) -> { string }

Spawn count entities in a single FFI crossing. Returns an array of the new entity IDs in spawn order. Each entity is given a display name of <name_prefix><i> (or entity<i> if prefix is omitted). Use this instead of looping entity.spawn when creating large entity counts — the Luau-side per-call overhead drops from N to 1.

entity/batchWrite

entity.batchWrite(target, component, field, source) -> number

Write a single component-field across many entities in one FFI crossing. Polymorphic on the shape of target and source:

  • entity.batchWrite(ids, comp, field, values) — per-call entity-id resolution; values is a Lua array the same length as ids (nil slots are skipped). Use for one-shot writes.
  • entity.batchWrite(binding, comp, field, values) — binding handle from world.bindEntities(ids); skips per-call id resolution. Use for per-frame writes against a stable entity set.
  • entity.batchWrite(binding, comp, field, buffer) — typed CPU buffer source (substrate.createBuffer({type="vec3"}) etc.). Eliminates per-entity {x,y,z} table allocation; per-entity work is pointer arithmetic + 12/16-byte memcpy. Vec3/Quat field shapes today.

Returns the count of successful writes. Buffer sources require a binding (the typed kernel is binding-only). The dispatcher errors loudly when the shape combination is invalid.

entity/batchWriteBound

entity.batchWriteBound(binding, component, field, values) -> number

FFI primitive — backs entity.batchWrite(binding, ...). Prefer the unified entity.batchWrite (auto-dispatches by argument shape); this entry stays for power users / debug code that wants to skip dispatch overhead, also reachable as entity.__batchWriteBound.

entity/batchWriteFromBuffer

entity.batchWriteFromBuffer(binding, component, field, buffer) -> number

FFI primitive — backs entity.batchWrite(binding, ..., buffer). Prefer the unified entity.batchWrite (auto-dispatches by argument shape); this entry stays for power users / debug code, also reachable as entity.__batchWriteFromBuffer. Caller fills a typed substrate buffer (substrate.createBuffer({type="vec3"})) once via buf:write(flat_floats), then the substrate memcpys 12 (Vec3) or 16 (Quat) bytes per entity into the component field. Buffer count and binding count should match — mismatch processes min(both). Vec3/Quat field shapes today.

entity/despawn

entity.despawn(target)

Despawn an entity and all its components. Pass an id string or an entity proxy to despawn that ONE entity. Pass a name to despawn EVERY entity with that name — names are not unique, so a name argument despawns all matches (not one arbitrary match). A despawned id becomes invalid after this call. Errors if no entity matches; for a bulk name despawn, locked entities are skipped with a logged summary and only error if every match is locked.

entity/duplicate

entity.duplicate(sourceId, name?) -> string

Duplicate an entity with all its components (transform, script components, attributes, visuals, material). Returns the new entity's ID.

entity/exists

entity.exists(idOrProxy) -> boolean

Check if an entity currently exists in the scene. Accepts an entity-id string or an entity proxy (as returned by entity() / entity.find() / entity.findAll()), matched by EntityId — id/proxy only, so it agrees exactly with entity(). To check by name, resolve it with entity.find(name) and test that proxy.

entity/find

entity.find(name) -> entityProxy | nil

Find the first entity matching name. Matches against exact ID and Name component (plus same-frame pending spawns), skipping anything queued for despawn in the same frame. Returns a single entity proxy, or nil if nothing matches. Names are NOT unique — use entity.findAll(name) when you need every match.

entity/findAll

entity.findAll(name?) -> {entityProxy}

Enumerate entity proxies. With a name argument, returns every entity whose Name component or ID matches (names are not unique). With no argument, returns every entity in the current snapshot, including same-frame pending spawns. Same-frame despawns are filtered out. Empty array if nothing matches.

entity/getChildren

entity.getChildren(id) -> {string}

Get an array of child entity IDs for the given entity.

entity/getDescendants

entity.getDescendants(id) -> {string}

Get every descendant entity ID (children, grandchildren, and deeper) of the given entity in breadth-first order, excluding the entity itself. Resolves the whole subtree in one linear pass over the entity set, so a large subtree costs O(total entities) rather than the O(subtree x total entities) that recursively calling getChildren pays.

entity/getParent

entity.getParent(id) -> string | nil

Get the parent entity ID, or nil if the entity is a root entity.

entity/hierarchy/swap

entity.hierarchy.swap(entityId, assetPath, opts?) -> string | nil, string?

Replace a blockout entity with a generated/imported asset, fitting the asset to the source's bounds. Returns the new entity id, or nil + error string on failure. Options: { fit = "bounds" | "bounds_xy" | "none", source_origin = "bottom" | "center" | "top", asset_origin = "bottom" | "center" | "top", keep = {string}, timeout = number }.

entity/instantiate

entity.instantiate(handle: number, count: number, fn) -> { string }

Spawn count instances of a template registered with entity.template. Each instance gets a fresh entity id; the optional fn(i) callback runs Luau-side per instance (i in 1..=count) and may return an overrides table. Override keys: name, position {x,y,z}, rotation {x,y,z,w}, scale (number | {x,y,z}), parent (id), temporary/active/hidden (bool), attributes ({ key = value }), components (script components: { [type] = partial-data } merged over the template body's data for that type — a type the template lacks is added fresh), and ecs (native components: { ecs.X{...}, ... } merged the same way over the template's ecs data). Each override supersedes the template's shared config for that instance. The whole batch crosses FFI once and lands as a single BulkInstantiate deferred mutation the engine drainer expands into bulk ECS work — per-instance cost drops from a full Lua↔Rust roundtrip to one callback + one mutation push. Inside queue() the batch is deferred onto the cross-frame ring; outside, it lands in the next frame's drain. Returns the array of newly-minted entity IDs in spawn order.

entity/proxy/active

entity(id).active -> boolean

True if this entity's own active flag is enabled. Read-only — set via entity(id).setActive(bool).

entity/proxy/activeInHierarchy

entity(id).activeInHierarchy -> boolean

True if this entity and every ancestor are active. Disabling a parent makes the whole subtree inactive in hierarchy without overwriting each child's own active flag.

entity/proxy/attribute/get

entity(id).attribute.get(key) -> value | nil

Get a custom attribute value by key.

entity/proxy/attribute/list

entity(id).attribute.list() -> {string}

List all attribute keys on this entity.

entity/proxy/attribute/remove

entity(id).attribute.remove(key)

Remove a custom attribute from this entity.

entity/proxy/attribute/set

entity(id).attribute.set(key, value)

Set a custom attribute on this entity.

entity/proxy/bounds

entity(id).bounds() -> { min, max, center, size } | nil

World-space axis-aligned bounding box of this entity's own mesh geometry. Returns a table with min/max/center/size Vec3 fields, or nil if the entity has no mesh. Use hierarchyBounds() to include all descendants.

entity/proxy/component/add

entity(id).component.add(type, data?)

Add a component to this entity.

entity/proxy/component/addSynced

entity(id).component.addSynced(type, data?) -> table | nil

Add a component that replicates to all peers. Same as add() — including returning the new component's live public proxy — but forces sync registration even without a sync {} block.

entity/proxy/component/get

entity(id).component.get(type, instanceName?) -> table | nil

Get a component's live public proxy. Returns nil if not present. Writes through the proxy hit the running component.

entity/proxy/component/getAll

entity(id).component.getAll(type?) -> { {type: string, instance: string?, data: table} }

Every component on this entity as { type, instance?, data } records, where data is the live public proxy. Pass a type to get every instance of that type.

entity/proxy/component/getInParent

entity(id).component.getInParent(type) -> table | nil

Get the nearest component of the given type on this entity or any ancestor (self inclusive), or nil. The hierarchy lookup so callers never hand-roll a getParent() walk.

entity/proxy/component/has

entity(id).component.has(type) -> boolean

Check if this entity has a component of the given type.

entity/proxy/component/list

entity(id).component.list() -> {string}

List all component type names on this entity.

entity/proxy/component/remove

entity(id).component.remove(type)

Remove a component from this entity by type name.

entity/proxy/component/setEnabled

entity(id).component.setEnabled(type, enabled)

Enable or disable a component on this entity.

entity/proxy/despawn

entity(id).despawn()

Despawn this entity and all its components.

entity/proxy/destroy

entity(id).destroy()

Destroy this entity. Alias for despawn().

entity/proxy/getChildren

entity(id).getChildren() -> {string}

Get an array of child entity IDs.

entity/proxy/getParent

entity(id).getParent() -> string | nil

Get the parent entity ID, or nil if this is a root entity.

entity/proxy/hide

entity(id).hide()

Hide this entity's mesh and all its descendants' meshes from the rendered scene.

entity/proxy/hierarchyBounds

entity(id).hierarchyBounds() -> { min, max, center, size } | nil

World-space AABB encompassing this entity AND all its descendants' mesh geometry. For leaf entities, equivalent to bounds(). Returns nil if no geometry is loaded in the subtree.

entity/proxy/id

entity(id).id -> string

Entity unique ID. Use this for all API calls that require an entityId.

entity/proxy/name

entity(id).name -> string

Entity display name (from Name component). Returns an empty string when the entity has no Name component yet. Coherent with same-frame rename() and spawn(name) in the same execute() call.

entity/proxy/position/add

entity(id).position = { entity(id).position.x + dx, entity(id).position.y + dy, entity(id).position.z + dz }

Translate by (dx, dy, dz) in world space — read components and assign.

entity/proxy/position/get

entity(id).position.x / .y / .z

Read entity world position components (e.g. local x, y, z = e.position.x, e.position.y, e.position.z).

entity/proxy/position/set

entity(id).position = { x, y, z }

Set entity world position (also accepts { x=, y=, z= }, a vector, or another entity's .position). Single axis: entity(id).position.x = n.

entity/proxy/rename

entity(id).rename(newName)

Rename this entity (updates the Name component).

entity/proxy/rotation/get

entity(id).rotation.x / .y / .z / .w  (or entity(id).rotation.eulerAngles)

Read entity rotation quaternion components, or read .eulerAngles for a { x=pitch, y=yaw, z=roll } degrees vec3.

entity/proxy/rotation/rotateAxisAngle

entity(id).rotation.rotateAxisAngle(ax, ay, az, radians)

Rotate this entity around an arbitrary axis. Composes the new rotation onto the current one (delta * current). Kept as a method (incremental operation).

entity/proxy/rotation/set

entity(id).rotation = { qx, qy, qz, qw }  |  entity(id).rotation.eulerAngles = { pitch, yaw, roll }

Set entity rotation. Assign a 4-component quaternion (auto-normalized), or assign .eulerAngles with degrees (ZYX).

entity/proxy/scale/get

entity(id).localScale.x / .y / .z

Read entity scale components.

entity/proxy/scale/set

entity(id).localScale = { x, y, z }

Set entity scale (single axis: entity(id).localScale.y = n).

entity/proxy/setActive

entity(id).setActive(active)

Enable or disable this entity (Unity-style activeSelf). Disabling fires onDisable on every script component of this entity AND its descendants — so Model/SkinnedModel hide their mesh — and stops them participating in activeInHierarchy checks; re-enabling fires onEnable again. Per-component enabled flags and each descendant's own active flag are preserved, so a child that was independently disabled stays disabled when its parent is re-activated.

entity/proxy/setParent

entity(id).setParent(parentId, opts?)

Set parent entity. The entity keeps its world-space position, rotation, and scale across the reparent; pass { keepWorldTransform = false } to keep the LOCAL transform instead, reinterpreted in the new parent's space.

entity/proxy/setTemporary

entity(id).setTemporary(temporary)

Mark this entity as temporary. Temporary entities behave normally at runtime but are excluded from scene.save() along with all their descendants. Use this to spawn helper entities from scene entrypoints that must not be baked into the saved scene.

entity/proxy/show

entity(id).show()

Show this entity's mesh and all its descendants' meshes in the rendered scene.

entity/proxy/temporary

entity(id).temporary -> boolean

True if this entity is explicitly marked temporary (carries the Temporary component). Temporary entities are excluded from scene.save() along with all their descendants. Read-only — set via entity(id).setTemporary(bool).

entity/proxy/temporaryInHierarchy

entity(id).temporaryInHierarchy -> boolean

True if this entity OR any ancestor is marked temporary — i.e. scene.save will skip this entity. Use this when you want to know whether something will actually get saved, irrespective of WHICH node in the chain flipped the flag.

entity/proxy/transform/forward

entity(id).transform.forward -> { x, y, z }

Read-only. Returns the entity's forward direction vector (local -Z axis) in world space, derived from the current rotation. Returns a normalized vec3 table.

entity/proxy/transform/right

entity(id).transform.right -> { x, y, z }

Read-only. Returns the entity's right direction vector (local +X axis) in world space, derived from the current rotation. Returns a normalized vec3 table.

entity/proxy/transform/up

entity(id).transform.up -> { x, y, z }

Read-only. Returns the entity's up direction vector (local +Y axis) in world space, derived from the current rotation. Returns a normalized vec3 table.

entity/proxy/unparent

entity(id).unparent(opts?)

Remove this entity from its parent; it becomes a root entity and keeps its world-space transform. Pass { keepWorldTransform = false } to keep the LOCAL transform instead.

entity/spawn

entity.spawn(name?, opts?) -> entityProxy

Spawn a new entity and return its PROXY (the same value entity(id) yields) — act on it immediately (entity.spawn(name).component.add(...), .localPosition = ...) with no second entity(id) round trip. The proxy still exposes .id for the rare site that needs the raw string. Entity starts with only a Transform and is invisible -- add components (Model, PointLight, etc.) to make it visible. Mirrors entity.find / entity.findAll, which also return proxies.

entity/spawnSynced

entity.spawnSynced(name?, opts?) -> entityProxy

Spawn an entity already flagged multiplayer-synced at the root — the explicit form of entity.spawn for SHARED, host-authoritative content. The entity's existence broadcasts to every peer; joiners receive it from the relay snapshot instead of spawning their own copy. Use this (NOT entity.spawn) for anything that must be the SAME object on all clients: enemies, pickups, projectiles, dynamic world props. Call it ONLY where exactly one client runs the code — a scene's onHostLoad (host-only) phase, or behind multiplayer.isHost(). Calling it in all-client code (onLoad, a component awake that runs on every peer) makes every client spawn+sync its own copy — the double-spawn 'explosion'. Equivalent to entity.spawn(name, { synced = true }); identical in every other respect.

entity/template

entity.template(def: { components?, ecs?, temporary?, active?, hidden?, attributes? }) -> number

Construct a reusable spawn template. Captures a shared entity config ONCE (validated + converted to ScriptValue) and returns a stable integer handle for entity.instantiate(handle, count, fn?) — Bevy Commands::spawn_batch analog, one FFI crossing per batch. def keys: components ({ [type] = init-data } script components), ecs (array of ecs.X{...} native components), temporary (bool — instances are non-persistent, skip-on-save), active/hidden (bool spawn state), attributes ({ key = value } applied to every instance). Every config value is a shared default; a per-instance instantiate override supersedes it. The template body is captured by value: later edits to the source table do NOT affect created templates.

  • api
  • reference