CanvasLayer: a HUD independent of the camera
Separate HUD and world rendering and understand why ZIndex cannot replace a canvas layer.
Create a separate canvas for the HUD
The complete example creates a world with Camera2D and a separate CanvasLayer. The blue HUD marker sits at (24,24) in its canvas. FollowViewportEnabled=false disables following the world canvas. Moving the camera changes the world view while the HUD keeps its Viewport position.
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());
}
}Also include LocalMarker.cs from the complete example. Attach CanvasExample.cs to Entity2D, not CanvasLayer. The script creates its own layer and children; no additional manually created layer is needed.
Distinguish ordering and transforms
| Setting | Purpose |
|---|---|
| CanvasLayer.Layer | Order of this canvas relative to other layers |
| CanvasItem.ZIndex | Ordering of items within their canvas |
| CanvasLayer.Offset, Rotation, Scale | Layer transform |
| FollowViewportEnabled | Follow the world canvas for effects such as parallax |
The ordinary world uses layer 0. The example HUD uses Layer=1 to render above it. A larger ZIndex on a world item cannot place it above a separate canvas with a higher Layer. Avoid relying on incidental ordering between CanvasLayers with the same number; assign distinct numbers when ordering matters.
TopLevel stops parent CanvasItem transform inheritance. It does not create an independent HUD canvas. CanvasLayer is an Entity that organizes a canvas, not a rectangular Control or an anchor layout system.
What the layer does not automate
Viewport-space HUD coordinates do not provide responsive layout. Use Control, anchors and containers for menus, and configure Viewport size and stretching separately. Fixed size here means independence from camera movement, not identical physical dimensions on every display.
CanvasLayer does not isolate input routing. A Control can consume events before _UnhandledInput. The example moves its marker only for an unhandled click. With multiple windows or SubViewports, explicitly choose the destination Viewport; a single HUD is not automatically shared across them.
Quick diagnosis
- HUD moves with the camera: check its CanvasLayer membership and FollowViewportEnabled.
- World overlaps the HUD: check Layer. ZIndex in another canvas cannot fix it.
- Marker coordinates stay fixed but resizing breaks layout: configure Control layout and stretching.
- World pointer conversion breaks after adding a HUD: obtain mouse coordinates through a CanvasItem in the intended canvas.