Skip to content

Project World Location to Widget Position and off-screen markers

At a glance

Lives in: Blueprint API / Viewport - Target: Widget Layout Library - Returns: a Vector2D widget position and a Boolean success value - Can fail: yes; branch on the Boolean before using the position - Official docs: Project World Location to Widget Position

The one-minute version

  • The node asks one PlayerController to project a 3D world point through that player's camera, then converts the result into 2D coordinates suitable for UMG layout.
  • The Screen Position output is a Vector2D in widget/viewport units, with viewport quality scale and DPI scale removed. Do not divide it by DPI again.
  • The Return Value is a Boolean. Use the position only when it is true; a point behind the camera or otherwise not projectable needs a separate off-screen-marker policy.
  • Enable Player Viewport Relative for UI attached to one local player's screen. This makes the coordinate relative to that player's viewport subregion, which matters in split-screen and constrained views.
  • Projection does not test walls, clamp to an edge, reserve a safe zone, or move a widget. Those are later steps owned by your HUD.
  • Moving world markers often need frequent updates, but one controller-owned marker manager should update a reusable pool. Do not make every marker scan the world from its own Tick.

What the node actually does

Project World Location to Widget Position converts one world-space point, such as an actor's head socket or an objective marker component, into the 2D space used by viewport widgets.

Its contract is:

Pin Declared type Meaning
Player Controller PlayerController object reference Chooses the local player's camera and viewport.
World Location Vector The 3D point to project.
Player Viewport Relative Boolean Whether the result uses the owning player's subviewport as its origin.
Screen Position Vector2D The projected position in UMG-ready viewport units.
Return Value Boolean Whether the world point successfully projected onto the screen.

In C++, the static function returns bool and writes the position through an FVector2D& output parameter. Blueprint presents both as output pins. The Player Controller input is an APlayerController*, and World Location is an FVector.

The node is pure: it calculates outputs when the graph evaluates it. It does not have execution pins and does not retain the target, create a marker, or schedule another attempt.

pseudocode of the Blueprint pattern - not engine source

Projected, WidgetPosition =
    ProjectWorldLocationToWidgetPosition(
        OwningPlayerController,
        TargetWorldLocation,
        PlayerViewportRelative = true)

if Projected:
    MarkerCanvasSlot.SetPosition(WidgetPosition)
else:
    ApplyBehindCameraPolicy()

The output name says Screen Position, but its unit is the important part: this node removes the viewport's quality and DPI scaling so the result can line up with UMG. Project World to Screen, by comparison, exposes screen coordinates before this widget-space conversion.

One coordinate space from projection to placement

Most mysterious offsets come from mixing pixel coordinates, desktop coordinates, full-viewport coordinates, and player-local widget coordinates. Pick one path and keep every value in that space.

For a marker inside a player HUD:

  1. Create the HUD for the correct owning PlayerController and add it with Add to Player Screen when supporting local multiplayer.
  2. Call Project World Location to Widget Position with that same controller and Player Viewport Relative = true.
  3. Place the marker in a full-player-screen Canvas Panel, using its Canvas Panel Slot -> Set Position.
  4. Derive clamp bounds from the same player-screen geometry, not from the desktop resolution or another player's viewport.

Get Player Screen Widget Geometry returns the geometry that contains widgets added to that player's screen. Its local size is a reliable companion for a player-relative projection. If your existing layout uses full-viewport UI, use full-viewport geometry consistently instead.

Get Viewport Scale reports the current DPI scale. It is useful for diagnosing a mismatch, but the projection node has already removed that scale. Dividing its result again makes the offset change with resolution. The same warning applies to Set Position in Viewport: its Remove DPI Scale option is for pixel positions that have not already been converted. With this node's UMG-ready result, do not request another inverse-DPI conversion.

The marker's pivot is part of the answer

The returned coordinate identifies the projected world point. It does not know whether that point should touch the marker's center, arrow tip, top-left corner, or health-bar bottom edge.

For a Canvas child, set the slot alignment to the intended pivot:

centered icon:       Alignment = (0.5, 0.5)
nameplate above head: Alignment = (0.5, 1.0)
top-left debug label: Alignment = (0.0, 0.0)

Then add only the designed offset, such as a small upward nameplate gap. Do not subtract half the marker size manually and also use a centered alignment; that applies the pivot twice.

If the marker's size changes with text, cache or measure its desired size after layout when computing exact edge margins. A fixed-size Size Box is often simpler for edge arrows because the clamp cannot jump when the label changes.

On-screen, off-screen, and behind the camera

Successful projection and on-screen placement are separate questions.

  • A true result gives a usable projected coordinate. Compare it with the chosen local bounds to decide whether it is inside the visible marker area.
  • A false result means the coordinate is not a usable placement result. Do not clamp that output and assume it describes a behind-camera direction.
  • A valid coordinate can still lie outside your safe rectangle, especially near an edge or when the marker itself has width and height.

A robust marker manager calculates a direction independently when it needs an edge arrow:

pseudocode - one policy, not engine source

ToTarget = TargetWorldLocation - CameraWorldLocation
InFront = Dot(ToTarget, CameraForwardVector) > 0

Projected, Position = ProjectWorldLocationToWidgetPosition(...)

if Projected and Position is inside SafeRect:
    show normal marker at Position
else:
    Direction = derive a 2D direction from ToTarget in camera right/up space
    if not InFront:
        choose the opposite screen-edge direction for your game's convention
    EdgePosition = intersect Direction from SafeRect center with SafeRect edge
    show edge arrow at EdgePosition, rotated to Direction

The explicit camera-space direction matters because projection failure does not promise useful output coordinates. Games also disagree about how a target directly behind the camera should be represented: some send the arrow toward the shortest turn, some prefer the nearest horizontal edge, and some hide it. That is presentation policy, not part of the projection node.

Avoid clamping X and Y independently when an arrow should follow a ray from the screen center; independent clamping tends to make it slide unnaturally around corners. Intersect the direction with the inset rectangle instead.

Clamp the whole marker inside a safe rectangle

The physical viewport edge is rarely the correct clamp. Build a local safe rectangle from:

  • the owning player's local viewport bounds;
  • platform safe-zone padding;
  • extra art-direction padding;
  • half the marker's rendered size, adjusted for its pivot.

For a centered marker of size (Width, Height), a simple inset starts at half that size on each edge. An arrow whose pivot is at its tip needs asymmetric margins instead. Clamp or intersect against the inset rectangle so the whole graphic remains visible.

A UMG Safe Zone widget automatically pads its child layout for the current platform's unsafe area. For freely positioned Canvas markers, either put the marker layer inside that Safe Zone and work in its local geometry, or use Get Safe Zone Padding and convert the padding into the same coordinate space. Do not combine a Safe Zone parent and the same padding manually.

Test resized PIE windows, the UMG device previews, aspect-ratio-constrained views, and split-screen. A marker that works only at the designer's reference resolution usually has a coordinate-space or pivot error, not a projection error.

Occlusion is a separate world query

Projection answers where the point lands through the camera. It does not tell you whether a wall is between the camera and that point.

If the design hides occluded nameplates, run an intentional visibility trace from the relevant camera/view point to the marker anchor. Choose a trace channel whose responses mean "blocks this UI marker," ignore the owning pawn where appropriate, and compare the blocking hit with the intended target.

Decide independently what each state displays:

Projection In safe bounds Occluded Typical policy
success yes no normal marker
success yes yes hide, fade, or use an occluded style
success no either edge arrow if allowed
failure / behind camera n/a either edge policy or hidden

Do not accidentally trace once per marker from both the HUD and each marker widget. The controller-owned manager should own projection, visibility tests, and final presentation state.

Split-screen and multiplayer

Projection is local presentation. Each local player needs that player's controller, camera, subviewport, widget instance, and clamp bounds.

for each local player's HUD presenter:
    project tracked replicated world facts through OwningPlayerController
    update only that player's marker widgets

In a network game, replicate the gameplay facts that make a marker possible: the objective actor, team, alive state, or target location. Do not replicate a screen coordinate. Two clients can see the same actor through different cameras, resolutions, DPI scales, and safe zones, so each client projects it locally.

Player Viewport Relative = false can be correct for a deliberately shared full-screen layer. It is not a substitute for choosing the owning player; the controller still selects the view used for projection.

Update cost and pooled markers

Camera movement can change every marker's screen position even when its world target stands still, so moving world markers often do need a frame update. The scalable pattern is centralized:

HUD marker manager update
-> iterate currently tracked targets
-> reject invalid, hidden, or out-of-range targets early
-> project each remaining anchor once
-> run occlusion at the rate the design needs
-> assign position/style to a reused marker widget

Use a pool: create enough marker widgets for the active set, reset and reuse them when targets leave, and cap the number displayed. A damage-number system can recycle widgets when an animation ends; an objective manager can keep one widget per active objective.

Projection itself is only part of the cost. World scans, one trace per target per frame, repeated widget construction, layout churn, and per-widget property bindings are common larger costs. Static objectives can update more slowly; fast combat markers may update every frame while spacing expensive occlusion traces over time.

When it fails (and what failure does)

  1. The Player Controller is None or not the intended local player. The call cannot produce the desired player's projection. Guard optional references and use the widget's owning player where possible.
  2. The world point does not project onto the screen. The Boolean is false; treat the Vector2D as unusable and run the chosen behind-camera or hidden policy.
  3. The coordinate is valid but the widget is offset. This is usually a mixed DPI/coordinate-space path, a Canvas anchor/alignment mismatch, or a pivot applied twice.
  4. The marker escapes one split-screen pane. The graph used full-viewport bounds or disabled Player Viewport Relative while placing player-local UI.
  5. The icon is clipped at an edge. The graph clamped its pivot point, not the marker's complete bounds, or ignored safe-zone padding.
  6. The marker shows through walls. Projection succeeded correctly; no occlusion query or policy was added.

There is no exception, retry, or latent completion. The pure node returns its current outputs, and your graph decides what happens next.

What it does not do

  • It does not create, add, move, rotate, show, or hide a widget.
  • It does not prove that the coordinate lies within your safe rectangle.
  • It does not return the marker's size, pivot, edge direction, or arrow angle.
  • It does not test line of sight, collision, distance, team, or relevance.
  • It does not replicate world state or presentation state.
  • It does not convert a viewport coordinate back into a world ray.
  • It does not turn a screen-space marker into a world-space Widget Component.

Lookalikes - which one do I want?

Node / tool Use when Watch out
Project World Location to Widget Position You are placing UMG from a world point and want widget-ready coordinates. Branch on success; clamping and occlusion are separate.
Project World to Screen You specifically need screen coordinates, such as the pixel-side of another screen-space API. It does not perform this node's widget-space scale conversion.
Deproject Screen to World You want a world-space ray from a screen coordinate for aiming or picking. It is the inverse kind of question, not marker placement.
Set Position in Viewport You need to position a root UserWidget already attached to the viewport. Match its Remove DPI Scale option to the units you supply.
Canvas Panel Slot -> Set Position You are positioning a Canvas child inside an existing HUD layout. The slot's anchors and alignment define how the coordinate is interpreted.
Widget Component The UI should exist as a component in the 3D world. World/screen Widget Component modes have different occlusion and size behavior from viewport UMG.
Line Trace by Channel Marker visibility depends on world occlusion. A trace answers collision, not projection or safe-area layout.

Rule of thumb: project with the widget's owning controller, keep placement and bounds in that player's local UMG space, and make off-screen and occlusion behavior explicit.

Going deeper