UAIPerceptionComponent::ConfigureSense is not updating stimuli listener

I think UAIPerceptionComponent::ConfigureSense is not working properly. For some reason, it’s not calling RequestStimuliListenerUpdate(). When I step through the code and try to step into the RequestStimuliListenerUpdate() function, it goes straight to UActorComponent::GetWorld().

I ran into this issue because my hearing perception would not trigger. After stepping through the code, I realized that UAISense_Hearing::Update() was exiting out early because the FPerceptionListener did not have the hearing sense. I can only get the hearing perception to trigger if I manually call UAIPerceptionComponent::UpdatePerceptionWhitelist in order to add the hearing sense to the perception listener (which should already be done by ConfigureSense).

I thought this might be specific to my engine build, so I made a blank project from the release build. I still had the same problem with getting the hearing perception to trigger. Again, manually calling UpdatePerceptionWhitelist allowed the hearing perception to trigger, so I think there’s an issue with the official release.

There might also be an issue with the UAIPerceptionComponent::SensesConfig array member as well. I noticed that sight perception was able to trigger even though hearing was not. For some reason, the SensesConfig array already contains a UAISenseConfig_Sight value before registering the component, so it’s able to update the the perception listener with the sight config during UAIPerceptionComponent::OnRegister(). But I think that sight config value is nulled out somewhere between the constructor and the begin play phase, because I was running into some really weird errors. The SensesConfig array would have proper sense config values during construction but null sense config values during BeginPlay and my editor would crash trying to access the invalid addresses.

Lastly, I think the FPerceptionUpdatedDelegate should use const TArray& instead of TArray as its parameter type. Else, BlueprintNativeEvents bound to this delegate will cause compilation errors since the compiler gets confused about the TArray type. Is there a good reason why it’s not?

Hopefully my explanations aren’t too confusing. Maybe someone can try to reproduce these issues. Thanks!

Here’s my code sample for the AIController:

[AMyAIController.h]

#pragma once

#include "AIController.h"

#include "Perception/AiPerceptionComponent.h"
#include "Perception/AISenseConfig_Hearing.h"
#include "Perception/AISense_Hearing.h"

#include "MyAIController.generated.h"

/**
 * 
 */
UCLASS()
class TESTPROJECT_API AMyAIController : public AAIController
{
	GENERATED_BODY()
	
public:
	AMyAIController();

	virtual void BeginPlay() override;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "AI")
		UAIPerceptionComponent* AIPerception;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "AI")
		UAISenseConfig_Hearing* HearingConfig;

	UFUNCTION(BlueprintCallable, Category = "AI")
		void PerceptionUpdated(TArray<AActor*> UpdatedActors);
	
};

[AMyAIController.cpp]

#include "TestProject.h"
#include "MyAIController.h"

AMyAIController::AMyAIController()
{
	// Setup the perception component
	AIPerception = GetAIPerceptionComponent();
	AIPerception = CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("AIPerception Component"));

	HearingConfig = CreateDefaultSubobject<UAISenseConfig_Hearing>(TEXT("Hearing Config"));
}

void AMyAIController::BeginPlay()
{
	Super::BeginPlay();

	HearingConfig->HearingRange = 10000.f;
	HearingConfig->DetectionByAffiliation.bDetectEnemies = true;
	HearingConfig->DetectionByAffiliation.bDetectNeutrals = true;
	HearingConfig->DetectionByAffiliation.bDetectFriendlies = true;
	AIPerception->ConfigureSense(*HearingConfig);

	AIPerception->SetDominantSense(HearingConfig->GetSenseImplementation());

	AIPerception->OnPerceptionUpdated.AddDynamic(this, &AMyAIController::PerceptionUpdated);

	// Uncomment this to get the hearing perception to actually trigger
	//AIPerception->UpdatePerceptionWhitelist(HearingConfig->GetSenseID(), true);

	UAIPerceptionSystem::RegisterPerceptionStimuliSource(this, HearingConfig->GetSenseImplementation(), GetPawn());
}

void AMyAIController::PerceptionUpdated(TArray<AActor*> UpdatedActors)
{
	GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, FString::Printf(TEXT("Perception")));
}

Hey vle07,

I have setup a AIController with a sense and everything appears to be working correctly on my end.

[AHAIController.h]

#pragma once

#include "AIController.h"

#include "Perception/AIPerceptionComponent.h"
#include "Perception/AISenseConfig_Sight.h"
#include "Perception/AISense_Sight.h"

#include "AHAIController.generated.h"

/**
 * 
 */
UCLASS()
class AH454882_API AAHAIController : public AAIController
{
	GENERATED_BODY()

public:
	AAHAIController( );
	
	virtual void BeginPlay( ) override;
	virtual void Possess(APawn* InPawn) override;
	void OnPossess( APawn* In );
	
	UAIPerceptionComponent *Perception;
	UAISenseConfig_Sight *Sight;
	
	UFUNCTION( )
	void PerceptionUpdated( TArray<AActor*> Actors  );

};

[AHAIController.cpp]

#include "AH454882.h"
#include "AHAIController.h"

AAHAIController::AAHAIController( )
{
	Perception = CreateDefaultSubobject<UAIPerceptionComponent>( TEXT("Perception") );
	Sight = CreateDefaultSubobject<UAISenseConfig_Sight>( TEXT("Sight") );

	if( Perception )
	{
		Perception->ConfigureSense(*Sight);
		Perception->SetDominantSense( Sight->GetSenseImplementation( ) );
	}
}

void AAHAIController::BeginPlay()
{
	if( Perception )
	{
		Perception->OnPerceptionUpdated.AddDynamic( this, &AAHAIController::PerceptionUpdated );
	}

	UAIPerceptionSystem::RegisterPerceptionStimuliSource(this, UAISense_Sight::StaticClass(), this);

	Super::BeginPlay( );
}
 
void AAHAIController::Possess(APawn* InPawn)
{
	Super::Possess( InPawn );
	OnPossess( InPawn );
}

void AAHAIController::OnPossess(APawn* In)
{
	if( Sight )
	{
		Sight->SightRadius = 1536;
		Sight->LoseSightRadius = 2048;
		Sight->PeripheralVisionAngleDegrees = 270;
		Sight->DetectionByAffiliation.bDetectEnemies = true;
		Sight->DetectionByAffiliation.bDetectFriendlies = true;
		Sight->DetectionByAffiliation.bDetectNeutrals = true;
		Sight->AutoSuccessRangeFromLastSeenLocation = 200;
		
		if( Perception )
		{
			Perception->ConfigureSense( *Sight );
		}
	}
}

void AAHAIController::PerceptionUpdated(TArray<AActor*> Actors)
{
	for( int i = 0; i < Actors.Num( ); i++ )
	{
		UE_LOG( LogTemp, Warning, TEXT("See: %s"), *Actors[ i ]->GetName( ) );
	}
}

There is also the Survival Game example that has been going on in the forums that handles senses a bit differently. If you want to check it out, you can here:

https://forums.unrealengine.com/showthread.php?63678-Upcoming-C-Gameplay-Example-Series-Making-a-Survival-Game

The code for it is hosted on GitHub, here:

https://github.com/tomlooman/EpicSurvivalGameSeries

If you are still having issues, please update your post with more information (such as a code sample).

In my post (4th paragraph), I mentioned that only UAISense_Sight was triggering because it was being added to the perception listener in OnRegister() rather than ConfigureSense(). Can you retry your setup with UAISense_Hearing instead? Also, can you try putting a break point on the ConfigureSense() function and try to step into RequestStimuliListenerUpdate() and see if it actually takes you to the correct function? I’ve updated my original post with my AIController code. Thanks!

Hey again,

Like Sight, I am not having any issue reporting noise to a Hearing sense:

[AHAIController.h]

#pragma once

#include "AIController.h"

#include "Perception/AIPerceptionComponent.h"
#include "Perception/AISenseConfig_Hearing.h"
#include "Perception/AISense_Hearing.h"

#include "AHAIController.generated.h"

UCLASS()
class AH454882_API AAHAIController : public AAIController
{
	GENERATED_BODY()

public:
	AAHAIController( );
	
	virtual void BeginPlay() override;
	virtual void Possess( APawn *InPawn ) override;
	void OnPossess( APawn *In );

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "AI")
	UAIPerceptionComponent* Perception;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "AI")
	UAISenseConfig_Hearing* Hearing;

	UFUNCTION(BlueprintCallable, Category = "AI")
	void PerceptionUpdated(TArray<AActor*> UpdatedActors);

};

[AHAIController.cpp]

#include "AH454882.h"
#include "AHAIController.h"

AAHAIController::AAHAIController( )
{
	Perception = CreateDefaultSubobject<UAIPerceptionComponent>( TEXT("Perception") );
	Hearing = CreateDefaultSubobject<UAISenseConfig_Hearing>( TEXT("Hearing") );

	if( Perception )
	{
		Perception->ConfigureSense(*Hearing);
		Perception->SetDominantSense(Hearing->GetSenseImplementation( ) );
	}
}

void AAHAIController::BeginPlay()
{
	if( Perception )
	{
		Perception->OnPerceptionUpdated.AddDynamic( this, &AAHAIController::PerceptionUpdated );
	}

	UAIPerceptionSystem::RegisterPerceptionStimuliSource(this, UAISense_Hearing::StaticClass(), this);

	Super::BeginPlay( );
}
 
void AAHAIController::Possess(APawn* InPawn)
{
	Super::Possess( InPawn );
	OnPossess( InPawn );
}

void AAHAIController::OnPossess(APawn* In)
{
	if( Hearing )
	{
		Hearing->HearingRange = 2048;
		Hearing->DetectionByAffiliation.bDetectEnemies = true;
		Hearing->DetectionByAffiliation.bDetectFriendlies = true;
		Hearing->DetectionByAffiliation.bDetectNeutrals = true;
		
		if( Perception )
		{
			Perception->ConfigureSense( *Hearing );
		}
	}
}

void AAHAIController::PerceptionUpdated(TArray<AActor*> Actors)
{
	for( int i = 0; i < Actors.Num( ); i++ )
	{
		UE_LOG( LogTemp, Warning, TEXT("Hear %s at distance: %f"), *Actors[ i ]->GetName( ), ( GetPawn( )->GetActorLocation( ) - Actors[ i ]->GetActorLocation( ) ).Size( ) );
	}
}

Then, within my Character Jump( ) function:

void AAH454882Character::Jump( )
{
	Super::Jump( );

	MakeNoise( 1200.f, this, GetActorLocation( ), 1200.f );
}

And with every Jump( ), I get the log:

99147-454882_log.png

Thanks, I tried moving my ConfigureSense call to the Possess function like you have and it seems to work in my test project. Unfortunately, it doesn’t work in my main project. I think there’s something wrong with my perception references, so I’ll let you know once I figure it out.

After banging my head against the wall for hours, it looks like my blueprint which inherits from my AIController was corrupt. I just created a new blueprint and it works fine. I didn’t realize that blueprints could have this problem even after cleaning, compiling, and rebuilding the solution.

Thanks for the help, Kyle! There isn’t very much information on the perception system available, so the sample code you provided really helped narrow down what was wrong. By the way, do you think there will be any issue if I change the FPerceptionUpdatedDelegate to use const TArray& instead of TArray as its parameter type? I can’t get BlueprintNativeEvents bound to this delegate to compile without this change, since the compiler gets confused with TArray types.

No problem. I’m glad you have figured out the issue you were having.

I can’t tell you with certainty if FPerceptionUpdatedDelegate should use TArray& vs TArray as the parameter but with the source code, you are able to change it in anyway you see fit. If you’re not running the engine through the source code, you can find out how here:

[][1]

[1]: