Skip to content

Multi-HUD at named anchors

Multiple independent React HUDs at separate screen positions are just multiple React Web Browser widgets on the same canvas — there is no separate multi-HUD API. Each browser is its own React app or route at its own URL, addressed by its own BridgeName.

The constraint that makes this work: direct CanvasPanel children

Every browser must be a direct child of a CanvasPanel. This is not a style preference — it is required for more than one browser to render at all on macOS.

Here's why. Slate assigns paint layers per container:

  • SPanel::PaintArrangedChildren — the base paint path most panels use (VerticalBox, HorizontalBox, Overlay, Border, …) — gives every child the same LayerId.
  • SConstraintCanvas::OnPaint (what backs UCanvasPanel) is the exception: it assigns each child its own ChildLayerId = MaxLayerId + 1.
  • On macOS, the native web view registers itself per-frame in FSlateViewportInfo::CreateNativeLayer (SlateRHIRenderer.cpp:364), keyed on that integer in a TSet<int32>.

So two ReactWebBrowser widgets under a shared non-canvas container (a VerticalBox, say) get the same LayerId, collapse to a single registered native view, and the second one silently never renders. Nest them directly under a CanvasPanel instead, and each gets its own LayerId — and its own native layer.

So: create a plain User Widget, drag a Canvas Panel in as its root, set that panel's Visibility to Not Hit-Testable (Self Only) (a full-screen panel that hit-tests would swallow every click meant for the game; Self Only keeps the browsers inside it clickable), and drop your browsers directly onto it.

The plugin warns you if you get this wrong

UReactWebBrowser::ValidateCompiledDefaults walks the widget tree at Widget Blueprint compile time. If it finds a second ReactWebBrowser sharing a non-canvas ancestor with this one, compiling the Blueprint emits a compiler warning naming both browsers and the container, and naming the container you need to flatten. The same check runs again at RebuildWidget() for browsers created dynamically in C++ (which never go through Blueprint compilation), logged once per instance. See Known Issues → LayerId collision if you hit it.

Laying out several browsers on one canvas

text
WBP_MyMultiHUD
└── CanvasPanel (root)
    ├── ReactWebBrowser  "HpBar"    — Anchors: top-left,     Position (16, 16),   Size (320, 96)
    ├── ReactWebBrowser  "Skills"   — Anchors: bottom-center, Position (0, -32),  Size (480, 120)
    └── ReactWebBrowser  "Minimap"  — Anchors: top-right,     Position (-16, 16), Size (220, 220)

Each browser is a normal CanvasPanel slot, so screen position uses the engine's own Anchors preset grid in Details (top-left, top-center, top-right, middle-left, center, middle-right, bottom-left, bottom-center, bottom-right, plus the stretch variants) together with Position, Alignment, and Size:

text
 TopLeft       TopCenter      TopRight
 ┌──────┐     ┌──────┐      ┌──────┐
 │      │     │      │      │      │
 └──────┘     └──────┘      └──────┘
 MiddleLeft    Center       MiddleRight
 ┌──────┐     ┌──────┐      ┌──────┐
 │      │     │      │      │      │
 └──────┘     └──────┘      └──────┘
 BottomLeft   BottomCenter  BottomRight
 ┌──────┐     ┌──────┐      ┌──────┐
 │      │     │      │      │      │
 └──────┘     └──────┘      └──────┘

Nothing here is plugin-specific — it's the same UMG anchor system you'd use to lay out any CanvasPanel children. Set each ReactWebBrowser's URL and BridgeName in Details, same as a single-HUD layout.

Routing

The Sender component's TargetBridgeName matches a browser's BridgeName (case-sensitive). Empty broadcasts to every browser.

text
ReactBridgeSender (component)
  TargetBridgeName: "HpBar"   ← only the browser with BridgeName "HpBar" receives PushFloat/PushMap calls

ReactBridgeSender (component)
  TargetBridgeName: ""        ← broadcast to HpBar, Skills, Minimap, ...

Toggling a layer without breaking the layout

Use each browser's own SetVisible(bool) / SetInteractive(bool) Blueprint methods to show/hide or lock a single HUD without unmounting it — unmounting and re-mounting a browser on macOS re-triggers a one-time native focus grab (see Known Issues).

See Blueprint API reference for the full ReactWebBrowser property/method/event list.

Released under the MIT License.