Quick Reference
Quick reference and cheat sheet for ElysAwareness.
Common Patterns
Basic Targeting Setup
1. Add ERPAwarenessComponent to PlayerController
2. Configure Target Pipeline:
- Channel Id: "Target"
- Sampler: ERPSphereOverlapSampler (range: 2000)
- Filter: ERPTargetableFilter
- Scorer: ERPDistanceScorer (normalize: true), Weight: 1.0
- ResolverPolicy: LowestScore (default)
3. Add ERPTargetingComponent (Channel: "Target")
4. Bind to OnDomainCandidateChanged / OnDescriptorChanged
5. Implement IERPTargetable on targets
Basic Interaction Setup
1. Configure Interaction Pipeline (range: 300)
- Channel Id: "Interact"
- Sampler, Filter (InteractableFilter), Scorer
2. Add ERPInteractionComponent (Channel: "Interact")
3. Implement IERPInteractable on interactables
4. Call StartInteractionAttempt() on input
5. (Optional) Add ERPBaseInteractionFeedbackComponent on interactables
→ auto-notified, implement OnInteractionFocused / OnDescriptorUpdated
6. OR: Bind to OnDescriptorChanged / OnDomainCandidateChanged for custom UI
Basic Instanced Interaction Setup
1. Create DataTable (row type: FERPInstancedMeshConfig)
2. Add rows: Mesh → ProxyClass → StateSteps
- Mesh: the static mesh used in ISMCs (e.g., SM_Pine_1)
- ProxyClass: Blueprint child of ERPInstanceProxy
- StateSteps: ordered lifecycle (Normal → Harvested → Regrowing)
3. Add ERPInstancedInteractionComponent to PlayerController/Pawn
- Set MeshConfigTable to your DataTable
- SwapRadius: 600 (must be > awareness SamplingRange)
- DespawnMargin: 200 (hysteresis to prevent flickering)
- ScanInterval: 0.2
4. Create proxy Blueprint (parent: ERPInstanceProxy)
- Override OnInstanceInteracted for loot/VFX
- Override OnStateChanged for transition effects (call Parent)
5. Configure StateSteps per row:
- State: GameplayTag (ERP.Instance.Normal, .Harvested, etc.)
- Mesh: what to show on the proxy actor
- DistantMesh: what to show in ISMC when player leaves
- Descriptor: interaction actions (empty = non-interactable)
- Duration: 0 = manual, >0 = auto-transition (seconds)
- bLoopToFirst: true on last step to cycle back to step 0
Registry-Based Perception (Map Markers, POIs)
1. Configure Pipeline:
- Channel Id: "MapMarkers"
- Sampler: ERPRegistrySampler
- Scorer: ERPDistanceScorer (weight: 1.0)
- ResolverPolicy: LowestScore (default)
2. On target actors: Add ERPRegistrationComponent
- Set ChannelIds: ["MapMarkers"]
3. Bind to OnChannelEvaluated for ALL candidates (minimap)
or OnChannelCandidateAcquired for best only
Custom Domain Setup
1. Create Blueprint/C++ class extending ERPDomainComponent
2. Override IsCandidateEligible (validation)
3. Override HandleCandidateUpdated (reactions)
4. Configure a perception pipeline with matching ChannelId
5. Add your domain component to the same actor
Component Quick Reference
ERPAwarenessComponent
// Get candidate for channel
AActor* Target = Perception->GetChannelCandidate("Target");
// Check if has candidate
if (Perception->HasChannelCandidate("Interact")) { ... }
// Bind to generic events
Perception->OnChannelCandidateAcquired.AddDynamic(this, &AMyPC::OnAcquired);
// Bind to pipeline evaluation (ALL scored candidates — for minimap/radar)
Perception->OnChannelEvaluated.AddDynamic(this, &AMyPC::OnEvaluated);
// void OnEvaluated(FName ChannelId, const TArray<FERPPipelineScoredCandidate>& Candidates)
// Validate actor for anti-cheat
bool bValid = Perception->ValidateActorForChannel(Actor, "Interact");
ERPDomainComponent (and subclasses)
// Get current candidate
AActor* Candidate = DomainComp->GetCurrentCandidate();
// Check if has candidate
if (DomainComp->HasCandidate()) { ... }
// Bind to domain events
DomainComp->OnDomainCandidateChanged.AddDynamic(this, &AMyPC::OnChanged);
ERPInteractionComponent
// Configure
InteractionComp->ChannelId = "Interact";
// Get descriptor
FERPInteractionDescriptor Desc;
InteractionComp->GetCurrentDescriptor(Desc);
// Execute interaction (handles networking)
InteractionComp->StartInteractionAttempt();
// Force descriptor refresh (same candidate changed its data)
InteractionComp->ForceRefreshDescriptor();
// Bind to events
InteractionComp->OnDescriptorChanged.AddDynamic(this, &AMyPC::OnDescriptor);
InteractionComp->OnInteractionExecuted.AddDynamic(this, &AMyPC::OnExecuted);
ERPTargetingComponent
// Configure
TargetingComp->ChannelId = "Target";
// Get descriptor
const FERPTargetDescriptor& Desc = TargetingComp->GetCurrentDescriptor();
// Bind to events
TargetingComp->OnDomainCandidateChanged.AddDynamic(this, &AMyPC::OnTargetChanged);
TargetingComp->OnDescriptorChanged.AddDynamic(this, &AMyPC::OnDescriptor);
ERPChannelListenerComponent
// Lightweight event-only listener
Listener->ChannelId = "Target";
Listener->OnCandidateAcquired.AddDynamic(this, &AMyPC::OnAcquired);
Listener->OnCandidateLost.AddDynamic(this, &AMyPC::OnLost);
AActor* Current = Listener->GetCurrentCandidate();
Interface Implementation
IERPTargetable (C++)
class AMyEnemy : public ACharacter, public IERPTargetable
{
virtual bool CanBeTargetedBy_Implementation(AActor* TargetingActor) const override
{
return bIsAlive;
}
};
IERPInteractable (C++)
class AMyDoor : public AActor, public IERPInteractable
{
virtual bool CanBeInteractedWith_Implementation(AActor* InteractingActor) const override
{
return true;
}
virtual void GetInteractionDescriptor_Implementation(FERPInteractionDescriptor& OutDescriptor) const override
{
OutDescriptor.ObjectName = FText::FromString(TEXT("Door"));
FERPInteractionAction Action;
Action.ActionName = FText::FromString(bIsOpen ? TEXT("Close") : TEXT("Open"));
OutDescriptor.Actions.Add(Action);
}
virtual bool ExecuteInteraction_Implementation(AActor* InstigatorActor, int32 ActionIndex) override
{
bIsOpen = !bIsOpen;
return true;
}
};
Pipeline Configurations
Distance-Based Targeting
Sampler: ERPSphereOverlapSampler (range: 3000)
Filter: ERPTargetableFilter
Scorer: ERPDistanceScorer (weight: 1.0)
ResolverPolicy: LowestScore
StickyBias: 0.1
Camera-Aware Targeting
Sampler: ERPSphereOverlapSampler
Filter: ERPTargetableFilter
Scorers:
- ERPDistanceScorer (weight: 1.0)
- ERPConeScorer (cone: 45, weight: 1.5)
ResolverPolicy: LowestScore
StickyBias: 0.1
Screen-Space Targeting
Sampler: ERPSphereOverlapSampler
Filter: ERPTargetableFilter
Scorer: ERPViewportEllipseScorer (weight: 1.0)
-> Enable bProvideViewportInfoWhenAvailable
ResolverPolicy: LowestScore
StickyBias: 0.1
Debugging
Enable Debug Logging
Set bDebugPerception = true on ERPAwarenessComponent.
Console Variables
elys.perception.SamplerCacheDebug 1 // Log sampler cache hits/misses
Performance
stat GAME // Show component tick times
Performance Tips
Tick Interval
// On ERPAwarenessComponent
PerceptionTickInterval = 0.1f; // 10 Hz instead of every frame
Reduce Range
DefaultSamplingRange: 1500 (instead of 3000)
Scorer Weights
Use weights to express importance:
Distance (weight: 2.0) > Angle (weight: 1.0)
Instead of normalizing scores differently
Score Convention
Lower score = better (by convention)
| Scorer | Best (0.0) | Worst (1.0) |
|---|---|---|
| Distance | Very close | At max range |
| Cone | Center of view | Edge of cone |
| Viewport Ellipse | Screen center | Ellipse boundary |
All scores are multiplied by their weight before aggregation.
Aggregation is always weighted average (sum / count) -- built-in, no configuration needed.
Resolution uses ResolverPolicy (default: LowestScore) + StickyBias (default: 0.1) on the pipeline struct.
Class Hierarchy
UActorComponent
+-- UERPAwarenessComponent (perception engine)
+-- UERPChannelListenerComponent (lightweight listener)
+-- UERPDomainComponent (abstract base)
| +-- UERPInteractionComponent (interaction domain)
| +-- UERPTargetingComponent (targeting domain)
| +-- YourCustomDomain (extend for custom gameplay)
+-- UERPInstancedInteractionComponent (ISMC instance swapping)
+-- UERPRegistrationComponent (registry auto-registration)
+-- UERPBaseInteractionFeedbackComponent (visual feedback, optional, auto-wired)
AActor
+-- AERPInstanceProxy (ISMC instance proxy with state machine + interaction)
UWorldSubsystem
+-- UERPInstanceSwapSubsystem (instance state persistence)
UUserWidget
+-- UERPInteractionWidgetBase (interaction prompt)
| +-- UERPBaseWorldInteractionWidget (world-space prompt)
+-- UERPChallengeWidgetBase (challenge progress)
| +-- UERPBaseMultiPressChallengeWidget (press counter)
| +-- UERPBaseRadialHoldWidget (radial fill)
+-- UERPTargetingWidgetBase (reticle / target info)
+-- UERPBaseTargetFrameWidget (nameplate)
| +-- UERPBaseTargetHealthBarWidget (nameplate + health)
+-- UERPBaseTargetReticleWidget (lock-on reticle)
Widget Quick Reference
// Interaction prompt
InteractionWidget->UpdateFromDescriptor(Descriptor);
InteractionWidget->SetFocused(true);
FText Key = InteractionWidget->GetPrimaryActionDisplayKey();
// Challenge progress
ChallengeWidget->SetChallengeProgress(0.75f);
ChallengeWidget->NotifyChallengeComplete(true);
ChallengeWidget->ResetChallenge();
// Targeting
TargetWidget->UpdateFromDescriptor(TargetDescriptor);
TargetWidget->SetTargetActive(true);
TargetWidget->SetTargetActor(TargetActor);
// Health bar (extends target frame)
HealthBarWidget->SetHealthPercent(0.75f);
Next Steps
Full guides:
- Setup Guide - Complete installation walkthrough
- Pipeline System - Architecture deep dive
- Interaction Guide - Interaction implementation
- Instanced Interaction Guide - ISMC instance swapping and state management
- Targeting Guide - Targeting implementation
- Custom Domains - Create your own domains
- Customization - Custom pipeline components
- API Reference - Complete API documentation