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 ornil.Entity.exists(id)→truewhile the entity is alive,falseafter 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
| Call | Takes | Returns |
|---|---|---|
Entity.create(id, spec) | Entity ID plus a component/config table | true when applied, false on failure |
Entity.spawn(id, options) | Entity ID plus position/velocity as {x,y,z} arrays | ok, error — success queues creation for the next step |
Entity.replace(id, spec) | Existing handle, replacement spec table | true when applied, false on failure |
Entity.clone(source_id, new_id) | Source handle, new entity ID | true when copied, false on failure |
Entity.destroy(id) | One handle | true when destroyed |
Entity.destroy_many(ids) | Array of handles | Number 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; returnstruewhen applied.Entity.is_enabled(id)→ boolean. A disabled entity still exists (existsistrue) 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. Returnstruewhen attached; checkhas_componentafterwards if you need certainty.Entity.remove_component(id, name)— detaches the component. Returnstruewhen 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; passnilto detach to the scene root. Returnstruewhen applied.Entity.parent(id)→ parent handle ornil(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}), ornilwhen 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
- Transform API — rotation, scale, facing directions.
- Scene & prefab API — spawning multi-entity templates.
- Script properties — exposing entity fields to the editor.