Concept: Policy & Layout Architecture
CrimsonUI follows the Lyra/CommonGame three-tier pattern: a manager subsystem owns a policy, the policy owns one root layout per local player, and everything you ever see is a widget pushed onto a tag-keyed layer inside a root layout. The base types live in CrimsonCommon so any plugin can push UI; CrimsonUI supplies the concrete manager and settings that boot them.
The chain, start to pixel
| Stage | Type (plugin) | What happens |
|---|---|---|
| 1. Game instance init | UCrimsonUIManagerSubsystem (CrimsonUI) | Initialize loads UCrimsonUISettings::DefaultUIPolicyClass and creates the policy. It also subscribes to the game instance's player-added/removed events - which only exist on UCrimsonCommonGameInstance. |
| 2. Player joins | UCrimsonGameUIManagerSubsystem (CrimsonCommon) | NotifyPlayerAdded fires per local player - but only for UCrimsonCommonLocalPlayer subclasses - and forwards to the policy. |
| 3. Layout creation | UCrimsonGameUIPolicy (CrimsonCommon) | Instantiates LayoutClass for the player and adds it to the viewport, honoring LocalMultiplayerInteractionMode. |
| 4. Layer registration | UCrimsonPrimaryGameLayout (CrimsonCommon) | Your subclass registers a UCommonActivatableWidgetContainerBase (usually a stack) per UI.Layer.* tag. |
| 5. Screens | UCrimsonUIExtensions / PushWidgetToLayerStack | Everything else is a push onto a registered layer. |
Layers are a convention, not a fixed set
A layer is any tag under UI.Layer you register a stack for. The recommended set and their contracts:
| Layer | Contract | Defined by |
|---|---|---|
UI.Layer.Game | The HUD layout; exactly one widget, pushed at startup, never popped. | Your project (tag ini) |
UI.Layer.GameMenu | In-game screens that coexist with gameplay (inventory, map). | Your project (tag ini) |
UI.Layer.Menu | Pause/system menus. The escape handler pushes here. | CrimsonUI (native tag) |
UI.Layer.Modal | Dialogs and errors. The messaging subsystem pushes here. | CrimsonUI (native tag) |
UI.Layer.ContextMenu | Context menus. The context-menu subsystem pushes here. | CrimsonCommon (native tag) |
Because layers are separate stacks, pushing a menu does not disturb the HUD below it, and each stack restores its previous top widget when the current one deactivates. Stacking order on screen is simply the order of the stacks in your root layout's widget tree.
Local multiplayer
ECrimsonLocalMultiplayerInteractionMode on the policy: PrimaryOnly gives only the first local player a layout; SingleToggle keeps one interactive layout that can be handed between players (RequestPrimaryControl); Simultaneous gives every local player a full layout (split-screen co-op). Each layout is added to that player's own viewport slot.
Odds and ends worth knowing
- The manager runs a ticker mirroring
AHUD::bShowHUDonto the root layout's visibility - the console commandShowHUDtherefore hides the whole CommonUI stack too. - The policy class is loaded once at game-instance init; editing
DefaultUIPolicyClassrequires a PIE restart. GetPrimaryGameLayoutandGetCurrentUIPolicyare C++-only; Blueprint goes throughUCrimsonUIExtensions.- Dedicated servers create no layouts - every entry point above is local-player driven.