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):
UERPTargetingComponentextendsUERPDomainComponent, 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, thenERPLineOfSightFilter(Visibility) for normal lock-on behavior - Scorers:
ERPDistanceScorer(normalize by range), Weight: 1.0ERPConeScorer(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:
- Class Settings -> Interfaces -> Add
ERPTargetable - Implement
Can Be Targeted By-> returntrue(or custom logic) - Optionally implement
Get Target Descriptor-> return aUERPTargetDescriptordata 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).
| Pattern | Description | Key Hooks |
|---|---|---|
| Target Reticle | Lock-on marker positioned on target | OnTargetActiveChanged, tick for positioning |
| Target Info Panel | Nameplate with name, icon, health | OnDescriptorUpdated, 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
| Feature | ERPTargetingComponent | ERPChannelListenerComponent |
|---|---|---|
| Eligibility validation | IERPTargetable + Rules | None |
| Descriptor tracking | Yes | No |
| Base class | UERPDomainComponent | UActorComponent |
| Use case | Full targeting domain | Lightweight 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.