Markers, tooltips and overlays

Besides stacks, modals, compass and notifications (see HUD system), PulseHUD has these HUD elements:

Element Shown with Needs in the layout
World markers AddMarker(Data, World) UPulseHUDWorldMarkerLayer
Tooltips UPulseHUDTooltipComponent on a widget, or ShowTooltip UPulseHUDTooltipLayer
Highlight frame ShowHighlight, or a hint with HighlightWidget UPulseHUDHighlightLayer
Progress overlay PushProgress / UpdateProgress / PopProgress stack Pulse.HUD.Stack.Overlay (falls back to the main stack)
Subtitles PushSubtitle notification area Pulse.HUD.Notification.Subtitle
Tutorial hints ShowHint notification area Pulse.HUD.Notification.Hint
Context menu ShowContextMenu, or Make Context Menu Request + Show Modal And Wait modal stack
Radial menu UPulseRadialMenuWidget pushed on a stack (PushWidgetToStack) main stack (or any stack)

The default layout /PulseCore/HUD/W_DefaultHUDWidget contains all of them. The layers, areas and stacks register themselves with the HUD of their player, so an own layout only needs the widgets it wants to use, anywhere in its tree. All functions are on UPulseHUDSubsystem; UPulseHUDLibrary has Blueprint versions that take the player controller.

Where to put them in an own layout:

  • World marker layer: over the whole screen inside GameplayRoot, below the other gameplay elements (it hides with the HUD). Set it to Not Hit-Testable.
  • Subtitle and hint areas: inside GameplayRoot (subtitles at the bottom center, hints at a side).
  • Overlay stack: above the modal stack, so a progress overlay covers modals.
  • Highlight and tooltip layers: last, over everything, Not Hit-Testable.

World markers

A marker can show on the compass, on the screen, or both. Both use the same data (FPulseHUDMarkerData) and one handle:

FPulseHUDMarkerData data;
data.ActorToTrack = Chest;
data.WorldOffset = FVector(0, 0, 120);   // above the chest
data.Name = INVTEXT("Treasure");
data.bShowDistance = true;

const FPulseHUDHandle handle = HUD->AddMarker(data, static_cast<int32>(EPulseHUDMarkerTargets::Compass | EPulseHUDMarkerTargets::World));
HUD->UpdateMarker(handle, data);   // changes both
HUD->RemoveMarker(handle);         // removes both
  • AddMarker(Data, Targets, CompassMarkerClass, WorldMarkerClass): Targets is a bitmask (Compass, World, Map; default all three). Map puts the marker on the minimap and the world map of PulseMap. Returns an invalid handle if no target could show the marker.
  • UpdateMarker, RemoveMarker, RemoveMarkersForActor. Markers are cleared when the HUD is rebuilt (map travel).
  • AddCompassMarker and the other compass functions still work and only use the compass (no record, never on maps).

Every marker of AddMarker has a record (FPulseHUDMarkerRecord: handle, data, targets). Other widgets read them instead of keeping a list of their own:

  • GetMarkerRecords() (C++) and Get Marker Records (Blueprint, filtered by targets), FindMarkerRecord(Handle).
  • OnMarkerChanged / OnMarkerChangedNative: added, updated or removed, also when the compass or the world layer removes a marker of a destroyed actor and when the HUD is rebuilt (removed for each).

Marker data for the screen:

Field Effect on world markers
WorldLocation / ActorToTrack Where the marker is. A tracked actor wins; once it is destroyed the marker is removed (bRemoveWhenActorInvalid).
WorldOffset Added to the location (e.g. above the head).
bShowOnScreenEdge While the location is off screen (also behind the camera), the marker stays on the screen edge and its EdgeArrow points at the location. Off: the marker hides.
MinDistance, MaxDistance Visible range in DistanceUnits (0 = no maximum).
Icon, Name, bShowDistance, DistanceUnits, CustomData As on the compass.

Layer and widget:

  • UPulseHUDWorldMarkerLayer: bound canvas panel MarkerCanvas (required), DefaultMarkerClass, EdgeMargin (distance of edge markers to the screen edge). It projects all markers once per frame with one camera lookup and does nothing without markers. Marker widgets are pooled.
  • UPulseHUDWorldMarker: optional MarkerIcon (hidden without icon), MarkerNameText, MarkerDistanceText, EdgeArrow (rotated towards the location while on the edge), bHideNameOnEdge. Events OnMarkerDataChanged and OnScreenEdgeChanged. Markers are moved with a render translation, so they cost no layout work; the distance text only changes when the shown number changes.
  • Class order: WorldMarkerClass of the call, DefaultMarkerClass of the layer, Style.WorldMarkerClass.

The placement math (projection, edge clamp, arrow direction) is in Pulse::HUD::ScreenMath and covered by the PulseHUD.ScreenMath specs.

Tooltips

Add the UI component Pulse HUD Tooltip (UPulseHUDTooltipComponent) to any widget in the UMG designer (UI Components) and fill in Tooltip:

  • Mouse hover shows the tooltip at the mouse after the delay and follows the mouse.
  • Gamepad or keyboard focus inside the widget (bShowOnFocus) shows it next to the widget.
  • Delay below 0 uses the delay of the layer. SetTooltip changes the tooltip, also while it is shown.

FPulseHUDTooltipData: Title, Text, Icon, WidgetClass (own tooltip widget), Payload (any struct, e.g. item stats for a custom widget). From code: ShowTooltip(Data, Anchor, bFollowMouse, Delay) and HideTooltip(Anchor) (only hides the tooltip of that anchor; null hides any).

  • UPulseHUDTooltipLayer: bound canvas panel TooltipCanvas (required), DefaultTooltipClass, DefaultDelay (0.4 s), MouseOffset, AnchorGap. The tooltip is flipped to the other side of the mouse or widget where it would leave the screen and clamped into it. One tooltip at a time.
  • UPulseHUDTooltipWidget: optional TitleText, MessageText (text or rich text), IconImage; override OnTooltipDataSet to read the payload.

Highlight frame

ShowHighlight(Target, Duration) draws a frame around a widget, for example the button a tutorial talks about. Duration 0 keeps it until ClearHighlight(Handle). One highlight at a time; a new one replaces the old one. The frame follows the widget and hides while the widget is not visible.

UPulseHUDHighlightLayer: bound canvas panel HighlightCanvas with the frame widget HighlightFrame in it (both required), optional HighlightGlow that pulses (render opacity between PulseOpacityRange, one pulse every PulseDuration seconds, default 1.6), FramePadding, event On Highlight Changed. It does nothing while no highlight is shown.

Progress overlay

For loading, crafting or waiting for other players:

FPulseHUDProgressRequest request;
request.Title = INVTEXT("Crafting");
request.Progress = 0.f;          // below 0: unknown, the bar animates
request.bCancellable = true;

const FPulseHUDHandle handle = HUD->PushProgress(request, [this]() { CancelCrafting(); });
HUD->UpdateProgress(handle, 0.5f, INVTEXT("Forging the blade"));   // empty text keeps the old one
HUD->PopProgress(handle);
  • bCancellable shows the cancel button and makes the back action cancel. A cancel closes the overlay and is reported to the callback and to OnProgressCancelled / OnProgressCancelledNative (handle). Blueprint: bind OnProgressCancelled and compare the handle.
  • bBlockInput (default on): the overlay catches clicks on everything below it and adds a menu input context (cursor only when cancellable), priority InputPriority (1100, above modals).
  • Several overlays can be open; the stack shows the newest one.
  • UPulseHUDProgressWidget: optional TitleText, MessageText, ProgressBar (marquee while the progress is unknown), PercentText, CancelButton; override OnProgressChanged. Class order: WidgetClass of the request, Style.ProgressClass.

Subtitles

PushSubtitle(Speaker, Text, Duration) shows a subtitle in the notification area Pulse.HUD.Notification.Subtitle. Subtitles use the notification queue (order, maximum visible, duration factor of the player) and show the speaker in the optional SpeakerText of the notification widget (FPulseHUDNotification::Speaker).

The player setting Subtitles (Pulse.Setting.HUD.Subtitles, tab Interface) turns them off; PushSubtitle then returns an invalid handle.

Tutorial hints

FPulseHUDHint hint;
hint.HintKey = TEXT("Tutorial.Inventory");
hint.Title = INVTEXT("Inventory");
hint.Text = INVTEXT("Open the inventory with <input id=\"/Game/Input/IA_Inventory.IA_Inventory\"/>.");
hint.HighlightWidget = InventoryButton;   // optional frame while the hint is shown
HUD->ShowHint(hint);
  • Hints are notifications in the area Pulse.HUD.Notification.Hint (payload FPulseHUDHintPayload with the key).
  • bShowOnce (default): the hint is skipped if the player has seen it and remembered as seen when it is shown. HasSeenHint(Key), ResetSeenHints() (e.g. “Show tips again” in the options).
  • Hint storage: the HUD keeps seen hints in a UPulseHUDHintMemoryStorage (memory only, per local player). To keep them across sessions, either save GetSeenHints and restore SetSeenHints on that object, or give the HUD an own object that implements IPulseHUDHintStorage (HasSeenHint, MarkHintSeen, ResetSeenHints) with SetHintStorage. The interface keeps no state; the implementing object does.

Context menu

A context menu is a modal without backdrop that opens at the mouse, at a position or next to a widget. Its result is the id of the chosen action, or Cancel (back action or click outside the menu).

TArray<FPulseHUDModalAction> actions;
actions.Emplace(TEXT("Use"), INVTEXT("Use"));
actions.Emplace(TEXT("Drop"), INVTEXT("Drop"));

FPulseHUDContextMenuPayload placement;      // default: at the mouse
placement.AnchorWidget = SlotWidget;        // gamepad: next to the focused slot

HUD->ShowContextMenu(actions, placement, [this](FName Result) { /* "Use", "Drop" or "Cancel" */ });

Blueprint: Make Context Menu Request (actions, placement, title) and Show Modal And Wait. In C++ FPulseHUDModalRequest::MakeContextMenu builds the same request for PushModal.

  • Placement (FPulseHUDContextMenuPayload): AnchorWidget wins, then bAtMouse (default), else Position in viewport space. The menu is placed when its size is known, flipped and clamped into the screen.
  • Widgets: Style.ContextMenuClass (a UPulseHUDModal with a vertical ActionList) and Style.ContextMenuContainerClass (UPulseHUDContextMenuContainer: a canvas panel over the screen without backdrop, PointOffset, AnchorGap).

Radial menu

A radial menu shows the entries of one level around the middle of the screen; the mouse or a stick points at one, a click, the accept button or ConfirmHighlighted chooses it. Pings use it (see Pings); any game menu can.

The menu is data, FPulseRadialMenu: a flat list of FPulseRadialMenuEntry (id, parent id, label, icon, color role, a tag for the game, enabled), the angle of the first segment and a dead zone. An entry whose id other entries name as their parent opens that level; Back, Escape or the right mouse button go one level up and close the menu on the first level. The math is pure in Pulse::HUD::Radial (PulseHUDRadialMenu.h, specs PulseHUD.Radial):

Function What it does
DirectionToAngle Angle of a screen direction (0 up, clockwise)
FindSegment / FindSegmentFromDirection The segment an angle or an offset points at (none inside the dead zone)
GetSegmentOffset Where an entry sits around the middle
GetChildren, GetParentLevel, Choose The levels: entries of a level, the level above, open or choose
ValidateMenu Ids set and unique, parents exist, no cycles
FPulseRadialMenu menu;
FPulseRadialMenuEntry& emote = menu.Entries.AddDefaulted_GetRef();
emote.Id = TEXT("Emote");                    // opens its level
FPulseRadialMenuEntry& wave = menu.Entries.AddDefaulted_GetRef();
wave.Id = TEXT("Wave");
wave.ParentId = TEXT("Emote");               // a leaf on that level

UPulseRadialMenuWidget* widget = HUD->PushWidgetToStack<UPulseRadialMenuWidget>(Pulse::HUD::Stack::Stack_Main,
	UPulseRadialMenuWidget::StaticClass(), [&menu](UPulseRadialMenuWidget& InWidget) { InWidget.SetMenu(menu); });
widget->OnEntryChosenNative.AddUObject(this, &UMyThing::HandleChosen);
  • Events: On Entry Chosen (a leaf, once, then the menu closes) and On Cancelled (closed without a choice, also when the stack is cleared).
  • Input: while active the menu applies Input Context (cursor, UI only) through the Pulse player input manager, or sets the input mode itself when the manager is off; Center Cursor puts the cursor in the middle.
  • Look: entries use the style role Entry Style Role (Pulse.UI.Panel.HUD), the highlighted one Highlight Color Role (Pulse.UI.Color.Accent) and Highlight Scale; icons are tinted with the color role of their entry. A Blueprint child can bind EntryPanel (a Canvas Panel) and CenterText.
  • PulseHUDRadial opens a test menu with two levels.

Style classes

New entries of FPulseHUDStyle (Project Settings > Pulse > Core > HUD > Style), defaults in /PulseCore/HUD/...:

Field Default
WorldMarkerClass WorldMarker/W_DefaultWorldMarker
TooltipClass Tooltip/W_DefaultTooltip
ProgressClass Progress/W_DefaultProgress
ContextMenuClass ContextMenu/W_DefaultContextMenu
ContextMenuContainerClass ContextMenu/W_ContextMenuContainer

Default layers and areas: WorldMarker/W_DefaultWorldMarkerLayer, Tooltip/W_DefaultTooltipLayer, Highlight/W_DefaultHighlightLayer, Notification/W_DefaultSubtitleArea (+ W_DefaultSubtitle), Notification/W_DefaultHintArea, Stacks/W_DefaultOverlayStack.

Cheats

Cheat Does
PulseHUDMarker [distance] Adds a compass and world marker in front of the player (distance in cm, default 1000).
PulseHUDMarkersClear Removes the markers added by PulseHUDMarker.
PulseHUDRadial Opens a radial menu with two levels and prints the chosen entry.
PulseHUDProgress <percent> Opens a cancellable progress overlay, or sets the progress of the open one (0 to 100, below 0 = unknown).
PulseHUDProgressClose Closes the overlay of PulseHUDProgress.
PulseHUDSubtitle <text> Shows a subtitle.
PulseHUDHint <text> Shows a hint (every time) that highlights the compass.
PulseHUDContextMenu Opens a context menu at the mouse and prints the result.
PulseHUDTooltip [text] Shows a tooltip at the mouse, or hides it again.
PulseHUDShowcase [1] Shows notifications, a hint, a subtitle and markers at once; 1 adds the progress overlay (does not block input) and a tooltip.

The HUD snapshot (Pulse Debugger, gameplay debugger) has a section “Markers, tooltips, overlays” with the markers, the shown tooltip, highlight and progress overlays.


PulseCore plugin documentation.

This site uses Just the Docs, a documentation theme for Jekyll.