Sprites, shapes and text are controls in the same panel tree as 3D models, drawn in the Overlay phase of the same frame. There is no second framework to learn, and no UI event system — a button is a control with a delegate on it.
You lay out against DesignResolution, 1280×720 by default.
BasicResolution is the box you actually write coordinates in,
and what it equals depends on the platform.
| Property | Desktop and web | Phones and tablets |
|---|---|---|
BasicResolution | Equals DeviceResolution — you are laying out in real pixels. | Equals DesignResolution — the design box is preserved and scaled. |
Scale | 1. | The smaller of the two axis ratios, so the whole design box stays visible. |
ExtendResolution | Equals DeviceResolution. | The design box grown along the longer axis, covering the extra space a tall phone has. |
DeviceResolution | Always the true swapchain size in pixels. | |
The practical rule: anchor anything that must stay reachable to
BasicResolution, and stretch backgrounds and edge decoration to
ExtendResolution. That gives you a layout with no letterboxing
on a 20:9 phone without maintaining a second design.
A resize recomputes Scale and
ExtendResolution only.
BasicResolution is deliberately left alone, so dragging a window
edge does not reflow a mobile layout.
| Control | For |
|---|---|
Sprite2D | Screen-space images, including live compute outputs addressed by texture name. |
Sprite3D | World-anchored billboards: markers, emotes, floating icons. |
Shape | Dots, rectangles, circles, rounded rectangles and frames for panel chrome. |
Texts | MSDF text. See Text and fonts. |
Texture | The engine-side texture handle, for custom uploads and shared references. |
icon = new Sprite2D
{
Name = "Assets/Setting.png",
Color = Colors.White,
PosX = 20, PosY = 20, Width = 48,
OnClick = () => settingPanel.Alpha = 1f
};
AddControl(icon);
divider = new Shape
{
ShapeType = ShapeType.Square,
Color = Colors.DarkGray,
PosX = 20, PosY = 80, Width = 240, Height = 1
};
AddControl(divider);
Sprites. SpriteBase carries what both sprite
types need: a Color tint,
Ext for the file extension,
OriginWidth and
OriginHeight for the source pixel size, and
SourceX/Y/Width/Height for drawing one cell out of a sheet.
Clock advances a sheet frame,
FlipX and FlipY mirror it, and
TextureOverride takes a one-shot upload — a path or
already-decoded RGBA8 pixels — which the next update consumes and clears.
Shapes. ShapeType is
Dot, Square,
Circle, RoundRect,
RectFrame, Gradual or
GradualCircle, with
Border for frame thickness. Curves are analytically
antialiased rather than scaled bitmaps, so a circle is crisp at any size.
Billboards. Sprite3D.Mode chooses how it faces
the camera: Spherical always faces you,
Cylindrical turns only around world Y so a nameplate stays
upright, and None hands orientation to the quaternion
Rotation.
Every control has OnClick and
OnTouch, and both fire from inside the control's own
Update. That has a consequence worth stating plainly: a control
you forget to update never receives a click, even though it draws perfectly.
Update returns true when it invoked
OnClick, and it clears the pending release so nothing behind
receives the same click. Update order is therefore hit-test priority: update your topmost
panel first, and use the return value to stop walking the rest.
MouseOver is maintained for you and is part of the gate, along
with Enable, Ready and
Alpha > 0. There is no bubbling model, no focus system and no
command pattern.
Controls and panels implementing IRenderOrder sort by
Layer, then Order, then insertion
index — a stable total order, so a rebuilt list does not shuffle.
RenderDomain decides when in the frame a control is
drawn, independently of where it sits in the tree.
RenderDomain | Meaning |
|---|---|
Inherit | Follow the parent. The default, and almost always right. |
Scene | Draw with the 3D scene, before post-processing. Tonemapped, bloomed, TAA-resolved. |
Overlay | Draw after everything. Untouched by post-processing, which is why UI stays crisp. |
Putting a diegetic screen inside the world in Scene is exactly
right. Putting a HUD there is how you accidentally make your text ghost under TAA.
Set the domain on the app itself and the 3D passes cost you nothing:
RenderDomain = RenderDomain.Overlay;
That is what Samples/Creator does — same controls, same
panel model, none of the scene cost. Domain resolution is inherited down the tree, so one line
on the app covers every panel under it.
| Panel | What you get |
|---|---|
BoardPanel | A framed board with a settable FrameColor. The background for most overlays. |
FrameButton | Text button with separate normal and hover colours for ground and text. |
Input | Text field with description, alignment, abbreviation and an optional clear button. |
MovePanel | Scrolling and sliding container with configurable padding, size and motion type. |
Picker, SimplePicker | Single or multi-select lists over a List<EData>, with hover colours and OnSelect. |
ImageView, FullView | Thumbnail with an optional clear affordance, and its expanded form. |
ObjectPicker | Not a widget — the 3D pick and edit panel. See Picking and editing. |
No flexbox, no constraints, no anchors, no docking. Positions are numbers you compute,
which is fine for a HUD and tedious for a settings screen with forty rows. Text entry also
delegates to the platform: Input raises the native keyboard
through DeviceServices.Dialog.ShowKeyboard, so editing looks
like the operating system rather than like your application.