Theme:

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.position every frame;
  • places world.camera behind that target according to controls.zoom, controls.xShift, and controls.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