Child Entities

Child entities give you a clean, logical way to bind related entities together so you can traverse and manage them as a tree. Add a child to a parent and you instantly get:

  • On the child: .parent (and .root) to reach back up the tree.

  • On the parent: .children plus helpers like getChild, getChildBy, getChildrenBy, and removeChild.

Child entities are declared on the server and are automatically converted as instances and available on the client via state. This keeps server authority while still giving the client read-friendly access to the structure.


Why use child entities?

  • Simple traversal: car:getChild("engine") or car:getChild("wheels/frontLeft").

  • Strong locality: Each feature (e.g., door/engine/wheel) lives as its own entity with its own lifecycle, methods and attributes, yet stays discoverable from its parent.

  • Reliable: Accurately retrieve an entity child, create links, and ensuring access aligns with expectations.

  • Predictable sync: Child links are written to the parent’s state (state.children[name] = child.id) so clients always see the same structure.

  • Safer code: The API prevents adding children that aren’t created yet, catching mistakes early.


Key concepts at a glance

  • Parent/Child/Root

    • child.parent is set automatically by addChild.

    • child.root is the topmost ancestor (useful for cross-feature coordination).

    • parent.children is a name > entity map.

  • Server > Client

    • Server uses addChild which updates self.state.children.

    • Clients read the structure from state when requested and resolve the client instances


Quick start

Create entities (server)

local car = new Car(vector3(0,0,0))
local engine = new Engine(vector3(0,0,0))

local piston = new Piston(vec3(0,0,0))
local sparkPlugs = new SparkPlugsvector3(0,0,0))
local exhaust = new Exhaust(vector3(0,0,0))

Attach children

car:addChild("engine", engine)

engine:addChild("piston", piston)
engine:addChild("sparkPlugs", sparkPlugs)
engine:addChild("exhaust", exhaust)

Under the hood this sets:

  • engine.parent = car and engine.root = car

  • car.children["engine"] = engine

  • car.state.children["engine"] = engine.id (so clients auto-see it)

  • And the same for all engine components

Use it (client or server)

local engine = car:getChild("engine")
local piston = car:getChild("engine/piston")
-- OR
local piston = engine:getChild("piston")

-- Search helpers (examples)
local anyDoor = car:getChildBy("__type", "Door")
local damagedWheels = car:getChildrenBy("health", function(h) return h and h < 0.5 end)

</> API reference

addChild(name: string, child: BaseEntity) SERVER

Add an entity as child of this entity under the specified name. children can be accessed using the methods or directly with self.children

removeChild(childOrName: BaseEntity | string) SERVER

Detach a child entity by instance or by name.

getChild(path: string) CLIENT SERVER

Fetch a child entity by name or by slash-separated path:

car:getChild("engine")
car:getChild("engine/piston")

When you use a slash-separated path, it's just syntax sugar for chained calls:

car:getChild("engine/piston")
-- is equivalent to:
car:getChild("engine"):getChild("piston")

getChildBy(key: string, value) CLIENT SERVER

Finds the first child by a custom field, value can also be a function working as a filter. whose child[key] == value or satisfies value as a function value(child[key]).

getChildrenBy(key: string, value) CLIENT SERVER

Returns a name > child dictionary table of all matches, similar to Child Entities but doesnt stop at first match


Tips

  • Attach during OnAwake Use it to attach children so the full tree is reliably ready when OnSpawn fires.

  • Root awareness If a child needs to coordinate with siblings in other branches, use self.root to get the top entity and query from there.

Last updated