2D coordinates: local, world and viewport
Place a child at the intended point despite parent rotation and camera movement.
Choose a coordinate space
| Value | Meaning |
|---|---|
| Position | Position relative to the parent CanvasItem with ordinary transform inheritance |
| GlobalPosition | Position in the current canvas; not screen pixels |
| ToGlobal(point) | Convert a point from this entity's local coordinates to canvas coordinates |
| ToLocal(point) | The inverse conversion for this entity |
| GetGlobalTransformWithCanvas() | Local-to-Viewport transform, including canvas and camera |
For an ordinary child Entity2D, multiply the parent transform by the local transform. Adding positions does not account for rotation, scale or skew. Positive X points right and positive Y points down. Rotation uses radians; RotationDegrees uses degrees.
Place a child under the pointer
In the complete example below, Marker is a child of Rig. The handler obtains a canvas-space mouse position through Rig.GetGlobalMousePosition(), converts it to Rig's local space and assigns Marker.Position. Assigning Marker.GlobalPosition directly is another option for the same canvas.
Do not use Marker.ToLocal(target) to calculate Marker.Position: that produces coordinates relative to the marker rather than its parent. Do not assign InputEventMouseButton.Position directly to GlobalPosition: the event position is in Viewport space, which the camera can transform.
Run the example
Use a C# project with Reckfell.NET.Sdk 1.0.0 and net10.0. Save both files from the drawing guide, build and attach CanvasExample.cs to the Entity2D root of a .rscene level. Run it. The white marker starts at canvas point (88,48); the blue HUD marker starts at (24,24). A left click outside UI that consumes input moves the white marker.
CanvasExample.cs
using Reckfell;
public partial class CanvasExample : Entity2D
{
public Entity2D Rig { get; private set; } = null!;
public LocalMarker Marker { get; private set; } = null!;
public Camera2D Camera { get; private set; } = null!;
public CanvasLayer Hud { get; private set; } = null!;
public LocalMarker HudMarker { get; private set; } = null!;
public override void _Ready()
{
Rig = new Entity2D { Position = new Vector2(64, 48) };
AddChild(Rig);
Marker = new LocalMarker { Position = new Vector2(24, 0) };
Rig.AddChild(Marker);
Camera = new Camera2D { Position = new Vector2(160, 120) };
AddChild(Camera);
Camera.MakeCurrent();
Hud = new CanvasLayer { Layer = 1, FollowViewportEnabled = false };
AddChild(Hud);
HudMarker = new LocalMarker { Position = new Vector2(24, 24) };
HudMarker.SetStyle(8, new Color(0.25f, 0.45f, 1));
Hud.AddChild(HudMarker);
var label = new Label { Position = new Vector2(42, 12), Text = "HUD" };
Hud.AddChild(label);
}
public override void _UnhandledInput(InputEvent input)
{
if (input is InputEventMouseButton { Pressed: true, ButtonIndex: MouseButton.Left })
Marker.Position = Rig.ToLocal(Rig.GetGlobalMousePosition());
}
}Check the calculation without a camera
Rig.Position=(64,48) and Marker.Position=(24,0) produce Marker.GlobalPosition=(88,48) with unit scale and zero rotation. Rig.Scale=(2,3) and Rig.Rotation=Pi/2 instead produce (64,96). ToLocal(ToGlobal(point)) returns the original point within float tolerance when the transform is invertible.
Zero scale makes the inverse degenerate. Hide objects with Visible instead. TopLevel=true stops parent CanvasItem transform inheritance; it does not produce screen coordinates or replace CanvasLayer for a HUD. Explicitly assign the intended transform when switching modes.
Diagnose misplaced objects
- Error depends on parent rotation: local and global coordinates were mixed.
- Error depends on the camera: Viewport coordinates were assigned as world coordinates.
- HUD and world disagree: they use different canvases. Convert through the appropriate CanvasItem.
- Position does not solve UI layout: Control anchors and containers require UI layout rules, beyond Entity2D transforms.