# 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](Entity.md#world-entities-and-ui-entities) use a [UI Transform](UITransform.md) instead. ## How it works ### Position is at the feet `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 `eyeHeight` above it. So an entity standing on a floor at height 0 has `position.y == 0`. ### Sector membership Every frame, any entity whose Transform changed is assigned to the [sector](Sector.md) 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 `scale` means something different to each component that reads it: | Used by | How | |---|---| | [Sprite](Sprite.md) | `scale.x` is the sprite's **width**, `scale.z` its **height**. `scale.y` is not used. | | [Model](Model.md) | 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 `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](Model.md) and **static** [sprites](Sprite.md), which turn with it fully. - 4- and 8-direction [sprites](Sprite.md#directions), which use the facing flattened onto the map to pick which of their images you see. - The [Audio Source's](AudioSource.md#sound-cone) 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](Camera.md)). ## In the editor | 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](#scale). | ## Scripting | 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: ```lua local p = entity.transform.position p.x = 5 -- only changes p entity.transform.position = p -- moves the entity ``` ### Moving things that have a Rigidbody Setting `position` teleports the entity. On an entity with a [Rigidbody](Rigidbody.md) 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. ### Rotation helpers [`mathT`](mathT.md#quaternions) has the quaternion functions. Angles are in degrees, the same as the inspector: ```lua -- 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 ``` ## Examples ### Bob up and down, and spin ```lua -- 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 ``` ### Patrol between two points ```lua -- 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 ``` ### Chase the player when they come close ```lua -- 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 ``` ### Keep an entity on the floor ```lua -- 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 ```