Mouse: viewport and local coordinates
Convert a click and distinguish Relative from ScreenRelative.
Identify the coordinate space first. Window cursor position, a viewport point, a 2D world position, and object-local position are different quantities. Matching numbers in a stationary scene do not make them interchangeable.
Choose the source
| Context | Value |
|---|---|
| Mouse event in an Entity handler | InputEventMouseButton.Position or InputEventMouseMotion.Position: event coordinates in the viewport. |
| Control GUI handler | The event may already be transformed into Control-local coordinates. Do not apply viewport conversion again. |
| Current viewport state | GetViewport().GetMousePosition(): current position, not necessarily an earlier event's position. |
| 2D object | Convert a point from the same viewport into object-local space. |
| 3D picking | Build a camera ray from a viewport point; the mouse alone supplies no depth. |
A mouse event's GlobalPosition is not your Entity2D world position. For a specific click, retain its Position; polling later can return a changed position.
Viewport → local 2D point
This file accounts for object and canvas transforms. Supply a point from the target's own viewport; attach the target to the appropriate tree and use an invertible transform.
using Reckfell;
public static class PointerCoordinates
{
// point must be in the same viewport as target.
public static Vector2 ToLocal(CanvasItem target, Vector2 point) =>
target.GetGlobalTransformWithCanvas().AffineInverse() * point;
}In _UnhandledInput(), check an InputEventMouseButton with Pressed=true and ButtonIndex=MouseButton.Left, then pass its Position to PointerCoordinates.ToLocal(this, position) for your Entity2D. The result is local to that object. _UnhandledInput() is suitable when UI should get an opportunity to consume clicks before gameplay; see event routing.
Executed example: canvas scale 2 with offset (100, 50), object position (10, 20) and scale 2. Viewport point (168, 154) becomes local (12, 16). Simply subtracting object position would be incorrect.
Do not use one viewport's matrix for another viewport's events. Calling this helper on an event already localized to a Control also produces an incorrect second conversion.
Mouse movement and camera rotation
Relative is displacement that can scale when the event is transformed. ScreenRelative retains screen displacement; ScreenVelocity is screen velocity. Neither is an absolute position.
The check doubled an event transform: Relative changed from (3, 4) to (6, 8), while ScreenRelative stayed (3, 4). Use the appropriate screen displacement for look sensitivity independent of display scaling. Do not multiply displacement already measured per event by delta again as if it were velocity; a stick specifying angular velocity needs a different calculation.
In captured mode, do not calculate turning from absolute cursor position. Process relative movement and provide cursor release for menus. OS cursor capture depends on the windowing system and is not tested by synthetic events.
For 3D
Use Camera3D.ProjectRayOrigin() and ProjectRayNormal(), then a physics query with the intended distance and mask. The camera and point must belong to the same viewport. Complete example: ray picking.
Executed checks cover canvas/object scaling, Position versus GlobalPosition, and Relative versus ScreenRelative. Live cursor movement, OS capture, GUI routing, nested viewports, Camera2D, and exports were not evaluated here.