DEMIENGINEDOCUMENTATION Everything is free
Lua API reference · Create

Find, spawn, query, destroy, and component fields.

Entity API

An entity is one “thing” in the game world — the Skyforge player, a floating rune, a trigger volume. Entities are created from authored scene data or spawned at runtime, and every other API works through the handle this module hands out.

What an entity is

An entity is a stable identity plus a bag of components (transform, sprite, rigidbody, script, and so on). The authored ID is the name you wrote in the scene document; the handle is the live value scripts pass around. One name, one handle — names never change during play, handles are just how you address the live instance.

local Entity = require("demi.entity")

local player = Entity.find("skyforge_player")   -- handle, or nil if the scene has no such entity
if player == nil then
    return
end

Entity.find(name) takes the authored entity ID and returns the handle used by every other call, or nil when nothing matches. Store the handle in a local; looking it up every frame works but is wasted work.

Finding and checking

local Entity = require("demi.entity")

local id = Entity.find("skyforge_rune")
print(Entity.exists(id))        -- true while the entity is alive
  • Entity.find(name) → handle or nil.
  • Entity.exists(id) → true while the entity is alive, false after it is destroyed (or for a stale/unknown handle).

Always check find for nil before using the handle. After destroy, treat the handle as dead and stop calling other APIs with it.

Creating and removing entities

local Entity = require("demi.entity")

local ok, err = Entity.spawn("skyforge_crate", {
    position = { 4, 2, 0 },     -- Vec3 world position (optional)
    velocity = { 1, 0, 0 },     -- Vec3 initial velocity (optional)
})
-- ok: true when creation was queued; err: nil, or a diagnostic string

local crate = Entity.find("skyforge_crate")
if crate then
    Entity.destroy(crate)
end
Entity.destroy_many({ "skyforge_crate" })    -- takes an array of IDs
CallTakesReturns
Entity.create(id, spec)Entity ID plus a component/config tabletrue when applied, false on failure
Entity.spawn(id, options)Entity ID plus position/velocity as {x,y,z} arraysok, error — success queues creation for the next step
Entity.replace(id, spec)Existing handle, replacement spec tabletrue when applied, false on failure
Entity.clone(source_id, new_id)Source handle, new entity IDtrue when copied, false on failure
Entity.destroy(id)One handletrue when destroyed
Entity.destroy_many(ids)Array of handlesNumber of entities destroyed

Entity.spawn is for simple single entities. Prefab instances — multi-entity templates with children — use Prefab.instantiate instead. Mixing the two up is the most common mistake: spawn gives you one entity, instantiate gives you a root handle for a whole tree.

Enabling and disabling

Entity.set_enabled(id, false)   -- hide and stop simulating
print(Entity.is_enabled(id))    -- false
  • Entity.set_enabled(id, enabled) — takes a boolean; returns true when applied.
  • Entity.is_enabled(id) → boolean. A disabled entity still exists (exists is true) but is skipped by update and rendering.

Components

A component is one piece of behaviour or data on an entity: a transform, a sprite, a collider, a Lua script. Scripts read and tweak components; they do not replace the component system.

local Entity = require("demi.entity")

local id = Entity.find("skyforge_rune")

print(Entity.has_component(id, "Sprite"))       -- true or false
Entity.add_component(id, "Light3D", { intensity = 2 })
Entity.remove_component(id, "Light3D")

Entity.set_field(id, "Sprite", "color", { 1, 0.4, 0.4, 1 })
-- writes one field; every other field of that component is left alone
  • Entity.add_component(id, name, fields) — attaches a component with optional initial field values. Returns true when attached; check has_component afterwards if you need certainty.
  • Entity.remove_component(id, name) — detaches the component. Returns true when removed.
  • Entity.has_component(id, name) → boolean.
  • Entity.set_field(id, component, field, value) — writes one field on one component and leaves the rest of that component unchanged. This is the right tool for runtime tweaks.

Reading configured values

local color = Entity.get_config(id, "Sprite", "color")
-- color: the last configured value of that one field, or nil when unset

Entity.get_config(id, component, field) reads one last configured field value — what the scene or a script set up — not the live simulation state. For live state use the owning system (demi.physics.rigidbody3d, demi.physics.character_controller3d, and so on). Reading get_config and expecting a moving platform's current velocity is the wrong tool.

Querying

local Entity = require("demi.entity")

local enemies = Entity.query({ tags = { "enemy" } })
-- enemies: array of handles (possibly empty)

for _, id in ipairs(enemies) do
    Entity.set_enabled(id, false)
end

Entity.query(filter) takes a filter table (tags, all, layer, include_disabled) and always returns an array of handles — empty when nothing matches, never nil. Disabled entities are skipped unless include_disabled = true. Use #enemies to count and ipairs to iterate.

Parenting and positions

Entities form a tree. Child local positions are relative to their parent; world positions are what you see on screen. Stable IDs, not pointers, describe the relationship.

local Entity = require("demi.entity")

local rune = Entity.find("skyforge_rune")
Entity.set_parent(rune, Entity.find("skyforge_player"))

print(Entity.parent(rune))          -- parent handle, or nil when at scene root
local kids = Entity.children(rune)  -- array of child handles (possibly empty)

local pos = Entity.local_position(rune)   -- {x, y} or {x, y, z}, nil if no transform
local world = Entity.world_position(rune) -- hierarchy-resolved world position, or nil
  • Entity.set_parent(id, parentOrNil) — reparents; pass nil to detach to the scene root. Returns true when applied.
  • Entity.parent(id) → parent handle or nil (root).
  • Entity.children(id) → array of handles, empty when there are none.
  • Entity.local_position(id) / Entity.world_position(id) → a position array ({x, y} or {x, y, z}), or nil when the entity has no supported transform.

For setting rotation, scale, or directions, use Transform API. Entity position helpers here are the quick read/write pair for places and parenting.

Next steps