Skip to main content

Targeting Guide

Complete guide to implementing combat targeting using ElysAwareness.

Overview​

The targeting system builds on top of Perception via the UERPDomainComponent base class. UERPTargetingComponent provides eligibility validation using IERPTargetable interface and targeting rules, along with descriptor tracking.

Architecture:

  • Perception: Evaluates candidates via configurable pipeline
  • Domain (Targeting): UERPTargetingComponent extends UERPDomainComponent, validates eligibility
  • Presentation: optional supplied feedback components and Widget Blueprints, or your own implementation

Basic Setup​

1. Configure Targeting Pipeline​

On your PlayerController or Pawn, configure ERPAwarenessComponent:

Target Pipeline Settings:

  • Channel Id: Target
  • Default Sampling Range: 3000.0
  • Sampler: ERPSphereOverlapSampler
    • Object Channel: WorldDynamic (or your target collision object type)
    • Ignore Context Actor: check
  • Filters: ERPTargetableFilter, then ERPLineOfSightFilter (Visibility) for normal lock-on behavior
  • Scorers:
    • ERPDistanceScorer (normalize by range), Weight: 1.0
    • ERPConeScorer (optional, for camera-aware targeting), Weight: 0.5
  • ResolverPolicy: LowestScore (default)

2. Add Targeting Component​

Add ERPTargetingComponent to your PlayerController or Pawn:

  • Channel Id: Target (must match pipeline)
  • Rules: (optional instanced targeting rules)

3. Implement Targetable Interface​

On target actors (enemies, NPCs, destructibles):

Blueprint:

  1. Class Settings -> Interfaces -> Add ERPTargetable
  2. Implement Can Be Targeted By -> return true (or custom logic)
  3. Optionally implement Get Target Descriptor -> return a UERPTargetDescriptor data asset

C++:

class AYourEnemy : public ACharacter, public IERPTargetable
{
GENERATED_BODY()

public:
virtual bool CanBeTargetedBy_Implementation(AActor* TargetingActor) const override
{
return bIsAlive;
}

virtual UERPTargetDescriptor* GetTargetDescriptor_Implementation() const override
{
return TargetDescriptor; // Optional DataAsset
}

protected:
UPROPERTY(EditAnywhere) bool bIsAlive = true;
UPROPERTY(EditAnywhere) UERPTargetDescriptor* TargetDescriptor = nullptr;
};

4. Bind Events​

// In BeginPlay
TargetingComponent->OnDomainCandidateChanged.AddDynamic(this, &AMyPC::OnTargetChanged);
TargetingComponent->OnDescriptorChanged.AddDynamic(this, &AMyPC::OnTargetDescriptorChanged);

Blueprint Example​

Complete Targeting System​

Components on PlayerController:

  • ERPAwarenessComponent (target pipeline configured)
  • ERPTargetingComponent (channel: "Target")

Event Graph:

// Handle target change
Event: OnDomainCandidateChanged (Previous, New, ChannelId)
-> Set CurrentTarget = New
-> Branch: IsValid(New)
True:
-> Show Target Indicator Widget
-> Set Widget position to target actor
False:
-> Hide Target Indicator Widget

// Attack current target
Input Action: Attack
-> TargetingComp -> GetCurrentCandidate
-> IsValid?
True: -> Execute attack on target
False: -> Execute untargeted attack

Targeting Rules​

Add instanced UERPTargetRule objects for composable validation:

UCLASS(Blueprintable, EditInlineNew)
class UMyTeamRule : public UERPTargetRule
{
GENERATED_BODY()
public:
virtual bool CanTarget_Implementation(AActor* Instigator, AActor* Target, FName ChannelId) const override
{
// Only target enemies (different team)
ITeamAgentInterface* InstigatorTeam = Cast<ITeamAgentInterface>(Instigator);
ITeamAgentInterface* TargetTeam = Cast<ITeamAgentInterface>(Target);
if (InstigatorTeam && TargetTeam)
{
return InstigatorTeam->GetGenericTeamId() != TargetTeam->GetGenericTeamId();
}
return true;
}
};

Pipeline Configurations​

Distance-Only Targeting​

Simple nearest-enemy targeting:

Sampler: ERPSphereOverlapSampler (range: 2000)
Filter: ERPTargetableFilter
Scorer: ERPDistanceScorer (weight: 1.0)
ResolverPolicy: LowestScore
StickyBias: 0.1

Camera-Aware Targeting​

Prefers enemies near crosshair:

Sampler: ERPSphereOverlapSampler (range: 3000)
Filter: ERPTargetableFilter
Scorers:
- ERPDistanceScorer (weight: 1.0)
- ERPConeScorer (cone: 45 degrees, weight: 1.5)
ResolverPolicy: LowestScore
StickyBias: 0.1

Screen-Space Targeting​

Lock-on to enemy nearest screen center:

Sampler: ERPSphereOverlapSampler
Filter: ERPTargetableFilter
Scorer: ERPViewportEllipseScorer (weight: 1.0)
- Enable bProvideViewportInfoWhenAvailable on PerceptionComponent
ResolverPolicy: LowestScore
StickyBias: 0.1

UERPTargetDescriptor​

Target descriptors are UDataAsset-based objects that describe targeting properties:

UCLASS(BlueprintType)
class UERPTargetDescriptor : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadOnly) FText DisplayName;
UPROPERTY(EditAnywhere, BlueprintReadOnly) TSoftObjectPtr<UTexture2D> Icon;
UPROPERTY(EditAnywhere, BlueprintReadOnly) FGameplayTagContainer TargetTags;
UPROPERTY(EditAnywhere, BlueprintReadOnly) TMap<FName, TObjectPtr<UObject>> PresentationHints;
};

Use descriptors to drive UI without coupling to specific actor types.


Widget Integration​

The plugin provides UERPTargetingWidgetBase for targeting UI (reticles, lock-on indicators, info panels).

PatternDescriptionKey Hooks
Target ReticleLock-on marker positioned on targetOnTargetActiveChanged, tick for positioning
Target Info PanelNameplate with name, icon, healthOnDescriptorUpdated, OnTargetActorChanged

Quick wiring (presenter on your PlayerController):

Event: OnDomainCandidateChanged (Previous, New, ChannelId)
-> TargetWidget -> SetTargetActor(New)
-> TargetWidget -> SetTargetActive(IsValid(New))

Event: OnDescriptorChanged (Candidate, Descriptor, ChannelId)
-> TargetWidget -> UpdateFromDescriptor(Descriptor)

For complete step-by-step walkthroughs including screen-space positioning, descriptor-driven styling, and health bar integration, see the Widget Guide.

Hard lock and cycling​

ToggleLockOn() acts on the awareness pipeline's current screen-focused target: it locks that actor, transfers an existing lock when another actor is focused, and unlocks only when the same actor is already locked. LockOnClosestTarget() is a separate world-space command that ignores screen priority and locks the eligible candidate with the shortest distance. For a component placed on a PlayerController, lock range and nearest-target distance are measured from its possessed pawn, so the calculation remains correct after possession changes and when the controller actor does not follow the pawn. Pawn-owned and Actor-owned components use their owner position. CycleTarget(true) and CycleTarget(false) move through eligible candidates and hard-lock the result; when starting from automatic mode, cycling begins relative to the current soft focus. GetLockedTarget() is BlueprintPure and is the safest source for a context-sensitive action such as attacking or disrupting the selected actor.

The component commits the candidate before broadcasting OnTargetLocked, so the descriptor and actor received by UI listeners always belong to the same target. Unlocking also forwards SetTargetLocked(false) to the active feedback widget; applying a hard lock forwards true.

The supplied demo map evaluates its command pipelines when the input is pressed:

  • T: lock the eligible visible target nearest the raised aiming reference, or unlock when that same target is already locked;
  • C: hard-lock the target nearest the possessed pawn among eligible targets across the entire screen;
  • Tab: cycle through eligible targets across the entire screen and hard-lock the new selection;
  • Left Mouse Button: disrupt a locked sentinel in the dungeon example;
  • F: interact with the Interaction domain's candidate, including hold/release/retry while another actor remains locked.

Command pipelines without another Tick​

In Demo_ERP_Awareness_PC, ERPCommandQueries has Start with Tick Enabled disabled. Each command calls Query Channel once, using its own channel (TargetAim, TargetClosest, or TargetCycle). Their Sticky Bias is zero: a deliberate button press uses the current view, with no passive focus hysteresis. The result is passed to Set Manual Target on ERPTargeting.

BP_ERP_DemoOnScreenFilter supplies the sample's full-viewport eligibility rule. BP_ERP_DemoPawnRangeFilter applies the same 1500 cm pawn-based limit as the lock component, including with a third-person camera. BP_ERP_DemoPawnDistanceScorer supplies C's pawn-distance policy; T uses only the screen-position scorer. The passive focus pipeline can keep its own narrower ellipse and hysteresis independently.

In the demo, Screen Center Ndc is (0.5, 0.4) for passive focus, T and the first cycling choice: 10% of screen height above the midpoint, away from the third-person character. Only passive focus rejects candidates outside its ellipse; command eligibility still covers the whole viewport. Adjust this reference in the demo controller's scorers to match your camera framing.

Query Channel runs an existing configured pipeline synchronously, returns its best candidate and current scored candidates, and clears its outputs when nothing matches. It does not enable ticking, publish passive candidate events, or replace the component's passive channel state. The candidates retain sampler order; the demo owns its cycling policy.

Equivalent sphere samplers use the shared Sampler Cache Id DemoTargetSphere. Raw sampling is reused only within the same engine frame and context actor; filters, scores, and resolution still run for each query. Use the same cache ID only for equivalent sampler settings and sampling origins. An input on a later frame samples again. A command does not require an additional recurring evaluation just to populate this cache.

If the locked actor is destroyed, the component clears hard-lock state and resumes accepting automatic awareness updates on the next evaluation.


Targeting and interaction input are separate design choices​

A hard lock belongs to the Targeting domain; it does not determine which object the Interaction domain can use. A player can keep one actor locked while interacting with another. Use separate input actions to demonstrate this independence, or define a shared-button policy in your own PlayerController/Pawn Blueprint. The plugin does not prescribe an interaction-versus-targeting priority.

See Keep targeting and interaction independent for input routing and a hold/cancel/retry verification scenario.

Compared to ChannelListenerComponent​

FeatureERPTargetingComponentERPChannelListenerComponent
Eligibility validationIERPTargetable + RulesNone
Descriptor trackingYesNo
Base classUERPDomainComponentUActorComponent
Use caseFull targeting domainLightweight event mirror

Use ERPChannelListenerComponent when you only need raw perception events without eligibility checks.


Next Steps​

Build your targeting UI with Widget Guide.

Set up interactions with Interaction Guide.

Create custom domains with Custom Domains.

Check complete API in API Reference.