Quick Reference
Quick reference and cheat sheet for ElysPerceptionPlugin.
Common Patterns
Basic Targeting Setup
1. Add ERPPerceptionComponent to PlayerController
2. Configure Target Pipeline:
- Channel Id: "Target"
- Sampler: ERPSphereOverlapSampler (range: 2000)
- Filter: ERPTargetableFilter
- Scorer: ERPDistanceScorer (normalize: true), Weight: 1.0
- Resolver: ERPResolverBase
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, Resolver
2. Add ERPInteractionComponent (Channel: "Interact")
3. Bind to OnDescriptorChanged, OnInteractionExecuted/Failed
4. Implement IERPInteractable on interactables
5. Call RequestInteraction() on input
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
ERPPerceptionComponent
// 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);
// 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->RequestInteraction();
// Bind to events
InteractionComp->OnDescriptorChanged.AddDynamic(this, &AMyPC::OnDescriptor);
InteractionComp->OnInteractionExecuted.AddDynamic(this, &AMyPC::OnExecuted);
ERPTargetingComponent
// Configure
TargetingComp->ChannelId = "Target";
// Get descriptor
const UERPTargetDescriptor* 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.DisplayName = FText::FromString(TEXT("Door"));
OutDescriptor.PromptText = FText::FromString(bIsOpen ? TEXT("Close") : TEXT("Open"));
}
virtual bool ExecuteInteraction_Implementation(AActor* InstigatorActor) override
{
bIsOpen = !bIsOpen;
return true;
}
};
Pipeline Configurations
Distance-Based Targeting
Sampler: ERPSphereOverlapSampler (range: 3000)
Filter: ERPTargetableFilter
Scorer: ERPDistanceScorer (weight: 1.0)
Resolver: ERPResolverBase
Camera-Aware Targeting
Sampler: ERPSphereOverlapSampler
Filter: ERPTargetableFilter
Scorers:
- ERPDistanceScorer (weight: 1.0)
- ERPConeScorer (cone: 45, weight: 1.5)
Resolver: ERPResolverBase
Screen-Space Targeting
Sampler: ERPSphereOverlapSampler
Filter: ERPTargetableFilter
Scorer: ERPViewportEllipseScorer (weight: 1.0)
-> Enable bProvideViewportInfoWhenAvailable
Resolver: ERPResolverBase
Debugging
Enable Debug Logging
Set bDebugPerception = true on ERPPerceptionComponent.
Console Variables
elys.perception.SamplerCacheDebug 1 // Log sampler cache hits/misses
Performance
stat GAME // Show component tick times
Performance Tips
Tick Interval
// On ERPPerceptionComponent
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.
Default aggregator: sum of weighted scores. Default resolver: lowest total score wins.
Class Hierarchy
UActorComponent
+-- UERPPerceptionComponent (perception engine)
+-- UERPChannelListenerComponent (lightweight listener)
+-- UERPDomainComponent (abstract base)
| +-- UERPInteractionComponent (interaction domain)
| +-- UERPTargetingComponent (targeting domain)
| +-- YourCustomDomain (extend for custom gameplay)
+-- UERPBaseInteractionFeedbackComponent (visual feedback)
UUserWidget
+-- UERPInteractionWidgetBase (simple prompt)
| +-- UERPHoldInteractionWidgetBase (hold-to-interact with progress)
+-- UERPTargetingWidgetBase (reticle / target info)
Widget Quick Reference
// Simple interaction prompt
InteractionWidget->UpdateFromDescriptor(Descriptor);
InteractionWidget->SetFocused(true);
FText Key = InteractionWidget->GetCurrentInputDisplayKey();
// Hold-to-interact
HoldWidget->UpdateFromDescriptor(Descriptor);
HoldWidget->SetHoldProgress(AccumulatedTime / Descriptor.HoldDuration);
HoldWidget->ResetHold(); // On release
// Targeting
TargetWidget->UpdateFromDescriptor(TargetDescriptor);
TargetWidget->SetTargetActive(true);
TargetWidget->SetTargetActor(TargetActor);
Next Steps
Full guides:
- Setup Guide - Complete installation walkthrough
- Pipeline System - Architecture deep dive
- Interaction Guide - Interaction implementation
- Targeting Guide - Targeting implementation
- Custom Domains - Create your own domains
- Customization - Custom pipeline components
- API Reference - Complete API documentation