Skip to content

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

cpp
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
);
ParamTypeDefaultRequiredNotes
WorldContextObjectUObject*yesAuto-supplied by BP context
NameFNameyesGlobally unique per world; also becomes BridgeName for Sender routing
URLFStringyesComplete URL; plugin does not prepend Default Dev URL
AnchorEWebHUDAnchorTopLeftnoOne of 9 anchor positions
PaddingFVector2D(16, 16) pxnoInset from anchor edge; sign auto-flipped for right/bottom; ignored for Center
SizeFVector2D(0.3, 0.2)noViewport ratio per axis; >1 clamped to 1, <=0 rejected
MinPixelSizeFVector2D(0, 0)noCSS-style floor (px) on the final rect after Viewport × Size. Axis value 0 = no floor
MaxPixelSizeFVector2D(0, 0)noCSS-style ceiling (px). Axis value 0 = no ceiling. If Max < Min, Min wins
bInteractivebooltruenofalse = 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 × 280

The 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.

ReturnsFWebHUDHandle. Invalid (Name = None, Layer = nullptr) if validation failed; check with Handle.IsValid() on the C++ side or compare Name != None in BP.

Behavior

ConditionResultLog
Name reused for existing layerUpdates in place (LoadURL, anchor, padding, size)Warning: name exists — updated in place
Name = NAME_NoneReturns invalid handleError: Name is None
URL = ""Returns invalid handleError: URL is empty
Size.X <= 0 or Size.Y <= 0Returns invalid handleError: Size must be > 0 on both axes
Size.X > 1 or Size.Y > 1Clamped to 1.0 per axisWarning: Size clamped to (X, Y)
Anchor = Center with non-zero PaddingPadding ignoredVerbose: Padding ignored for Center anchor
Layout subsystem unavailableReturns invalid handleError: layout subsystem unavailable

Example

text
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

cpp
static bool RemoveWebHUDByName(UObject* WorldContextObject, FName Name);
ParamTypeRequiredNotes
WorldContextObjectUObject*yesAuto-supplied by BP context
NameFNameyesThe name passed to Add Web HUD

Returnstrue 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

text
Input Action "Esc" (pressed)
  └─ Remove Web HUD By Name (Name: "HpBar")  → bRemoved

Remove Web HUD

cpp
static bool RemoveWebHUD(const FWebHUDHandle& Handle);
ParamTypeRequiredNotes
HandleFWebHUDHandleyesThe handle returned by Add Web HUD

Returnstrue 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

text
Add Web HUD → HudHandle (stored on actor)
...
Event End Play
  └─ Remove Web HUD (Handle: HudHandle)

Remove All Web HUDs

cpp
static void RemoveAllWebHUDs(UObject* WorldContextObject);
ParamTypeRequiredNotes
WorldContextObjectUObject*yesAuto-supplied by BP context

Returnsvoid.

Side effects — Removes every registered layer in the current world; layout stack is torn down.

Example

text
Event "Return To Main Menu"
  └─ Remove All Web HUDs

Is Web HUD Active

cpp
static bool IsWebHUDActive(UObject* WorldContextObject, FName Name);

BlueprintPure — safe to call from arbitrary expressions and Tick branches.

ParamTypeRequiredNotes
WorldContextObjectUObject*yesAuto-supplied by BP context
NameFNameyesThe name to look up

Returnstrue if a layer with that Name is currently registered with the layout subsystem.

Example

text
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

cpp
static FString GetBundledDemoURL();

BlueprintPure — safe to call from arbitrary expressions.

ParamTypeRequiredNotes
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

text
Event BeginPlay
  └─ Add Web HUD
        Name:   "OfflineDemo"
        URL:    Get Bundled Demo URL
        Anchor: Center
        Padding: (0, 0)
        Size:   (1.0, 1.0)

See also

Released under the MIT License.