Repository navigation
Transform
Inspector name: Transform · Lua: entity.transform · Public field type: Transform
The Transform says where an entity is: its position, rotation and size in the world. Every world entity gets one when it is created, and almost every other component depends on it. Sprites and models are drawn at it, physics moves it, audio plays from it, and the camera looks out from it. UI entities use a UI Transform instead.
position is the point at the bottom of the entity, where it touches the floor, not its
centre:
- A sprite is drawn upward from it.
- A sphere collider sits on it: its centre is one radius above
position. - The player camera is
eyeHeightabove it.
So an entity standing on a floor at height 0 has position.y == 0.
Every frame, any entity whose Transform changed is assigned to the sector that
contains its (x, z) position. This drives entity:GetSector(), the sector's occupancy lists and
the OnSectorChange / OnEntityEnter / OnEntityExit callbacks. It doesn't matter what moved the
entity: physics, the player controller or a script.
relativeHeight is the height of the feet above the current floor. Physics keeps it up to date for
entities with a Rigidbody and a Collider, and uses it to decide whether gravity applies.
scale means something different to each component that reads it:
| Used by | How |
|---|---|
| Sprite |
scale.x is the sprite's width, scale.z its height. scale.y is not used. |
| Model | Multiplies the model on each axis. |
| Physics |
scale.y is the body height used for ceiling collision (at least the collider's diameter). |
New entities start at 32, 32, 32.
rotation is a quaternion. The inspector shows it as X/Y/Z angles in degrees. From Lua it is a
Vector4 (x, y, z, w).
An entity faces its local +Z. With rotation 0, 0, 0 it faces +Z on the map, and rotation Y
turns it left and right the same way as camera yaw, so Y 90 faces +X. The rotation is used by:
- Models and static sprites, which turn with it fully.
- 4- and 8-direction sprites, which use the facing flattened onto the map to pick which of their images you see.
- The Audio Source's sound cone, which points along the facing, tilted up or down too.
It does not move the camera. The camera has its own yaw and pitch (see Camera).
| Field | Lua | Default | Notes |
|---|---|---|---|
| Position (X, Y, Z) | position |
0, 0, 0 |
World position of the feet. |
| Rotation (X, Y, Z) | rotation |
0, 0, 0 |
Degrees in the inspector, a quaternion in Lua. |
| Scale (X, Y, Z) | scale |
32, 32, 32 |
See Scale. |
| Property | Type | Description | |
|---|---|---|---|
isValid |
boolean | read-only |
false if the entity or its Transform is gone. |
position |
Vector3 | read/write | Feet position. Setting it updates sector membership this frame. |
rotation |
Vector4 | read/write | Quaternion (x, y, z, w). Normalised when written. |
scale |
Vector3 | read/write | |
relativeHeight |
number | read/write | Height above the floor. Physics overwrites it every frame on physics bodies. |
sectorIndex |
integer | read-only | Internal index of the current sector, -1 outside the map. Use entity:GetSector() instead: it returns the sector itself and survives map edits. |
isDirty |
boolean | read/write | Engine flag meaning "moved this frame". You shouldn't need it. |
forward |
Vector3 | read-only | Unit vector the entity faces (local +Z), from rotation. |
right |
Vector3 | read-only | Unit vector to the entity's right, from rotation. With rotation 0, 0, 0 it is -X, the same as the camera's screen right at yaw 0. |
up |
Vector3 | read-only | Unit vector out of the top of the entity (local +Y), from rotation. |
| Method | Description |
|---|---|
AddPosition(offset) |
Moves by a Vector3 offset. Same as position = position + offset. |
LookAt(point, [yawOnly]) |
Turns so the entity faces a world Vector3 point. Does nothing if the point is at the entity's position. |
LookDirection(direction, [yawOnly]) |
Turns so the entity faces along a Vector3 direction. Does nothing for a zero direction. |
DistanceTo(target) |
Straight-line distance to target, which is an Entity (its position) or a Vector3 point. |
DistanceTo2D(target) |
Like DistanceTo, but ignores height: distance across the map. |
DirectionTo(target) |
Unit Vector3 pointing at target. Zero if the target is exactly at this position. |
DirectionTo2D(target) |
Like DirectionTo, but flat (y is 0). Good for LookDirection and walking along the ground. |
Passing an Entity that has no Transform (a UI entity) to DistanceTo and the others raises an
error.
yawOnly defaults to true: the entity only turns left and right, and the height of the point or
direction is ignored. Pass false to tilt up and down as well (never any roll). Upright things
such as sprites and characters usually want the default.
You can change one component directly: entity.transform.position.x = 5 moves the entity. A
vector stored in a variable is a copy, so assign it back when you've changed it:
local p = entity.transform.position
p.x = 5 -- only changes p
entity.transform.position = p -- moves the entitySetting position teleports the entity. On an entity with a Rigidbody that is fine
for teleports and respawns. For normal movement, set the Rigidbody's velocity so physics can do
the moving and stop it at walls. A position set by a script is not checked against walls until
physics runs later in the frame, and a big jump can pass straight through a thin wall.
mathT has the quaternion functions. Angles are in degrees, the same as
the inspector:
-- Face +X (rotation Y 90 in the inspector).
entity.transform.rotation = mathT.QuaternionFromEuler(0, 90, 0)
-- Read the angles back.
local angles = mathT.QuaternionToEuler(entity.transform.rotation)
-- Turn a further 45 degrees to the left (positive Y turns from +Z toward +X).
local turn = mathT.QuaternionAngleAxis(Vector3(0, 1, 0), 45)
entity.transform.rotation = mathT.QuaternionMultiply(turn, entity.transform.rotation)
-- The direction the entity faces, as a unit Vector3.
local facing = entity.transform.forward-- Scripts/Movement/BobAndSpin.lua (pickup with a Model)
public number bobHeight = 4
public number bobSpeed = 0.5
public number spinSpeed = 90
local base
local t = 0
function Start()
base = entity.transform.position
end
function Update()
t = t + GameTime.deltaTime
local y = base.y + (mathT.Sin(t * bobSpeed * mathT.Tau) + 1) * 0.5 * bobHeight
entity.transform.position = Vector3(base.x, y, base.z)
entity.transform.rotation = mathT.QuaternionFromEuler(0, t * spinSpeed, 0)
end-- Scripts/Movement/Patrol.lua (no Rigidbody needed)
public Vector3 pointA = Vector3(0, 0, 0)
public Vector3 pointB = Vector3(128, 0, 0)
public number speed = 30
local target
function Start()
entity.transform.position = pointA
target = pointB
end
function Update()
local here = entity.transform.position
local moved = mathT.Vector3MoveTowards(here, target, speed * GameTime.deltaTime)
entity.transform.position = moved
-- Face the way we're walking (for 4/8-direction sprites).
entity.transform:LookAt(target)
if mathT.Vector3Distance(moved, target) < 0.01 then
target = (target == pointB) and pointA or pointB
end
end-- Scripts/Movement/Chase.lua (enemy with a Rigidbody)
public Entity player
public number sightRange = 300
public number speed = 40
function Update()
if player == nil or not player.isValid then return end
local t = entity.transform
local rb = entity.rigidbody
local v = rb.velocity
if t:DistanceTo2D(player) > sightRange then
rb.velocity = Vector3(0, v.y, 0)
return
end
local dir = t:DirectionTo2D(player)
t:LookDirection(dir)
rb.velocity = Vector3(dir.x * speed, v.y, dir.z * speed)
end-- Scripts/Movement/SnapToFloor.lua
-- For entities without a Rigidbody that are moved by script over sloped or stepped floors.
function Update()
local sector = entity:GetSector()
if sector == nil then return end
local p = entity.transform.position
entity.transform.position = Vector3(p.x, sector:GetFloorHeightAt(p), p.z)
end