Controls
controls is a global singleton which provides an interface for VR, desktop and mobile inputs. These are the default controls and provide your standard third/first person controls for mobile/desktop and VR locomotion. This also stores controller state e.g. mouse interactions of VR hand/controller interactions and state.
Built-in player movement
The editor enables a complete local-player controller by default. Game scripts normally extend this controller instead of creating another player movement system.
On desktop, the built-in controller:
- uses WASD or the arrow keys for movement, Shift to run, Space to jump, and the mouse to rotate the camera;
- performs floor and wall collision detection;
- moves an internal camera target and copies that target to
world.avatar.positionevery frame; - places
world.camerabehind that target according tocontrols.zoom,controls.xShift, andcontrols.yShift; - sets
controls.moving,controls.falling,controls.floorHeight, and other movement state used by the avatar animation system.
Do not move world.avatar.position directly to implement normal local-player locomotion. The desktop controller will overwrite it on its next update. To teleport the local player and its camera target together, use:
world.avatar.teleport(new U.Vector3(0, 0, 5))
world.avatar.teleport() uses avatar root/feet coordinates, so y = 0 places the avatar on a floor at y = 0. The engine handles the private desktop controller eye-height offset internally.
world.avatar loads asynchronously and may not exist during a script's first frames. Guard access until it is available.
For a third-person shooter, retain the built-in movement and configure the camera rather than replacing the controller:
controls.setZoom(3, 0)
controls.lockZoom = true
controls.xShift = 0.15
controls.yShift = 0.1
controls.flyEnabled = false
While pointer lock is active, the player ray is cast through the center of the camera. controls.intersections contains hits against objects registered with world.addRaycastable, and controls.pressing represents the desktop primary mouse button. These are the normal starting points for camera-directed shooter aiming and firing.
Examples:
// set object's local postion to the left controller position (world space)
this.update = () => {
this.position.copy(controls.left.position);
};
// Vibrate the controller when it clicks an object
this.click = (evt) => {
// Pulse at max intensity, for 20ms
// evt.hand is 'left' or 'right'
controls[evt.hand].pulse(1.0, 20);
};
this.update = () => {
// If using hand tracking
if (controls.right.inputSource == 2) {
this.position.copy(controls.right.hand["index-finger-tip"].position);
}
};
// Set nested object's rotation/quaternion to controller quaternion
this.update = () => {
// get inverse of parent quaternion
const quaternion = new U.Quaternion();
this.parent.getWorldQuaternion(quaternion);
quaternion.invert();
// copy it so that objects world rotation is 0, 0, 0
this.quaternion.copy(quaternion);
// then multiply / set from world controller rotation
this.quaternion.multiply(controls.left.quaternion);
};
// Change walking speed on desktop, and locomotion speed in VR
controls.speed = 3;
// Set camera to first person
controls.setZoom(0, 0);
// Set camera to third person
controls.setZoom(2.5, 0);
controls.zoom : Number
Controls how far away the camera is from the player on mobile and desktop. Setting controls.zoom = 0 is first person and controls.zoom > 0.5 is third person, (3 is the default).
Prefer controls.setZoom(value, seconds) when changing zoom. It clamps the value to 0–4 and optionally transitions over the supplied duration. Set controls.lockZoom = true to prevent the mouse wheel from changing zoom during gameplay.
controls.yShift : Number
How far to move the camera above player eye height. This scales is multiplied by controls.zoom:
camera.position.y += controls.zoom * controls.yShift;
This is useful if you want to move the camera higher so the player's head doesn't overlap with the cursor. The default is 0.
controls.xShift : Number
Offset the avatar on the X axis, this can help make the cursor not overlap with the player's position
The value offsets the camera horizontally relative to the controller target. This is useful for an over-the-shoulder shooter camera.
controls.speed : Number
Movement speed
controls.intersections : Array
An array of the interesected raycastable objects
controls.jumpEnabled : boolean
Flag to enable or disable jumping capability.
controls.flyEnabled : boolean
Flag to enable or disable flying capability.
controls.pressing : Boolean
Left click on desktop
controls.grabbing : Boolean
Right click on desktop
controls.floor : U.Object3D | null
The current object below the player, useful for sound effects, collisions.
controls.floorHeight : Number
The floors y position in world space.
controls.falling : boolean
Whether player controls are falling
controls.startPosition : Vector3
The initial camera position when the scene is loaded. This is used for resetting the player position on fall timeout. You can over-ride this. This can be useful for resetting to a point on a map when the player dies or falls for too long
controls.fallResetTime : Number : 2
The time before the player controls resets to the original startPostion when falling for x seconds. The default is 2 (seconds).
To disable this use: controls.fallResetTime = -1
Desktop
// Disable or enable using pointer lock. true by default
controls.desktop.pointerLockEnabled = true | false
controls.desktop.enabled = true | false
// Keep the local avatar facing the camera's horizontal direction.
// Camera pitch is ignored so the avatar remains upright. false by default.
controls.desktop.avatarFollowsCamera = true | false
controls.desktop.cursor -> cursor DOM element
// You can create your own desktop specific controls, e.g.
controls.desktop = {
update: (delta)=>{ /* your logic '*/ }
}
Mobile
controls.mobile.enable();
controls.mobile.disable();
controls.mobile = {
update: (delta) => {
/* your logic '*/
},
};
XR
controls.locomotion : Locomotion
Handles locomotion within the VR environment.
controls.locomotion.enabled : boolean
Flag to enable or disable locomotion.
controls['left' or 'right'] : Hand
Left/right controller object, containing state and interaction data for that hand.
Hand : Object
Represents the right control interface, including position, rotation, and interaction states.
| Name | Type | Description |
|---|---|---|
| position | U.Vector3 |
The current position in 3D space. |
| rotation | U.Euler |
The current rotation using Euler angles. |
| quaternion | U.Quaternion |
The current orientation using quaternion. |
| pressing | boolean |
Indicates if the control is currently being pressed. |
| grabbing | boolean |
Indicates if the control is currently grabbing an object. |
| gamepad | Gamepad |
The associated gamepad device, if any. |
| pulse | function |
Function to trigger haptic feedback. Takes: (intensity, duration) |
| hand | {} |
Object/Dict storing references to each hand/finger joint |
| raycaster | U.Raycaster |
The raycaster for detecting intersections. |
| ray | U.Ray |
The current ray used for intersection tests. |
| intersections | Array |
A list of current intersections detected by the raycaster. |
| inputSource | number |
0 = not connected, 1 = controller, 2 = hand tracking |
Hand joints:
'wrist'
'thumb-metacarpal'
'thumb-phalanx-proximal'
'thumb-phalanx-distal'
'index-finger-phalanx-proximal'
'index-finger-phalanx-intermediate'
'index-finger-phalanx-distal'
'middle-finger-phalanx-proximal'
'middle-finger-phalanx-intermediate'
'middle-finger-phalanx-distal'
'ring-finger-phalanx-proximal'
'ring-finger-phalanx-intermediate'
'ring-finger-phalanx-distal'
'pinky-finger-phalanx-proximal'
'pinky-finger-phalanx-intermediate'
'pinky-finger-phalanx-distal'
// Three.Group storing bone position, rotation
controls.left.hand['pinky-finger-phalanx-distal'] -> U.Group