Interactor System¶
What You'll Learn¶
- The two-tier focus model: how one detected set becomes one focused actor and one action list
- The interactor state machine and what drives each transition
- How action cycling and interaction start/stop are networked
- Which component type to pick for your project
Core Concepts¶
What Is an Interactor?¶
The interactor lives on whatever should be able to interact - almost always your player Pawn, but equally valid on an NPC or AI-controlled actor. It detects nearby interactables, picks one actor to focus on, and lets you cycle through and trigger the actions available on that actor.
Real-world analogy
Think of it as the "hands and eyes" of an actor: it notices what's nearby (detection), decides what you're looking at (Tier 1 focus), and lets you choose which of several available actions on that thing to perform (Tier 2 cycling) - all without ever deciding when to act. That decision stays entirely with your project's input code.
Two-Tier Focus Model¶
// Tier 1: one focused actor, selected by weight + distance
AActor* Focused = Interactor->Execute_GetFocusedActor(Interactor.GetObject());
// Tier 2: every interactable component on that actor, weight-sorted
TArray<FInteractionActionCandidate> Actions = Interactor->Execute_GetActionList(Interactor.GetObject());
int32 Highlighted = Interactor->Execute_GetHighlightedActionIndex(Interactor.GetObject());
Tier 1 - actor focus. The interactor evaluates every actor represented in its detected set and picks exactly one as focused: the actor whose highest-weight interactable component wins, ties broken by squared distance. Only the focused actor gets an attention widget; everything else stays silently tracked.
Tier 2 - action cycling. Within the focused actor, every IMounteaInteractableInterface component becomes one entry in the action list (a door might have one interactable; a workbench might expose Craft, Repair, and Inspect as three separate components). CycleAction(Direction) moves the highlighted index; StartInteraction locks onto whichever action is currently highlighted.
Zero input bound by the plugin
StartInteraction, StopInteraction, and CycleAction are all plain listen-only BlueprintNativeEvent interface methods. The interaction system never binds its own input and never calls these itself - your project's own input-handling code calls Execute_CycleAction(Interactor, Direction) the same way it calls Execute_StartInteraction, exactly like calling any other Blueprint function.
Component Architecture¶
UMounteaInteractorComponentBase is abstract; three concrete subclasses supply the actual detection method:
| Component | Detection method | Best for |
|---|---|---|
| Interactor Component Overlap | Physics overlap events on a sibling collision shape | First/third-person games - "walk into range" |
| Interactor Component Trace | Periodic line/box trace from the owner | Camera-aim / crosshair interaction |
| Interactor Component Mouse | Trace under the mouse cursor | Top-down / strategy / point-and-click |
Each subclass only ever calls AddDetected() / RemoveDetected() on the base - the base owns all Tier 1/2 selection logic in RefreshFocus(), so switching detection method later never touches your focus/cycling code.
State Machine¶
UENUM(BlueprintType)
enum class EInteractorStateV3 : uint8
{
EIS_Scanning, // actively scanning for interactables (default)
EIS_Active, // performing an interaction
EIS_Asleep, // useful for cutscenes - no detection
EIS_Suppressed, // cannot interact (e.g. a secondary interactor while a master is active)
EIS_Disabled,
};
State transitions are server-authoritative (SetState_Server). On both the authority path and the remote-client replication path (OnRep_InteractorState), the base calls ProcessStateChanged() - a virtual hook each concrete subclass overrides to bind/unbind its own detection (Overlap binds/unbinds overlap events; Trace and Mouse start/stop their periodic timers) before calling Super::ProcessStateChanged() to broadcast the state-changed delegates.
Networking Model¶
| Property | Replication | Why |
|---|---|---|
HighlightedActionIndex |
COND_OwnerOnly, client-predicted |
No other client needs a remote player's own cursor position in their own action list |
bSelectionLocked, LockedAction |
COND_OwnerOnly |
Other clients query occupancy via the interactable's own ActiveInteractorSlots, not by introspecting a remote interactor |
CollisionChannel, DefaultInteractorState, InteractorTag, SafetyTraceSetup, ListOfIgnoredActors |
Replicated | Server-authoritative config values |
Action cycling is client-predicted, server-confirmed: CycleAction_Implementation updates the calling side's own index and broadcasts immediately for instant feedback, then forwards to CycleAction_Server (Unreliable, WithValidation) which recomputes the same clamped index from Direction server-side - it never trusts a client-sent absolute index. StartInteraction/StopInteraction route through Reliable, WithValidation Server RPCs.
Key Properties¶
| Property | Type | Notes |
|---|---|---|
CollisionChannel |
TEnumAsByte<ECollisionChannel> |
Response channel used for detection |
DefaultInteractorState |
EInteractorStateV3 |
Boot state, from SetDefaults() |
InteractorTag |
FGameplayTag |
Checked against interactables' Required/Excluded tag filters |
SafetyTraceSetup |
FSafetyTracingSetup |
Line-of-sight validation - see Interactor Runtime Config |
MaxCandidatesPerActor |
int32 |
Caps ActionList size for the focused actor |
DetectedSet |
TArray<TScriptInterface<IMounteaInteractableInterface>> |
Every currently-detected interactable, across all actors |
Delegates¶
| Delegate | Fires when |
|---|---|
OnStateChanged |
InteractorState transitions |
OnFocusedActorChanged |
Tier 1 focus changes |
OnActionListChanged |
Tier 2 list is rebuilt |
OnHighlightedActionChanged |
Cycling moves the highlighted index |
OnInteractionKeyPressed / OnInteractionKeyReleased |
StartInteraction / StopInteraction called |
OnIgnoredActorAdded / OnIgnoredActorRemoved |
Ignore list changes |
OnCollisionChanged, OnInteractorTagChanged |
Property setters called |
Setup¶
// Blueprint-callable, via interface - never call *_Implementation directly on a raw pointer
Interactor->Execute_SetDefaults(Interactor.GetObject()); // pulls from Interactor Runtime Config
Interactor->Execute_ActivateInteractor(Interactor.GetObject(), ErrorMessage);
SetDefaults_Implementation() resolves UMounteaInteractorRuntimeConfig and copies its values into the component's own editable properties - called automatically from BeginPlay() when the owner has authority, and available in-editor via the Set Default Values button for iterating on config changes without a play session.
See Configuration → Interactor Runtime Config, K2Nodes & Statics for the full Blueprint-facing API, and User Interface for how focus changes drive the attention widget pool.