Currents and Coordinates: Hierarchy & Transforms
In this lesson you'll learn to
- βStudent can parent/unparent entities and manipulate local vs global transforms (translate/rotate/scale) through the hierarchy.
Student can parent/unparent entities and manipulate local vs global transforms (translate/rotate/scale) through the hierarchy.
π Reference & full walkthrough: Currents and Coordinates: Hierarchy & Transforms
π§© Your capstone piece: Pedestal sockets: banked relics parent to their pedestal and inherit its transform
Hierarchy & Transforms: Parents, Children, and Where Things Sit
So far (Part 1, Part 2) we've treated each entity as if it floats alone. It doesn't. Entities hang off each other in a tree, and each one carries a transform that says where it is. These two ideas work together, so we'll learn them together.
The family tree: parents and children
<!-- section-art:the-family-tree-parents-and-children -->

Parent-Child Hierarchy
Picture a lamp post: a tall pole, and a glowing bulb sitting on top. You could make those two separate, unrelated entities β but then if you move the pole, the bulb stays behind, floating in the air. Useless.
Instead you make the bulb a child of the pole. Now the pole is the parent, the bulb is the child, and the golden rule kicks in:
When the parent moves, every child moves with it.
Move the pole ten meters left, the bulb rides along, still perfectly on top. That parent-child link is the hierarchy, and it's why a complicated object β a car, a lantern, a whole house β can move and rotate as a single piece.
At the very top of every tree is the one root from Part 1: the simulation entity. Everything hangs somewhere below it.
Walking the tree in Verse
Your code can move around the tree. The core verbs, all real Scene Graph APIs on entity:
# Get this entity's parent. Square brackets [] because it can fail
# (the root has no parent), so we guard it with if:
if (Parent := SomeEntity.GetParent[]):
# ...we have a parent, do something with it
# Get the direct children of this entity (one level down).
Children := SomeEntity.GetEntities()
# Get the rootmost entity of the whole experience.
if (Root := SomeEntity.GetSimulationEntity[]):
# Root is the simulation entity
Read them as sentences: GetParent[] β "get my parent (might fail)." GetEntities() β "get my direct children." GetSimulationEntity[] β "get the root of everything (fails if I'm not in the scene)." Notice GetEntities() uses round brackets β it can't fail, it just hands back a list (possibly empty). GetParent[] and GetSimulationEntity[] use square brackets because they can fail. The bracket shape tells you the risk, exactly like in The Grammar of Verse.
One caution from the docs: GetEntities() only returns direct children β one level down. To reach deeper, use the Find... query verbs (we used FindDescendantEntitiesWithComponent in Part 2, and we'll lean on these queries in Part 4).
Attaching and detaching: AddEntities and RemoveFromParent
You rearrange the tree from code with two verbs:
# Make BulbEntity a child of PoleEntity.
# AddEntities takes an array, so wrap a single child in array{...}.
PoleEntity.AddEntities(array{BulbEntity})
# Detach an entity from its parent (removes it from the scene).
BulbEntity.RemoveFromParent()
AddEntities says "adopt these entities as my children." The docs note a handy detail: "If child entity already has a parent, removes the entity from its current parent and adds it to the new one" β so re-parenting is just one call. RemoveFromParent takes a thing out of the tree; the docs add that it "can be added back later by using NewParent.AddEntities" β so removing isn't deleting forever, it's unhanging.
There's a deeper meaning to parenting, too: the parent controls the lifetime of its children. Per the docs, "when an entity is removed from the scene, all its child entities and components will be removed as well." Remove the lamp post and the bulb goes with it. That's a feature β group cleanup for free.
The transform: where a thing sits, turns, and how big it is
<!-- section-art:the-transform-where-a-thing-sits-turns-and-how-b -->

Parent-Child Stack
Now, where is a thing? That's the transform. A transform is a real struct that bundles three pieces:
# The transform struct (from /Verse.org/SpatialMath) holds three things:
MyTransform : transform = transform:
Translation := vector3{ Left := 100.0, Up := 50.0, Forward := 0.0 } # position
Rotation := IdentityRotation() # facing
Scale := vector3{ Left := 1.0, Up := 1.0, Forward := 1.0 } # size
Translationβ the position, as avector3. In Verse's coordinate system avector3hasLeft,Up, andForwardparts (those are the real field names). SoUp := 50.0floats the thing 50 units up.Rotationβ which way it faces.IdentityRotation()just means "no rotation, default facing."Scaleβ how big it is.1.0on each axis is normal size;2.0would be double.
The docs describe transform as "a combination of scale, rotation, and translation, applied in that order." You rarely need all three at once β usually you just care about position.
Local vs. world: the one idea people miss
Here's the part worth slowing down for. Every entity has two transforms, and knowing the difference saves you hours.
- Local transform β where the thing sits relative to its parent. The bulb's local position might be "50 units above the pole." That stays the same no matter where the pole goes.
- Global (world) transform β where the thing actually is in the whole world, after you add up every parent above it. If the pole is at one end of the map, the bulb's global position is "that end of the map, plus 50 up."
Think of it like an address. Your local position is "third door on the left." Your global position is "third door on the left, in apartment 4, in the building at 12 Oak Street, in this city." Same spot, two ways of saying it. Move the whole building (the parent) and your local "third door on the left" is unchanged, but your global address shifts.
Verse gives you both, as extension methods on any entity:
# Read the entity's position relative to its parent.
Local := MyEntity.GetLocalTransform()
# Read the entity's true position in the world.
World := MyEntity.GetGlobalTransform()
# Move the entity relative to its parent.
MyEntity.SetLocalTransform(NewLocalTransform)
# Place the entity at an absolute spot in the world.
MyEntity.SetGlobalTransform(NewWorldTransform)
A useful detail from the docs: if an entity doesn't have a transform_component yet, SetLocalTransform "will create one and set its local transform." So you can position a bare entity and Verse fills in the position-power for you. (And GetLocalTransform on an entity with no transform just returns the identity β a clean default, not an error.)
A gameplay example: a platform that slides up to greet the player
Let's tie hierarchy and transforms together. Imagine a moving platform that rises one meter when the game starts. We write a component (Part 2 style) and use the transform verbs:
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/Simulation }
using { /Verse.org/SceneGraph }
using { /Verse.org/SpatialMath }
# Bolt this onto a platform entity to make it rise smoothly at startup.
rising_platform_component := class<final_super>(component):
# How far up to rise, in units. A knob in the editor.
@editable
var RiseHeight<public>:float = 100.0
OnSimulate<override>()<suspends>:void =
# Where are we right now, relative to our parent?
Start := Entity.GetLocalTransform()
# Build a target: same as now, but RiseHeight higher.
Target := transform:
Translation := vector3:
Left := Start.Translation.Left
Up := Start.Translation.Up + RiseHeight
Forward := Start.Translation.Forward
Rotation := Start.Rotation
Scale := Start.Scale
# Move there, then wait a moment.
Entity.SetLocalTransform(Target)
Sleep(1.0)
Read the heart of it: "get my current local transform; build a new transform that's identical except Up is RiseHeight higher; set that as my new local transform." Because we used the local transform, this works no matter where the platform's parent sits β it always rises relative to home. That's the payoff of understanding local vs. world.
Why this helps you boss around an AI
You can now ask for exactly the right thing: "Parent the bulb under the pole with AddEntities, then SetLocalTransform the bulb 50 units Up so it stays on the pole when the pole moves." That's unambiguous and buildable. Saying "put the light on the pole" leaves the helper guessing whether you mean local or world placement β and getting that wrong is the classic "why is my light floating across the map" bug.
Quick recap
- Entities form a tree: parent moves, children follow; the simulation entity is the root.
- Walk it with
GetParent[],GetEntities(),GetSimulationEntity[](square brackets = can fail). - Rearrange it with
AddEntities(array{...})andRemoveFromParent(); the parent controls its children's lifetime. - A
transformbundlesTranslation(avector3ofLeft/Up/Forward),Rotation, andScale. - Local transform = relative to parent; global transform = absolute in the world. Get/set both with
GetLocalTransform/SetLocalTransformandGetGlobalTransform/SetGlobalTransform.
Next, the finale: Building a System with the Scene Graph β we put entities, components, hierarchy, and transforms together into one working pickup.
References
Keep going β free
You've read the intro. The rest of this lesson is free for members. Sign in to continue and track your progress.
Sign in free to continue this lessonSources
/guides/verse-scene-graph-hierarchy-transformsBiloxi Studios original lesson
Add this lesson to your free study plan.
π§ The Keeperβs log
Quest complete? Chart your next heading from the π The Deeps expedition.
β΅ Take me there