AI Perception in Unreal Engine 4 – How to Setup

For those of you who are wondering what AI Perception is – this is a really nice system in UE4 that provides an easy way for the enemy AI in your game to track hostile/friendly/neutral characters. For example if the enemy AI sees the main character, he will attack it an so on. The AI can become aware of characters through vision, or if they hear them. There are many predefined senses.

The reason for this post is that setting up the AI Perception is very easy, but there is not much documentation yet and it was very hard for me to find a proper way to set it right.

The Setup

There are two components that we need: AIPerceptionComponent and AIPerceptionStimuliSourceComponent. The AIPerceptionComponent is the component that listens for perception stimulants (sight, hearing, etc.). The AIPerceptionStimuliSourceComponent is a stimuli source for the AIPerceptionComponent. The stimuli source stimulates the perception of the enemy AI, so that it can detect the source (our character for example).

I’ve seen many people add the AIPerceptionComponent to the enemy Character, but that is incorrect. It must be added to it’s AI Controller.

AIPerceptionComponent Setup

We create a default AIController, and the only component we add to it is an AIPerception Component. We are going to configure it only for sight sense like so.

AEnemyAIController

UAIPerceptionComponentSetup

I left everything to the default values. As you can see it’s setup only to detect enemies.

AIPerceptionStimuliSourceComponent Setup

The AIPerceptionStimuliSourceComponent must be added to any actors that we want to be stimuli sources for the AIPerceptionComponent. In our case we add it to our character.

AKnightCharacter

UAIPerceptionStimuliSourceComponent

Is it working?

Lets add an event to the EnemyAIController Blueprint to see if it’s working.

BP_EnemyAIController

PerceptionNotWorking

Well, the “Found” message is not printed so it’s not working. Well I can actually say that it works properly. The message is not being printed on screen because the enemy is not considering us as an enemy. By default all characters are neutral to each other. So how do we tell the enemy AI that the main character is actually an enemy?

Different Teams Setup

We need to place the enemy AI and our character in different teams. By default they are in a team NoTeam which is an enum value that equals to 255. Unfortunately it can’t be done entirely in blueprints. We need to write some code.

In the constructor of the AEnemyAIController we set the team of the AI like so.

AEnemyAIController::AEnemyAIController(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// Assign to Team 1
	SetGenericTeamId(FGenericTeamId(1));
}

We can’t do the same with the player controller however. Here is the tricky part. A player is controlling a character that is of a certain team, the controller itself doesn’t have a team.

In order to assign our character to a team he needs to implement the IGenericTeamAgentInterface interface. The AIController already implements it. Then we need to override the GetGenericTeamId function. Lets do it.

// KnightCharacter.h
UCLASS()
class THEKNIGHT_API AKnightCharacter : public ACharacter, public IGenericTeamAgentInterface
{
	GENERATED_BODY()

	// ...

private:
	FGenericTeamId TeamId;

	virtual FGenericTeamId GetGenericTeamId() const override;

	// ...
};

// KnightCharacter.cpp
AKnightCharacter::AKnightCharacter(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// ...
	TeamId = FGenericTeamId(0);
	// ...
}

FGenericTeamId AKnightCharacter::GetGenericTeamId() const
{
	return TeamId;
}

Now if we test it again, the enemy is considering us as an enemy and is seeing us.

PerceptionWorking

The reason our character is “Found” if we exit the field of view of the enemy is that the perceptions is updated when we exit the sight of the enemy too.

20 thoughts on “AI Perception in Unreal Engine 4 – How to Setup”

  1. Hello, I’m very new to the Unreal 4 engine. I am curious how to do the C++ end of things while using blueprints.

    After you had said “In the constructor of the AEnemyAIController we set the team of the AI like so.” I had become lost. I don’t know really how to access the C++ side of things from a Blueprint project yet. Is there any way you could open your project for download so I could attempt to reverse engineer it and learn that way? It would be of help.

    1. Hi mate, sorry for the late reply.
      Well if you are using a blueprint project only that means you don’t have any C++ code at all.
      When creating an AIController blueprint it derives from AIController.
      To access the C++ code you must first create a C++ class that derives from AIController, and then create a blueprint that derives from the C++ class you just created.
      That way you can have any base logic in the C++ class, and you can still work with the blueprint like you normally do.

  2. Hello,

    First of all, thanks for the post, it really helped me with my current project. I’ve got a question, how should I set the desired attitude towards a sensed character? In your example you successfully made the AIController detect the player character as an enemy. How can I make it to sense that player as friendly or as neutral? Or to make the controller detect other AI controlled characters with different attitude?

    Again, thanks and keep up the good work!

    1. Hi,

      If you want to detect neutral or friendly characters, you just have to modify the AIPerceptionComponent to also detect those.
      Actors are friendly if they are in the same team different from NoTeam, which is an enum value of 255. So if both are of team 5, for example, they are friendly.
      Neutral actors need to be assigned to team NoTeam. If the actors are in different teams different from NoTeam, then they are enemies.
      As for how to check the attitude towards an actor – there is a really nice method for that: Link

  3. Hey man, do you know how to actually attach the Sight cone to the head of the character? My character turns his head around with animations, but the Sight cone still points forward (in the direction of the pawn itself)

  4. hey mate,
    im trying to get this to work, but stumbling at the first bit of c++. Ive created a c++ class from the aicontroller. Now im trying to work out where i put your code.
    When i past it directly into the constructor i get errors.
    Im not great in c++ as you can see. Ive looked for other tutorials, but yours is the only one that seems to come up 🙂

    1. I am doing all of the initializations only in the constructors. Both in the EnemyAIController and the KnightCharacter.
      I did this in 4.15, maybe they changed something. Try in BeginPlay() or something.

  5. Your tutorial perfectly works for 4.18. Awesome! Thank you!

    I’ve tried C++ code. Let me share it in the case of anybody interested.
    It looks like the following:

    Teams ids defines:

    // team ids
    #define PTG_TEAM_ID_PLAYER 0
    #define PTG_TEAM_ID_ENEMY 1

    Enemy AI controller class implementation:

    UCLASS()
    class PTG_API APTGAIControllerEnemy : public AAIController
    {
    // ...;

    private:
    FGenericTeamId GetGenericTeamId() const override;

    UFUNCTION()
    void OnPerceptionUpdated(TArray actors);

    UPROPERTY()
    UAISenseConfig_Sight* SightConfig;
    };


    APTGAIControllerEnemy::APTGAIControllerEnemy()
    {
    SightConfig = CreateDefaultSubobject(TEXT("SightConf"));

    PerceptionComponent = CreateDefaultSubobject(TEXT("Perception"));
    PerceptionComponent->ConfigureSense(*SightConfig);
    PerceptionComponent->SetDominantSense(SightConfig->GetSenseImplementation());
    PerceptionComponent->OnPerceptionUpdated.AddDynamic(this, &APTGAIControllerEnemy::OnPerceptionUpdated);
    }

    void APTGAIControllerEnemy::OnPerceptionUpdated(TArray actors)
    {
    UE_LOG(LogPTG, Error, TEXT("perception updated"));
    }

    FGenericTeamId APTGAIControllerEnemy::GetGenericTeamId() const
    {
    return PTG_TEAM_ID_ENEMY;
    }

    Player character class implementation:

    UCLASS(Blueprintable)
    class APTGCharacter
    : public ACharacter
    , public IGenericTeamAgentInterface
    {
    // ...;

    private:
    FGenericTeamId GetGenericTeamId() const override;

    UPROPERTY()
    class UAIPerceptionStimuliSourceComponent* AIPerceptionStimuliSource;
    };


    APTGCharacter::APTGCharacter()
    {
    // ...;
    AIPerceptionStimuliSource = CreateDefaultSubobject(TEXT("PercSS"));
    AIPerceptionStimuliSource->RegisterForSense(UAISense_Sight::StaticClass());
    }

    FGenericTeamId APTGCharacterBase::GetGenericTeamId() const
    {
    return PTG_TEAM_ID_PLAYER;
    }

  6. Dear Mr Rizov
    A horse walks into a bar. The barman says ‘why the long face?’.

    This is an English joke (having a long face is to be looking sad). But, you have the longest face that I have ever seen. You could pass for a horse if you wanted to.

    Anyway, personal remarks to one side, I really like your content here. Thanks

    cheers
    podgyhodgy.

  7. Thank you for sharing the article (this is the only one I can see out there explaining how to handle the teams stuff via C++). I have just a question, why are you putting the Team membership logic for the player on the Character? It sounds more correct to have it in the player controller (like for the AIController), doesn’t it?

  8. For completeness, if one wants to differentiate the cases of “found actor” and “lost track of actor”, the following code would be helpful:

    void AQLAIController::OnPerceptionUpdatedImpl(const TArray<AActor*>& UpdatedActors)
    {
        for (auto&& Target : UpdatedActors)
        {
            FActorPerceptionBlueprintInfo Info;
            PerceptionComponent->GetActorsPerception(Target, Info);
    
            for (const auto& Stimulus : Info.LastSensedStimuli)
            {
                if (Stimulus.Type == UAISense::GetSenseID(UAISense_Sight::StaticClass()))
                {
                    bool bSenseResult = Stimulus.WasSuccessfullySensed();
                    if (bSenseResult)
                    {
                        // the actor enters the sense range
                    }
                    else
                    {
                        // the actor leaves the sense range
                    }
                }
            }
        }
    }
    

    Thanks for the tutorial again : D

  9. Hi Denis,
    I just wanted to thank you for that GREAT article.
    It was so helpful to me! The doc was really lacking key parts of your article.
    Thanks again!

  10. Question: Instead of setting the Perception like the AI Controller, why is the Preceptioncomponent is created in ACharacter class….

    1. You can try. When I was writing the post I read somewhere that this was the correct way to do it. In practice, you should be able to set a Perception Component for any actor, so try it 🙂

Leave a reply to linesprod Cancel reply