Skip to content

Interaction Trace And Prompt

The interaction system answers one question every frame:

What usable thing is the player looking at right now?

The answer is stored as FocusedInteractableObject on AAndromedaCharacter.

Tick And Line Trace

AAndromedaCharacter enables ticking:

PrimaryActorTick.bCanEverTick = true;

Every tick it calls:

UpdateFocusedInteractable();

That method traces forward from the first-person camera:

camera location
-> camera forward vector * InteractionTraceDistance
-> line trace by channel

The current trace channel is ECC_Visibility. In this project, that means "things the player can aim at for interaction should block visibility." This is a practical early-slice choice. Later, we may create a project-specific interaction trace channel if visibility becomes overloaded.

Ignoring Held Items

When the player is carrying a salvage item, that item is attached in front of the camera. If the trace did not ignore it, the player would constantly be aiming at the carried object instead of the scanner, tagging bench, or warehouse zone.

So the trace adds the held item as an ignored actor:

if (CarryComponent && CarryComponent->GetHeldItem())
{
    QueryParams.AddIgnoredActor(CarryComponent->GetHeldItem());
}

This is a small but important detail. It lets the player carry an item and still aim at stations.

Resolving The Interactable

The line trace returns a FHitResult. The hit can represent a component or an actor. The code checks in this order:

1. Hit component implements the interactable interface?
2. Hit actor implements the interactable interface?
3. Any actor component implements the interactable interface?

That gives flexibility:

Salvage item actor       implements interface directly.
Scanner/station actor    implements interface directly.
Interactable component   can provide reusable interface behavior.

The result is stored as a UObject*, not specifically an AActor*, because an interactable target might be an actor or a component.

CanInteract

Finding an object is not enough. The character asks:

IAndromedaInteractableInterface::Execute_CanInteract(NewFocusedInteractable, this)

If CanInteract returns false, the focus is cleared.

This is why invalid targets do not show prompts. For example:

unscanned item at tagging bench -> bench cannot accept it
unpackaged item at warehouse zone -> zone cannot accept it
processed item -> item cannot be picked up
scanner with no widget class -> scanner cannot be used

Prompt Widget Lifecycle

The character creates WBP_InteractPrompt once at BeginPlay:

BeginPlay
-> CreateInteractPromptWidget
-> CreateWidget
-> AddToViewport
-> ClearPrompt

When focus changes, NotifyFocusedInteractableChanged either sets the prompt text or clears it.

The prompt widget has C++ behavior and Blueprint layout. The widget Blueprint provides named Designer widgets; C++ updates those widgets.

Prompt Suppression

When a UI panel is open, such as the scanner result widget or shift result widget, the character suppresses interaction prompts:

SetInteractionPromptSuppressed(true);

That prevents world prompts from appearing behind UI. When the UI closes, it restores game input and unsuppresses prompts.

InteractInput

InteractInput is called when IA_Interact fires. It now handles both normal interaction and contextual drop.

The flow is:

E pressed
-> if no focused interactable:
       drop held item if carrying one
-> else if focused object no longer implements interface:
       clear focus
       drop held item if carrying one
-> else if focused object CanInteract:
       Execute_Interact
-> else:
       drop held item if carrying one

This makes E behave like a workbench verb:

Pick up item.
Place item.
Run scanner.
Package item.
Store item.
Drop item when not aiming at a valid target.