Skip to content

Crosshair, Reticle, and Interaction Prompts

A crosshair is local screen presentation. An interaction prompt is usually the visible result of a local trace asking a world target what the player can do.

At a glance

Use this for: center reticles, "Press E" prompts, pickup labels, target outlines, interact hints, locked/blocked feedback - Main owner: local PlayerController/HUD for the widget; local pawn or controller for the trace; target actor/component for the prompt data - Can fail by: stale targets, wrong trace, hidden targets, duplicate widget creation, or trusting client prompts as server authority - Official docs: Create Widget, Add to Viewport, and Line Trace By Channel

The one-minute version

  • The crosshair/reticle widget usually belongs to the local player's HUD. Create it once and update it; do not create a new widget every time the trace changes.
  • A prompt target comes from a trace, overlap, selection system, or explicit focus. For first-person games, a camera-centered line trace is the usual first version.
  • The prompt text should come from the target through a clear contract, often a Blueprint Interface such as BPI_Interactable.GetInteractionPrompt.
  • UI shows "what the local player appears able to do." The server still validates the real interaction when the player presses the input.
  • Prompt priority matters when several targets are hit or overlapped. Pick one rule and make it visible in debugging.
  • Hide prompts for invalid, blocked, too-far, dead, hidden, same-team, or line-of-sight-failed targets instead of letting stale text linger.

Split the pieces

Do not put the whole system in the widget. A clean first version has three parts:

Local PlayerController / HUD:
    creates WBP_Reticle once
    exposes UpdatePrompt(PromptData)

Local pawn/controller interaction trace:
    finds current candidate target
    asks target for prompt data
    sends prompt data to WBP_Reticle

Interactable target actor/component:
    owns display name, action text, allowed range, icon, priority, enabled state

The widget draws. The trace selects. The target describes itself. The server validates actions.

Fixed reticle vs target prompt

A fixed reticle can be pure viewport UI:

PlayerController BeginPlay
-> Create Widget WBP_Reticle
-> Store ReticleWidget
-> Add to Viewport

Then interaction data updates that existing widget:

Interaction trace result changed
-> ReticleWidget.SetPromptText("Open Door")
-> ReticleWidget.SetPromptVisible(true)

That is different from a world-space label over the door. The reticle stays in the center of the screen. The prompt can still describe whatever the center trace found.

The trace path

For a first-person or over-the-shoulder interaction:

Every 0.05 seconds, or on Tick while interaction is enabled:
    CameraLocation = get player camera location
    CameraForward = get camera forward vector
    End = CameraLocation + CameraForward * InteractionRange
    LineTraceByChannel(Start = CameraLocation, End = End)

    if hit actor implements BPI_Interactable:
        ask CanShowPrompt / GetPromptData
    else:
        clear prompt

You can run this every frame for a small prototype, but a short timer interval is usually enough for prompts and easier to reason about. Do not hide expensive world searches in a widget binding.

For mouse-driven games, the target may come from cursor hit testing instead of center-screen camera tracing. The same ownership rule applies: find the target outside the widget, then push display data into the widget.

Ask the target what to show

Use a contract so the trace does not cast through every possible target class:

BPI_Interactable:
    CanInteract(PlayerPawn) -> boolean
    GetInteractionPrompt(PlayerPawn) -> PromptData
    ServerInteract(PlayerController or Pawn) request path handled elsewhere

FInteractionPromptData:
    DisplayText
    SubText
    Icon
    Priority
    bBlocked
    BlockedReason

Examples:

Target Prompt
Door unlocked Open
Door locked Locked or Requires Keycard
Pickup Pick up Medkit
Generator Repair Generator plus progress text
Friendly NPC Talk
Dead or disabled target no prompt, or a blocked prompt if useful

If an actor can have many interactable parts, put the interface on components or return component-specific data from the hit result.

Priority and stale prompts

Prompt systems fail when they do not decide which candidate wins.

Common priority rules:

  • center-screen blocking hit beats broad overlap candidates;
  • closer target beats farther target;
  • "usable now" beats "blocked" if both are visible;
  • explicit focus/highlight beats background pickups;
  • target-defined priority handles quest items or critical prompts.

Write the rule as one function:

ChooseBestPrompt(Candidates):
    remove invalid / hidden / out-of-range / line-of-sight-failed targets
    sort by Priority, then distance, then screen-center angle
    return first candidate or None

When the best target changes, clear the old one before showing the new one:

if CurrentTarget != NewTarget:
    CurrentTarget = NewTarget
    ReticleWidget.UpdatePrompt(NewPrompt or Empty)

Never let "last frame had a door" keep drawing "Open Door" after the trace now hits a wall.

Input devices and wording

Prompt text is local presentation, so it should adapt to local input device:

Keyboard/mouse: "Press E"
Gamepad: "Press X"
Touch: "Tap"
VR: "Trigger"

Keep the action meaning separate from the physical key:

PromptData.Action = Interact
HUD asks input settings/icon table for the current local display key

That avoids hard-coding "Press E" into every door and pickup. The target says "Interact with me"; the local UI says which button currently performs Interact.

Multiplayer boundary

The prompt is a local prediction of usefulness. The interaction itself is a server-authoritative request when gameplay state changes:

Client local trace:
    sees Door_12
    shows "Open Door"

Client presses Interact:
    owned pawn/controller sends ServerTryInteract(Door_12)

Server:
    validates distance, line of sight, lock state, team, alive state
    changes door/objective/inventory state

Clients:
    replicated state updates world, prompt eligibility, sounds, and UI

The local trace can be wrong by the time the server receives the request. Handle server rejection with a small local feedback path, then refresh the prompt from current state.

When it fails, and what failure looks like

Symptom Likely cause
Prompt never appears Trace starts from pawn body instead of camera, trace channel misses target, target lacks interface, or range is too short.
Prompt stays after looking away You update on hit but never clear on miss or invalid target.
Prompt flickers between targets No stable priority, trace hits child components unpredictably, or range/visibility gates fight each other.
Pressing interact does nothing The prompt is local UI, but no input event/server request is wired.
Client can open doors through walls Server trusted local prompt instead of validating line of sight/range.
Prompt shows hidden/dead objects Target state is stale or the eligibility filter ignores hidden/dead/disabled state.
UI duplicates Create Widget runs every time a target is found instead of once during HUD setup.
Split-screen prompt shows the wrong player key Widget or projection uses player index 0 instead of the owning local player.

Debug with a line trace debug draw, printed candidate actor names, current prompt data, and the exact machine that handles the final interact request.

What prompts do not do

  • A prompt does not prove the server will accept the action.
  • A reticle widget does not find targets by itself.
  • A line trace does not know which text, icon, or input key should be shown.
  • A Blueprint Interface does not store state or replicate by itself.
  • A hidden prompt does not disable target collision or gameplay interaction.
  • Showing "Press E" does not bind the Interact input action.

Lookalikes - which one do I want?

Tool Use when Watch out
Viewport reticle widget Crosshair/prompt belongs to the local screen. Create once; update display data.
Widget Component label Text or health bar should sit over/in a world actor. It is actor-attached UI, not a fixed crosshair.
Line Trace By Channel You need a precise center/camera target. It only returns hit data; prompt rules are yours.
Overlap list A nearby-use system should choose among actors in range. Needs priority and stale-target cleanup.
Widget Interaction Component You need to click a world-space UMG panel. It simulates UI input, not generic gameplay prompt selection.
Blueprint Interface Many target classes should answer the same prompt/interact questions. It is a call contract, not authority or saved state.

Rule of thumb: the HUD owns the reticle, the trace owns target selection, the target owns prompt data, and the server owns final gameplay results.

Going deeper