Mouse Movement
React to the player's mouse movement in real time, to drive drag gestures, spin objects, or steer custom cameras.
Your scene can read the player's raw mouse movement on every frame, and use it to drive real-time interactions: drag and swipe gestures, spinning or sliding objects as the player drags them, or steering a custom camera like in a first-person shooter.
To do this, read the screenDelta property of the PrimaryPointerInfo component on engine.RootEntity. This property is a Vector2 that reports how many pixels the mouse moved since the last frame. Positive x values mean the mouse moved right, positive y values mean it moved up, matching the screen's origin at the bottom-left corner. On frames where the mouse doesn't move, both values are 0.
import { engine, PrimaryPointerInfo } from '@dcl/sdk/ecs'
engine.addSystem(() => {
const delta = PrimaryPointerInfo.getOrNull(engine.RootEntity)?.screenDelta
if (!delta) return
console.log(`mouse moved: ${delta.x}, ${delta.y}`)
})Since screenDelta only holds the movement of a single frame, always read it inside a system, so no movement goes unnoticed between frames.
๐ Note: Avoid referring to the engine.RootEntity on the initial scene loading, because that can result in errors if the entities are not initialized yet. To avoid this problem, always refer to the entity inside a system. It will always be available, because the first execution of the system is called once the scene is already properly initialized.
๐ Note: What's described in this document is only relevant to players on desktop. On the mobile app, input is touch-based and there is no free-moving cursor, so screenDelta always reports 0. See Input on mobile for how input works on touch devices, and Detect platform to offer alternative controls to mobile players.
Mouse movement while the cursor is locked
screenDelta behaves differently from the other properties of PrimaryPointerInfo when the cursor is locked: screenCoordinates freezes at the screen center and worldRayDirection always reports the ray at the center of the screen, but screenDelta keeps reporting the raw mouse movement on every frame. This makes it the only way to read mouse movement while the cursor is locked, which is exactly what you need for custom camera or aiming controls.
Drag to rotate an object
The following example lets the player spin a cube by dragging the mouse sideways while holding the pointer button down:
The same pattern works for any drag interaction: slide an entity along a rail using delta.x as an offset, scrub through an animation, or detect a quick swipe by checking for large delta values.
Mouselook camera controls
Because screenDelta keeps working while the cursor is locked, you can use it to build mouselook controls for a virtual camera. To do this, accumulate the mouse movement into yaw and pitch angles, then apply them to the camera's rotation on every frame.
In the following example, the player clicks a box to switch to a virtual camera that they can steer with the mouse, and presses the secondary button (F or right-click) to return to the default camera. While the virtual camera is active, the scene also locks the cursor and freezes the avatar with an input modifier, so the player doesn't walk around blindly while steering the camera.
A few things to note about this example:
The system subtracts
delta.yfrom the pitch, so that moving the mouse up tilts the camera up. Flip that sign if you prefer inverted vertical controls.The pitch is clamped to a range of -85 to 85 degrees, so the camera can never flip over backwards.
The
SENSITIVITYconstant expresses degrees of rotation per pixel of mouse movement, tweak it to taste.
๐ก Tip: The player can unlock the cursor at any time by pressing Esc or right-clicking, which stops the camera from responding to the mouse. Always give players a clear way to fully exit the camera mode, like the secondary button in the example above. You can also re-lock the cursor from code, see Lock or unlock the cursor.
Related topics
To read the cursor's absolute position on the screen instead of its movement, use the screenCoordinates property of the same component. See Check the player's cursor position.
To find out which entity is under the cursor, combine the worldRayDirection property with a raycast. See Raycasting.
Last updated