Luduvo Contents
Theme
On this page

Instances

The fundamental building block of Luduvo worlds is the Instance. Every single entity in a place (whether it’s a solid geometric part, a dynamic point light, a user interface button, or a player character) is an instance.

Gameplay code spends most of its time finding instances, moving them around the tree, copying them, and tearing them down. This page covers the rules each of those operations follows.

How the engine stores and walks that tree inside: Entities and Instances.

The place tree#

The engine arranges all instances into an n-ary tree known as the place tree. Each instance carries:

IdentityUnique 64-bit id and nameParentThe instance above itChildrenThe instances below itComponentsTransform, mesh, materialTags, attributes, script bindings

The parent is the instance directly above it in the tree, and the children are the instances directly below. The components hold the transform, mesh and material, tags, attributes, and script bindings.

Because parent-child relationships define both spatial coordinate hierarchies and lifecycle visibility, manipulating instances in scripts directly alters this underlying tree structure.

Accessing instances#

Nearly every script requires referencing other objects in the world. Luduvo provides two primary ways to locate instances: direct lookup using handles and hierarchical tree search via FindFirstChild.

Direct O(1) access with handles#

Locating an object by path, as in workspace.Buildings.RedTeam.Door, walks one child array per path segment, and the path breaks as soon as someone reorganizes the hierarchy.

Luduvo answers this with handles:

Name in editorAssigned to one instanceRegistered at startupName to instanceInjected into handlesOne global script tableResolved by nameNo hierarchy walking

In the editor, select an instance and assign it a handle name, such as LobbyDoor or BossSpawn. During place startup, the engine registers these named shortcuts into the global handles table. Any script then resolves the name directly:

Luau
-- Resolved by name, no walking the hierarchylocal door = handles.LobbyDoordoor.Position = Vector3.new(0, 10, 0)

Accessing handles.LobbyDoor resolves in constant time however deep the instance sits, and the name keeps working when someone reorganizes the hierarchy. Prefer handles when referencing statically placed level geometry and critical scene objects.

Searching the hierarchy: FindFirstChild#

When working with dynamically spawned entities or unhandled objects, search an instance’s direct children at runtime using FindFirstChild:

Luau
local door = self.Parent:FindFirstChild("Door")if door then	door.Position = Vector3.new(0, 4, 0)else	warn("No child named 'Door' found under", self.Parent.Name)end

Calling FindFirstChild(name) searches the instance’s direct children only, and returns nil when no child of that name exists. Children further down the tree are not searched. Names are interned as 32-bit ids, so the search compares integers rather than strings.

The search runs over every child in turn, so its cost grows with the number of children. Whenever possible, favor direct O(1) lookups via handles, or cache the resolved instance into a local variable rather than querying FindFirstChild every frame inside Update.

Managing the hierarchy: Parent#

Control an instance’s location in the place tree using the Parent property:

Luau
-- Attach this instance under a new containerself.Parent = handles.DebrisFolder-- Detach from the active hierarchyself.Parent = nil

Whenever assigning instance.Parent = newParent, the engine does this:

Unlink old parentDrops out of its childrenAttach to new parentAdded as its last childTransform marked staleNot recomputed on the spotTransform recomputedAfter the physics step

The engine removes the instance from its old parent’s children and adds it to newParent as its last child. The world transform is not recomputed on the spot: the engine recomputes it relative to the new parent after the physics step of the same tick.

Setting Parent = nil unlinks the instance and its entire subtree from the active world hierarchy. Both the Box3D physics solver and the rendering pipeline immediately ignore detached entities. The detached instance stays alive for as long as a Luau variable holds a reference to it.

Creating instances: Clone#

To duplicate existing entities or spawn prefabricated templates during gameplay, use Clone:

Luau
-- Deep-copy self and its descendantslocal projectile = handles.BulletTemplate:Clone()-- Configure initial stateprojectile.Position = self.Position + Vector3.new(0, 2, 0)-- Insert into the active simulation worldprojectile.Parent = handles.WorldFolder

What Clone does#

Calling Clone triggers a recursive deep-copy of the target instance and its entire subtree:

Copy every nodeThe instance and its subtreeCopy component valuesTransforms, colors, materials, attributesMint new idsUnique 64-bit per nodeReturn unparented rootParent is nil until assigned

The engine copies every node in the subtree along with its saved component values: spatial transforms, colors, materials, collision filters, and custom attributes. Every cloned node receives a brand-new, unique 64-bit entity ID. The returned root arrives with Parent = nil: it is a floating subtree, part of no world, until explicitly attached by assigning its Parent.

Cleaning up: Destroy#

Invoke Destroy to tear down an instance and all its descendants:

Luau
-- Destroy the instance and all its descendants (Server only)self:Destroy()

Component cleanup and lifecycle#

Parent = nil detaches the subtree. Destroy executes engine teardown:

Unregister componentsDetach and lockParent can never change againDisconnect signalsFrees listeners and closuresMark handle destroyedLater indexing raises an errorRemove Box3D bodiesBodies, shapes, constraintsRelease GPU resourcesVertex, mesh, texture handlesHalt script threadsAcross the whole subtree

The engine recursively unlinks the instance from its parent, sets Parent = nil, and permanently locks the Parent property so that scripts can never re-parent or resurrect it. Unregistration then removes all rigid bodies, collision shapes, and constraints from the Box3D physics world, releases vertex buffers, mesh resources, and texture handles from the renderer, and terminates all running script threads bound across the subtree.

Signal disconnection severs and frees all event listeners and callbacks connected to any Signal across the instance, so no closure keeps the subtree alive. Last, the handle itself is marked destroyed: if Luau scripts index or mutate properties on the destroyed instance later on, the engine raises the runtime error attempt to index a destroyed Instance.

Destroy is server-only. Calling Destroy in a client script (*.client.lua) throws runtime error 'Destroy' can only be called from the server. Connected clients observe entity deletions when the server replicates state deltas across the network.