Modify AI perception sense range during runtime

I have been looking around quite a lot to try and find an answer to this question, but as I am fairly new to using UE4 and still not great at c++ I was hoping someone could point me in the right direction.

What I want to be able to do is to modify the range of senses during runtime for my AI. This is to be able to make the range smaller if the AI is chasing after a sound or to make the sight radius bigger if the player is using a flashlight.

After finding this link and messing around in the BP editor I assume that the functionality was never added to blueprints, and as such I need to be able to do it using c++, but I am not sure how to use the provided code to expose the sense range to the BP editor.

I also have this same question. There is an old response that has some code on it, but for the life of me I could not get it to work. Maybe I just don’t understand the AI Perception well enough.

Modify AIPerception Sight Range at runtime [4.7.6] - Programming & Scripting - Unreal Engine Forums

That’s the link I found that apparently solved the problem for this person. I personally couldn’t implement it, but my coding skills are still at the beginner level so I don’t really even know where you would add that to make it work.

I created example project for my old code (SetSightRange function inside Util.h/cpp)

[download MyProject2.7z][1]

You can see the result using the gameplay debugger

1 Like

Relevant code if anyone is interested. Compiles on 4.19.2 and seems to be working.

bool UYourFunctionLibrary::SetSightRange(AAIController* Controller, float NewSightRange)
{
	if (Controller == nullptr)
	{
		UE_LOG(LogTemp, Error, TEXT("Controller == nullptr"));
		return false;
	}

	FAISenseID Id = UAISense::GetSenseID(UAISense_Sight::StaticClass());
	if (!Id.IsValid())
	{
		UE_LOG(LogTemp, Error, TEXT("Wrong Sense ID"));
		return false;
	}

	auto Perception = Controller->GetAIPerceptionComponent();
	if (Perception == nullptr)
	{
		UE_LOG(LogTemp, Error, TEXT("Perception == nullptr"));
		return false;
	}

	auto Config = Perception->GetSenseConfig(Id);
	if (Config == nullptr)
	{
		UE_LOG(LogTemp, Error, TEXT("Config == nullptr"));
		return false;
	}

	auto ConfigSight = Cast<UAISenseConfig_Sight>(Config);

	// Save original lose range
	float LoseRange = ConfigSight->LoseSightRadius - ConfigSight->SightRadius;

	ConfigSight->SightRadius = NewSightRange;

	// Apply lose range to new radius of the sight
	ConfigSight->LoseSightRadius = ConfigSight->SightRadius + LoseRange;

	Perception->RequestStimuliListenerUpdate();

	return true;
}
2 Likes

I took the code from v.s.'s post and split it into two functions, so that I can easily add the same thing for hearing or other senses. Something that may need explanation is how the LoseSightRadius value gets automatically set, it takes the difference of the current SightRadius from LoseSightRadius and adds that to NewSightRadius. In other words it keeps the same distance between SightRadius and LoseSightRadius and uses that with your NewSightRadius.

Header File

static UAISenseConfig* GetPerceptionSenseConfig(AAIController *Controller, TSubclassOf<UAISense> SenseClass);

UFUNCTION(BlueprintCallable)
		static bool SetSightRange(AAIController* Controller, float SightRange);

CPP File

UAISenseConfig* UYourFunctionLibrary::GetPerceptionSenseConfig(AAIController* Controller, TSubclassOf<UAISense> SenseClass)
{
	UAISenseConfig * result = nullptr;

	FAISenseID Id = UAISense::GetSenseID(SenseClass);
	if (!Id.IsValid())
	{
		UE_LOG(LogTemp, Error, TEXT("GetPerceptionSenseConfig: Wrong Sense ID"));
	}
	else if (Controller == nullptr)
	{
		UE_LOG(LogTemp, Error, TEXT("GetPerceptionSenseConfig: Controller == nullptr"));
	}
	else
	{
		UAIPerceptionComponent *Perception = Controller->GetAIPerceptionComponent();
		if (Perception == nullptr)
		{
			UE_LOG(LogTemp, Error, TEXT("GetPerceptionSenseConfig: Perception == nullptr"));
		}
		else
		{
			result = Perception->GetSenseConfig(Id);
		}
	}

	return result;
}

bool UYourFunctionLibrary::SetSightRange(AAIController* Controller, float SightRange)
{
	UAISenseConfig* Config = GetPerceptionSenseConfig(Controller, UAISense_Sight::StaticClass());
	if (Config == nullptr)
	{
		UE_LOG(LogTemp, Error, TEXT("SetSightRange: Config == nullptr"));
		return false;
	}
	else
	{
		UAISenseConfig_Sight *ConfigSight = Cast<UAISenseConfig_Sight>(Config);

		UE_LOG(LogTemp, Verbose, TEXT("SetSightRange was %f %f, setting to %f %f")
			, ConfigSight->SightRadius, ConfigSight->LoseSightRadius, SightRange, (ConfigSight->LoseSightRadius - ConfigSight->SightRadius + SightRange));

		// Save original lose range
		float LoseRange = ConfigSight->LoseSightRadius - ConfigSight->SightRadius;
		ConfigSight->SightRadius = SightRange;
		// Apply lose range to new radius of the sight
		ConfigSight->LoseSightRadius = ConfigSight->SightRadius + LoseRange;
		UAIPerceptionComponent *Perception = Controller->GetAIPerceptionComponent();
		Perception->RequestStimuliListenerUpdate();
	}

	return true;
}
2 Likes

Thank you this code helped me a lot!

Any ideas how to do this without having to pass an AIController, I am using the perception system on my player character to work out when NPC’s are in range, so I need a blueprint that takes the perception component and not the AIController - any ideas?

I’m not positive but I think the perception component might be a child of AIcontroller on mine.

Does anyone still have the files or the entire code for this? I can’t seem to get this to work and I don’t know if it’s UE4.26 or my own incompetence in c++

This is what I added to my function library files:

Header:
#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CPP_SetPerceptionRange.generated.h"

UCLASS()
class GAME_API UCPP_SetPerceptionRange : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()


		static UAISenseConfig* GetPerceptionSenseConfig(AAIController* Controller, TSubclassOf<UAISense> SenseClass);

	UFUNCTION(BlueprintCallable)
		static bool SetSightRange(AAIController* Controller, float SightRange);

};

C++ File:

#include "CPP_SetPerceptionRange.h"


UAISenseConfig* UCPP_SetPerceptionRange::GetPerceptionSenseConfig(AAIController* Controller, TSubclassOf<UAISense> SenseClass)
{
    UAISenseConfig* result = nullptr;

    FAISenseID Id = UAISense::GetSenseID(SenseClass);
    if (!Id.IsValid())
    {

… with the rest of Modeus Code in the c++ file, I cant paste it all cause of the Message length restriction.

it just won’t compile, what am I doing wrong here?

ok so mordeus solution works perfectly, all I had to do was add this to my .h file:

#include "AIController.h"
#include "Perception/AISenseConfig.h"
#include "Perception/AISense.h"
#include "Perception/AISenseConfig_Sight.h"
#include "Perception/AIPerceptionComponent.h"
#include "Perception/AISenseConfig.h"

I hope this helps anyone who is as stupid as I am!

1 Like

Try to add “AIModule” in your project .Build.cs file

PublicDependencyModuleNames.AddRange(new string[] { 
			"Core",
			"CoreUObject", 
			"Engine", 
			"InputCore", 
			"Sockets", 
			"Networking",
			"AIModule"
		});

Thank you! Just what I was looking for.