Web HUD library
Source: SampleProject/Plugins/UnrealReactBridge/Source/UnrealReactBridge/Public/WebHUDLibrary.h · Private/WebHUDLibrary.cpp
Static Blueprint nodes under the Web HUD category. Use these to add and remove HUD layers at named anchors on the viewport. Every node is a BlueprintFunctionLibrary static, so they are callable from any Blueprint with a world context (Level BP, Player Controller, Game Mode, Widget BP, etc.).
Add Web HUD
static FWebHUDHandle AddWebHUD(
UObject* WorldContextObject,
FName Name,
const FString& URL,
EWebHUDAnchor Anchor = EWebHUDAnchor::TopLeft,
FVector2D Padding = FVector2D(16, 16),
FVector2D Size = FVector2D(0.3, 0.2),
FVector2D MinPixelSize = FVector2D::ZeroVector,
FVector2D MaxPixelSize = FVector2D::ZeroVector,
bool bInteractive = true
);| Param | Type | Default | Required | Notes |
|---|---|---|---|---|
WorldContextObject | UObject* | — | yes | Auto-supplied by BP context |
Name | FName | — | yes | Globally unique per world; also becomes BridgeName for Sender routing |
URL | FString | — | yes | Complete URL; plugin does not prepend Default Dev URL |
Anchor | EWebHUDAnchor | TopLeft | no | One of 9 anchor positions |
Padding | FVector2D | (16, 16) px | no | Inset from anchor edge; sign auto-flipped for right/bottom; ignored for Center |
Size | FVector2D | (0.3, 0.2) | no | Viewport ratio per axis; >1 clamped to 1, <=0 rejected |
MinPixelSize | FVector2D | (0, 0) | no | CSS-style floor (px) on the final rect after Viewport × Size. Axis value 0 = no floor |
MaxPixelSize | FVector2D | (0, 0) | no | CSS-style ceiling (px). Axis value 0 = no ceiling. If Max < Min, Min wins |
bInteractive | bool | true | no | false = display-only overlay (HitTestInvisible, non-focusable, no focus forwarding into CEF). Use for bars/badges/info panels with no <input> so a runtime-mounted layer never grabs the cursor or breaks game input (WASD). Leave true for HUDs with React <input> fields |
Cross-resolution sizing (Min/MaxPixelSize)
Size is a viewport ratio so the widget scales as the viewport grows, but Padding is absolute pixels — mixing units makes 2K / 4K widgets look too cramped (Padding becomes a tiny fraction of a now-huge widget). MinPixelSize / MaxPixelSize give you CSS clamp() semantics on the final rect:
Size (0.32, 0.18) + MaxPixelSize (560, 280)
→ 720p : 410 × 130 (scales with viewport)
→ 1080p : 614 × 194 (scales with viewport)
→ 1440p : 819 × 259 → clamped → 560 × 259
→ 4K : 1229 × 389 → clamped → 560 × 280The widget grows naturally up to a ceiling, then stops — keeping HUD proportions stable across resolutions. Leave both at zero (default) to preserve pre-v1.4 behavior.
Display-only overlays (bInteractive = false)
By default a Web HUD is interactive: it can take mouse and keyboard focus so React <input> fields work. For an overlay with no inputs (HP bars, buff badges, info panels), pass bInteractive = false.
A non-interactive layer is mounted HitTestInvisible (the whole subtree — including the embedded CEF browser — ignores the mouse) and never forwards keyboard focus into CEF. This matters specifically for layers mounted at runtime in a possessed world: an interactive layer grabs keyboard focus shortly after CEF init (deferred), which makes the OS cursor appear and routes keystrokes (WASD) to the web layer instead of the PlayerController. Layers added at BeginPlay happen to avoid this because pawn possession re-focuses the viewport afterward — a runtime add has no such re-focus. bInteractive = false is the clean fix: the layer renders normally but is invisible to input.
You can flip the flag on a live layer with UWebHUDWidget::SetInteractive(bool) (e.g. on a toggled-open chat panel) — it reapplies focusability + hit-test visibility immediately.
Returns — FWebHUDHandle. Invalid (Name = None, Layer = nullptr) if validation failed; check with Handle.IsValid() on the C++ side or compare Name != None in BP.
Behavior
| Condition | Result | Log |
|---|---|---|
Name reused for existing layer | Updates in place (LoadURL, anchor, padding, size) | Warning: name exists — updated in place |
Name = NAME_None | Returns invalid handle | Error: Name is None |
URL = "" | Returns invalid handle | Error: URL is empty |
Size.X <= 0 or Size.Y <= 0 | Returns invalid handle | Error: Size must be > 0 on both axes |
Size.X > 1 or Size.Y > 1 | Clamped to 1.0 per axis | Warning: Size clamped to (X, Y) |
Anchor = Center with non-zero Padding | Padding ignored | Verbose: Padding ignored for Center anchor |
| Layout subsystem unavailable | Returns invalid handle | Error: layout subsystem unavailable |
Example
Event BeginPlay
└─ Add Web HUD
Name: "HpBar"
URL: "http://localhost:5173/hp"
Anchor: Top Left
Padding: (16, 16)
Size: (0.3, 0.1)
→ Handle (FWebHUDHandle)TIP
For dev iteration, point URL at your local Vite server (http://localhost:5173). For shipping, package your React build under Content/Web/ and use file:///... — see /guide/dev-server.
Remove Web HUD By Name
static bool RemoveWebHUDByName(UObject* WorldContextObject, FName Name);| Param | Type | Required | Notes |
|---|---|---|---|
WorldContextObject | UObject* | yes | Auto-supplied by BP context |
Name | FName | yes | The name passed to Add Web HUD |
Returns — true if a matching layer was found and removed; false otherwise.
Side effects — Unmounts the underlying widget from the viewport stack, fires NativeDestruct on the layer widget, unregisters from UUnrealReactBridgeSubsystem, and removes the layout stack itself if no layers remain.
Example
Input Action "Esc" (pressed)
└─ Remove Web HUD By Name (Name: "HpBar") → bRemovedRemove Web HUD
static bool RemoveWebHUD(const FWebHUDHandle& Handle);| Param | Type | Required | Notes |
|---|---|---|---|
Handle | FWebHUDHandle | yes | The handle returned by Add Web HUD |
Returns — true if the handle was valid and the layer was removed; false if the handle was already invalid or the layer was already gone.
Side effects — Identical to Remove Web HUD By Name.
Example
Add Web HUD → HudHandle (stored on actor)
...
Event End Play
└─ Remove Web HUD (Handle: HudHandle)Remove All Web HUDs
static void RemoveAllWebHUDs(UObject* WorldContextObject);| Param | Type | Required | Notes |
|---|---|---|---|
WorldContextObject | UObject* | yes | Auto-supplied by BP context |
Returns — void.
Side effects — Removes every registered layer in the current world; layout stack is torn down.
Example
Event "Return To Main Menu"
└─ Remove All Web HUDsIs Web HUD Active
static bool IsWebHUDActive(UObject* WorldContextObject, FName Name);BlueprintPure — safe to call from arbitrary expressions and Tick branches.
| Param | Type | Required | Notes |
|---|---|---|---|
WorldContextObject | UObject* | yes | Auto-supplied by BP context |
Name | FName | yes | The name to look up |
Returns — true if a layer with that Name is currently registered with the layout subsystem.
Example
Branch
Condition: Is Web HUD Active (Name: "PauseMenu")
True → Remove Web HUD By Name ("PauseMenu")
False → Add Web HUD ("PauseMenu", "http://localhost:5173/pause", Center, (0,0), (0.5, 0.6))Get Bundled Demo URL
static FString GetBundledDemoURL();BlueprintPure — safe to call from arbitrary expressions.
| Param | Type | Required | Notes |
|---|---|---|---|
| — | — | — | No parameters |
Returns — A file:///<plugin>/Resources/WebDemo/index.html URL pointing at the bundled offline demo HTML. Empty string if the plugin can't be located (shouldn't happen at runtime since this function lives inside the plugin).
Use it for — the no-Node review path (L_OfflineDemo), or any local "does the bridge work at all?" smoke check.
Don't use it for — shipped HUDs. The path is absolute and embeds the plugin's install location; cooked builds and end-user installs will move that path.
Example
Event BeginPlay
└─ Add Web HUD
Name: "OfflineDemo"
URL: Get Bundled Demo URL
Anchor: Center
Padding: (0, 0)
Size: (1.0, 1.0)See also
/guide/multi-hud— multi-layer HUD design walkthrough/reference/types—EWebHUDAnchor,FWebHUDHandle,FBridgeErrorInfo