UAIPerceptionComponent C++, OnPerceptionUpdated and OnTargetPerceptionUpdated not getting called

Hi,
Im trying to use the UAIPerceptionComponent on my AI however OnPerceptionUpdated andOnTargetPerceptionUpdated are never called. Any ideas why?

In my AIController header file:

UFUNCTION()
		void OnPerceptionUpdated(const TArray<AActor*>& UpdatedActors);

	UFUNCTION()
		void OnTargetPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus);

	UFUNCTION()
		virtual void OnPossess(APawn* pawn) override;

UPROPERTY(EditAnywhere)
		class UAIPerceptionComponent* AIPerception;

In my AI controller cpp:
Constructor:

IPerception = CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("AI Perception"));
	SetPerceptionComponent(*AIPerception);
	
	UAISenseConfig_Sight* SightConfig = CreateDefaultSubobject<UAISenseConfig_Sight>(TEXT("Sight Config"));
	SightConfig->SightRadius = 3000.0f;
	SightConfig->LoseSightRadius = 3500.0f;
	SightConfig->PeripheralVisionAngleDegrees = 90.0f;
	AIPerception->ConfigureSense(*SightConfig);
	AIPerception->SetDominantSense(SightConfig->GetSenseImplementation());

OnPossesses Func:

Super::OnPossess(pawn);
	UE_LOG(LogTemp, Warning, TEXT("Possessed"));
	AIPerception->OnPerceptionUpdated.AddDynamic(this, &AMainAIController::OnPerceptionUpdated);
	AIPerception->OnTargetPerceptionUpdated.AddDynamic(this, &AMainAIController::OnTargetPerceptionUpdated);

The two Perception Functions simple log to the console to see if they’re ever called

void AMainAIController::OnPerceptionUpdated(const TArray<AActor*>& UpdatedActors)
{
	UE_LOG(LogTemp, Warning, TEXT("PerUpdated"));
	for (AActor* Actor : UpdatedActors)
	{
		UE_LOG(LogTemp, Warning, TEXT("Actor: %s"), *Actor->GetName());
	}
	
}

void AMainAIController::OnTargetPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus)
{
	UE_LOG(LogTemp, Warning, TEXT("Target Per"));
}

Thank you

2 Likes

First off all they need to be override the base class to be called and second I think that OnPerceptionUpdated and OnTargetPerceptionUpdated are blueprint events not available in cpp. A function very similar is ActorsPerceptionUpdated().

I recommend configuring your sight before setting the perception component like this , you can use the same code in your " OnPossess" function

YourController::YourController() {

    AIPerceptionComponent = CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("AIPerceptionComponent"));

    // Create Sight configuration for the AIPerception component
    UAISenseConfig_Sight* SightConfig = CreateDefaultSubobject<UAISenseConfig_Sight>(TEXT("SightConfig"));
    SightConfig->SightRadius = 1000.0f; // Set your sight radius
    SightConfig->LoseSightRadius = 1200.0f; // Set your lose sight radius
    SightConfig->PeripheralVisionAngleDegrees = 180.0f; // Set your peripheral vision angle
    SightConfig->SetMaxAge(0.0f); // Set your max age, 0.0f means never expires
    // detect friendly pawns
    SightConfig->DetectionByAffiliation.bDetectEnemies = true;
    SightConfig->DetectionByAffiliation.bDetectFriendlies = true;
    SightConfig->DetectionByAffiliation.bDetectNeutrals = true;
  

 // Register the sight configuration with the AIPerception component
    AIPerceptionComponent->ConfigureSense(*SightConfig);
    
 // Set the AIPerception component 
    SetPerceptionComponent(*AIPerceptionComponent);

// Bind your functions to the delegates
    GetPerceptionComponent()->OnPerceptionUpdated.AddDynamic(this, &YourController::ActorsPerceptionUpdated);
    GetPerceptionComponent()->OnTargetPerceptionUpdated.AddDynamic(this, &YourController::TargetActorsPerceptionUpdated);
     
}

void YourController::TargetActorsPerceptionUpdated(AActor* TargetActor, FAIStimulus Stimulus)
{
    if (TargetActor->ActorHasTag("Player")) {
        if (Stimulus.WasSuccessfullySensed()) {
            // Player is currently seen
            UE_LOG(LogTemp, Warning, TEXT("I see you!"));
        } else {
            // Player was last seen
            FVector LastSeenLocation = Stimulus.StimulusLocation;
            UE_LOG(LogTemp, Warning, TEXT("I last saw you at: %s"), *LastSeenLocation.ToString());
        }
    }
}

void YourController::ActorsPerceptionUpdated(const TArray<AActor*>& UpdatedActors) {
    // Iterate through updated actors
    for (int32 i = 0; i < UpdatedActors.Num(); i++) {
        // Check if the updated actor is the player
        if (UpdatedActors[i]->ActorHasTag("Player")) {
            // Log a message for testing
            UE_LOG(LogTemp, Warning, TEXT("I see you!"));
        }
    }
}

also make sure you configured your stimuli source component in your Character class constructor or any other Actor that you want the perception system to detect. Sight is just an example in the snippet below, you can register more senses, i suggest testing it in blue prints first and then use it in code

	// set up stimulus source component in your Character class for example
	UAIPerceptionStimuliSourceComponent* StimuliSourceComponent = 		CreateDefaultSubobject<UAIPerceptionStimuliSourceComponent>(TEXT("StimulusSourceComponent"));
	
	// Set the custom tag 
        StimuliSourceComponent->ComponentTags.Add(FName("Player"));
	StimuliSourceComponent->RegisterForSense(TSubclassOf<UAISense_Sight>());
	StimuliSourceComponent->RegisterWithPerceptionSystem();

You should be able to successfully sense registered stimuli after making these changes.

2 Likes

OnPerceptionUpdated and OnTargetPerceptionUpdated are delegates in AIPerceptionComponent.h, they are available in cpp, you can bind them to your cpp functions, see the snippets below

// your controller.h
public: 
    
    UFUNCTION()
    void ActorsPerceptionUpdated(const TArray<AActor*>& UpdatedActors);
    UFUNCTION()
    void TargetActorsPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus);

In your controller cpp bind your functions in the constructor like this

// YourController.cpp
 GetPerceptionComponent()->OnPerceptionUpdated.AddDynamic(this, &YourController::ActorsPerceptionUpdated);
    GetPerceptionComponent()->OnTargetPerceptionUpdated.AddDynamic(this, &YourController::TargetActorsPerceptionUpdated);