Appearance
UI and Widgets
RedEngine.UI provides an application-level widget stack. Screens represent navigation, windows layer temporary UI above them, notifications are queued transient messages, and the loading screen follows world travel automatically.
The system owns presentation only. Widgets read state and request actions; replicated gameplay remains in actors, abilities, inventories, and other Fusion-aware components.
Execution model
- Authority — UI never becomes gameplay authority; requests are validated by the owning gameplay system.
- Prediction — widgets may show pending or predicted intent without treating it as confirmed state.
- Replicated — the actor, attribute, inventory, equipment, and match values displayed by widgets.
- Local only — widgets, layers, windows, notifications, loading screens, markers, animation, and audio.
Configure widgets in EngineSettings
Open the project's EngineSettings asset and assign:
- Default Widget Layer: a prefab with
WidgetLayerused as the persistent root; - Loading Screen Widget: a prefab with
ScreenWidgetshown duringGameInstancetravel.
If no default canvas is assigned, WidgetSubsystem creates an overlay canvas at runtime with a 1920×1080 reference resolution. The root survives scene travel and belongs to the GameInstance.
Widget types
| Type | Use |
|---|---|
Widget | Reusable UI block with child widgets, variables, and localization bindings |
WidgetLayer | Persistent root for instantiated widgets |
ScreenWidget | Navigable full-screen state with show/hide animation and LayerIndex |
WindowWidget | Modal or additive UI above screens; can close itself |
NotificationWidget | Temporary queued window with optional lifespan |
ScreenWidget.LayerIndex controls sibling sorting. Use low values for normal screens, higher values for overlays, dialogs, and system UI. Show and hide animation clips are queued so lifecycle callbacks occur after their visual transition.
Navigate between screens
csharp
[SerializeField] private AssetReference<MainMenuWidget> mainMenu = new();
[SerializeField] private AssetReference<SettingsWidget> settings = new();
WidgetSubsystem widgets = gameInstance.GetSubsystem<WidgetSubsystem>()!;
MainMenuWidget? menu = widgets.ShowScreen(
mainMenu,
new ScreenShownParameters(
openMode: EScreenOpenMode.ClearStackAndOpen));
SettingsWidget? settingsScreen = widgets.ShowScreen(
settings,
new ScreenShownParameters(
openMode: EScreenOpenMode.Push));Screen modes:
Pushdisables the current screen and resumes it when the new screen closes;ReplaceCurrentcloses the current screen before opening the next;ClearStackAndOpencloses the entire navigation stack.
Call CloseCurrentScreen() for Back behavior, or close a specific screen returned from ShowScreen.
For a prefab-backed widget that is not a screen, window, or notification, call WidgetSubsystem.CreateWidget(reference). The subsystem loads it through AssetReference, parents it to the UI root, constructs its variable context and activates it. Destroy the returned widget when its owning gameplay presentation is removed.
Open windows
csharp
[SerializeField] private AssetReference<ConfirmPurchaseWindow> confirmPurchase = new();
ConfirmPurchaseWindow? window = widgets.OpenWindow(
confirmPurchase,
new WindowShownParameters(
state: new ConfirmPurchaseState(itemId, price),
group: "Store.Modal",
openMode: EWindowOpenMode.Additive,
instancePolicy: EWindowInstancePolicy.SingleInstanceInGroup));Windows support additive display, replacement of all visible windows, and queued opening. Group policy is useful for ensuring only one dialog from the same flow remains open. A WindowWidget can call Close() from its button handler.
State objects are plain project-defined classes derived from ScreenState, WindowState, or NotificationState. They keep initialization data out of global fields and are available through the widget's Parameters property.
Show notifications
csharp
[SerializeField] private AssetReference<ItemReceivedNotification> itemReceived = new();
ItemReceivedNotification? toast = widgets.EnqueueNotification(
itemReceived,
new NotificationShownParameters(
state: new ItemReceivedState(itemName, quantity)));Set NotificationWidget.initialLifeSpan for automatic local dismissal, or call Hide() explicitly. Notification timing is presentation-only and therefore uses the game-instance timer manager rather than replicated Fusion state.
Variables and bindings
Every widget owns a VariablesContext that inherits values from its parent. SetVariable updates the context and notifies child widgets:
csharp
public sealed class PlayerHud : Widget
{
public void Present(Character character)
{
SetVariable("playerName", character.name);
SetVariable("health", character.Health);
SetVariable("maxHealth", character.MaxHealth);
}
}The showcase's ShowcaseTextBinding reads these variables in OnVariablesChanged. Localized TMP text can also consume variables through RedEngine's localization markup. ValueBinding<T> is available for small polled getter/setter bindings.
Child contexts inherit parent values but can override a key locally. This makes it practical to set a player name once on a screen and provide per-slot values inside inventory child widgets.
World markers
World-marker responsibilities are split between three Framework types:
WorldMarkerdescribes the world target, widget prefab, offset, distance and occlusion settings;WorldMarkerWidgetis the extensible screen-space presentation created for a registered marker;WorldMarkerSubsystemcreates marker widgets in the rootWidgetLayer, then centrally projects, range-culls and occlusion-tests every marker in itsWorld.
Use Bind(target, offset) for runtime-spawned actors and SetVisible for gameplay-driven availability. The subsystem requests the camera depth texture and runs occlusion queries in the target's PhysicsScene, so overlay views remain correctly isolated in Fusion Multi-Peer scenes. Compact Showcase uses this path for level labels, sample-specific interaction prompts and the widget-based NPC health bar; characters contain no marker scan.
Configure a marker
Add WorldMarker to the world object and configure:
- Widget Prefab — a prefab containing
WorldMarkerWidget; - Anchor and Local Offset — the transform and local position projected to the screen;
- Culling Mode — distance plus viewport, or distance only for an off-screen indicator;
- Fade Start Distance and Maximum Visible Distance — the range fade;
- Occlusion Mode —
None,Hide, orFade; - Occlusion Mask, radius, and padding — what blocks the marker and how broadly it is sampled.
For an actor created at runtime, a project-specific marker can expose its gameplay model while the widget remains presentation-only:
csharp
public sealed class ObjectiveWorldMarker : WorldMarker
{
public string Label { get; private set; } = string.Empty;
public void Initialize(Transform target, string label)
{
Label = label;
Bind(target, new Vector3(0f, 1.8f, 0f));
SetVisible(true);
}
}
public sealed class ObjectiveWorldMarkerWidget : WorldMarkerWidget
{
[SerializeField] private TMP_Text label = null!;
protected override void OnMarkerBound(WorldMarker marker)
{
if (marker is ObjectiveWorldMarker objective)
label.text = objective.Label;
}
protected override void OnMarkerUnbound(WorldMarker marker)
{
label.text = string.Empty;
}
}Do not run Camera.WorldToScreenPoint independently in every marker component. The world subsystem batches ownership, projection, culling, and occlusion and creates the widget in the correct WidgetLayer for the active peer.
Typical uses include player names, NPC health bars, interaction prompts, quest objectives, damage directions, and off-screen targets. For a marker that must remain visible at the screen edge, use DistanceOnly and clamp its visual position in the derived widget.
Loading screen and lifecycle events
WidgetSubsystem subscribes to GameInstance.OnBeginLoadWorld and OnWorldLoadComplete. It shows the configured loading widget before travel and hides it when the new world is ready. You can also call ShowLoadingScreen and HideLoadingScreen manually for non-travel content work.
The subsystem publishes OnScreenShown, OnScreenHidden, OnWindowShown, OnWindowClosed, OnNotificationShown, and OnNotificationHidden for presentation coordination and analytics.
Build the loading screen
- Create a prefab derived from
ScreenWidget. - Add a full-screen background, progress or activity indicator, and optional status text.
- Assign a hide animation to the widget so the new world is revealed after a short transition.
- Assign the prefab to EngineSettings > Loading Screen Widget.
The subsystem shows it on GameInstance.OnBeginLoadWorld. It hides it on either OnWorldLoadComplete or OnTravelFailure, so a failed connection cannot leave the UI permanently covered. BeginHide lets the configured hide animation complete before the widget is destroyed.
For local content work that does not perform travel, bracket the operation manually:
csharp
WidgetSubsystem widgets = gameInstance.GetSubsystem<WidgetSubsystem>()!;
widgets.ShowLoadingScreen();
try
{
await LoadFrontendCatalogAsync();
}
finally
{
widgets.HideLoadingScreen();
}The manual API is presentation state, not network synchronization. For additive Fusion levels, await World.LoadSceneAsync on the authoritative flow and decide separately whether every peer needs a full-screen overlay or a smaller streaming indicator.
Next: Diagnostics · Developer Console.