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):Targetsis a bitmask (Compass,World,Map; default all three).Mapputs 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).AddCompassMarkerand 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 panelMarkerCanvas(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: optionalMarkerIcon(hidden without icon),MarkerNameText,MarkerDistanceText,EdgeArrow(rotated towards the location while on the edge),bHideNameOnEdge. EventsOnMarkerDataChangedandOnScreenEdgeChanged. 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:
WorldMarkerClassof the call,DefaultMarkerClassof 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. Delaybelow 0 uses the delay of the layer.SetTooltipchanges 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 panelTooltipCanvas(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: optionalTitleText,MessageText(text or rich text),IconImage; overrideOnTooltipDataSetto 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);
bCancellableshows the cancel button and makes the back action cancel. A cancel closes the overlay and is reported to the callback and toOnProgressCancelled/OnProgressCancelledNative(handle). Blueprint: bindOnProgressCancelledand compare the handle.bBlockInput(default on): the overlay catches clicks on everything below it and adds a menu input context (cursor only when cancellable), priorityInputPriority(1100, above modals).- Several overlays can be open; the stack shows the newest one.
UPulseHUDProgressWidget: optionalTitleText,MessageText,ProgressBar(marquee while the progress is unknown),PercentText,CancelButton; overrideOnProgressChanged. Class order:WidgetClassof 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(payloadFPulseHUDHintPayloadwith 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 saveGetSeenHintsand restoreSetSeenHintson that object, or give the HUD an own object that implementsIPulseHUDHintStorage(HasSeenHint,MarkHintSeen,ResetSeenHints) withSetHintStorage. 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):AnchorWidgetwins, thenbAtMouse(default), elsePositionin viewport space. The menu is placed when its size is known, flipped and clamped into the screen. - Widgets:
Style.ContextMenuClass(aUPulseHUDModalwith a verticalActionList) andStyle.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) andOn 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 bindEntryPanel(a Canvas Panel) andCenterText. PulseHUDRadialopens 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.