Skip to content
Learni
Voir tous les tutoriels
Développement de Jeux

Comment implémenter une IA avancée en C++ sur Unreal Engine 2026

Read in English

Introduction

Développer une IA sophistiquée est crucial pour des jeux immersifs en 2026, où les joueurs exigent des comportements réalistes et réactifs. Unreal Engine excelle avec ses Behavior Trees (BT), Blackboards et modules AI, mais pour des performances optimales sous charge (centaines d'NPCs), le C++ surpasse les Blueprints en encapsulation, vitesse d'exécution et contrôle fin.

Ce tutoriel avancé vous guide pour créer un système d'IA patrouilleuse : un AI Controller custom qui pilote un Pawn via un BT avec tâche personnalisée générant des points aléatoires. Imaginez comme un gardien virtuel arpentant un périmètre – scalable pour open-worlds.

Avec 15 ans d'expérience en game dev, je partage du code complet et compilable, des analogies (BT comme un arbre décisionnel d'entreprise) et des astuces pros. À la fin, intégrez-le en 30 min dans n'importe quel projet UE5.4+. Prêt à booster vos NPCs ? (128 mots)

Prérequis

  • Unreal Engine 5.4+ installé via Epic Games Launcher (version LTS recommandée pour stabilité).
  • Visual Studio 2022+ avec Game Development with C++ workload (inclut Windows SDK 10.0.22621+).
  • Projet Unreal C++ existant (ex: Third Person template généré en C++).
  • Connaissances avancées : UCLASS/UPROPERTY, Actors/Components, AI basics (Navigation Mesh).
  • Git pour versionning (optionnel, mais pro).

Étape 1: Configurer les dépendances du module

Avant de coder l'IA, ajoutez les modules AI à votre MyProject.Build.cs. Cela active BehaviorTree, AIModule et GameplayTasks – essentiels pour BT et Blackboards. Sans ça, compilation échoue avec 'UBlackboardComponent undeclared'.

Ouvrez Source/MyProject/MyProject.Build.cs. Remplacez le contenu par le code ci-dessous. Analogie : comme importer des libs en C++ std, ici UE charge dynamiquement les features AI au link-time.

MyProject.Build.cs

Source/MyProject/MyProject.Build.cs
using UnrealBuildTool;

public class MyProject : ModuleRules
{
	public MyProject(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

		PublicDependencyModuleNames.AddRange(new string[] {
			"Core",
			"CoreUObject",
			"Engine",
			"InputCore",
			"AIModule",
			"GameplayTasks",
			"NavigationSystem",
			"Niagara"
		});

		PrivateDependencyModuleNames.AddRange(new string[] {
		});

		// Uncomment if you are using Slate UI
		// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });

		// Uncomment if you are using online features
		// PrivateDependencyModuleNames.Add("OnlineSubsystem");

		// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute, include steam-sdk-thirdparty.props, and add it to PrivateDependencyModuleNames in this file
		// PrivateDependencyModuleNames.AddRange(new string[] { "OnlineSubsystemSteam" });
	}
}

Ce fichier complet définit les modules publics/privés pour votre projet. Ajout de 'AIModule' et 'GameplayTasks' active BT/Blackboard sans bloat. 'NavigationSystem' pour pathfinding. Regenérez les files VS (right-click .uproject > Generate VS files) après sauve. Piège : oublier 'Public' rend UPROPERTY invisibles en Blueprint.

Étape 2: Implémenter l'AI Controller (header)

L'AIController est le cerveau de votre IA : possédé par le Pawn, il gère le BT et Blackboard. Créez MyAIController.h via Editor (Tools > New C++ Class > AIController). Remplacez par ce header.

Clés UPROPERTY : exposez le BT à l'éditeur pour assignation rapide. Analogie : comme un chef d'orchestre assignant partitions (BT) à musiciens (tasks).

MyAIController.h

Source/MyProject/MyAIController.h
#pragma once

#include "CoreMinimal.h"
#include "AIController.h"
#include "BehaviorTree/BehaviorTreeComponent.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "MyAIController.generated.h"

UCLASS()
class MYPROJECT_API AMyAIController : public AAIController
{
    GENERATED_BODY()

public:
    AMyAIController();

protected:
    virtual void BeginPlay() override;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AI")
    UBehaviorTreeComponent* BehaviorTreeComponent;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AI")
    UBlackboardComponent* BlackboardComponent;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
    class UBehaviorTree* BehaviorTree;

    virtual void OnPossess(APawn* InPawn) override;

public:
    virtual void Tick(float DeltaTime) override;

    UFUNCTION(BlueprintCallable, Category = "AI")
    void StartPatrol();
};

Header complet avec subobjects BT/Blackboard créés en ctor. OnPossess() run le BT. UFUNCTION pour appel Blueprint. VisibleAnywhere pour debug éditeur. Piège : oublier GENERATED_BODY() casse hot-reload.

MyAIController.cpp

Source/MyProject/MyAIController.cpp
#include "MyAIController.h"
#include "BehaviorTree/BehaviorTree.h"
#include "Kismet/GameplayStatics.h"

AMyAIController::AMyAIController()
{
    PrimaryActorTick.bCanEverTick = true;

    BehaviorTreeComponent = CreateDefaultSubobject<UBehaviorTreeComponent>(TEXT("BehaviorTreeComponent"));
    BlackboardComponent = CreateDefaultSubobject<UBlackboardComponent>(TEXT("BlackboardComponent"));
}

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

void AMyAIController::OnPossess(APawn* InPawn)
{
    Super::OnPossess(InPawn);

    if (BehaviorTree)
    {
        BehaviorTreeComponent->StartTree(*BehaviorTree);
    }
}

void AMyAIController::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
}

void AMyAIController::StartPatrol()
{
    RunBehaviorTree(BehaviorTree);
}

Implémente ctor avec CreateDefaultSubobject pour ownership UE. OnPossess() démarre BT si assigné. StartPatrol() pour appel manuel. Minimaliste mais extensible (ajoutez EQS pour queries). Compile sans warning UE5.4+.

Étape 3: Créer le Pawn contrôlé par l'IA

MyAIPawn est le corps : mesh mobile avec capsule collision. Hérite de APawn pour simplicité. Créez via Editor (Pawn class). Ajoutez SpringArm + Camera pour debug vue troisième personne.

Exposez ControllerClass pour auto-possess. Analogie : Pawn comme un robot, Controller son pilote distant.

MyAIPawn.h

Source/MyProject/MyAIPawn.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "MyAIPawn.generated.h"

UCLASS()
class MYPROJECT_API AMyAIPawn : public APawn
{
    GENERATED_BODY()

public:
    AMyAIPawn();

protected:
    virtual void BeginPlay() override;

public:
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    class UCapsuleComponent* CapsuleComponent;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    class UStaticMeshComponent* MeshComponent;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    class USpringArmComponent* SpringArm;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    class UCameraComponent* Camera;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
    TSubclassOf<AMyAIController> AIControllerClass;
};

Components standards : Capsule pour collision/NavMesh, Mesh pour visuel (assign SkeletalMesh en éditeur). SpringArm/Camera pour follow-cam debug. AIControllerClass pour spawn possess. Scalable : ajoutez MovementComponent plus tard.

MyAIPawn.cpp

Source/MyProject/MyAIPawn.cpp
#include "MyAIPawn.h"
#include "Components/CapsuleComponent.h"
#include "Components/StaticMeshComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "Kismet/GameplayStatics.h"

AMyAIPawn::AMyAIPawn()
{
    PrimaryActorTick.bCanEverTick = true;

    CapsuleComponent = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CapsuleComponent"));
    RootComponent = CapsuleComponent;
    CapsuleComponent->InitCapsuleSize(42.f, 96.f);

    MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
    MeshComponent->SetupAttachment(RootComponent);
    MeshComponent->SetRelativeLocation(FVector(0.f, 0.f, -96.f));
    MeshComponent->SetRelativeRotation(FRotator(0.f, -90.f, 0.f));

    SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->TargetArmLength = 300.f;
    SpringArm->bUsePawnControlRotation = true;

    Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
    Camera->SetupAttachment(SpringArm);

    AIControllerClass = AMyAIController::StaticClass();
}

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

    if (AIControllerClass)
    {
        AMyAIController* AIController = GetWorld()->SpawnActor<AMyAIController>(AIControllerClass);
        if (AIController)
        {
            AIController->Possess(this);
        }
    }
}

Ctor attache components hiérarchiquement (Root > Mesh > SpringArm > Cam). Auto-spawn/possess AIController en BeginPlay. Mesh rotated pour capsule standard. Fonctionnel out-of-box : droppez en level, NavMesh agent auto.

Étape 4: Développer la tâche BT custom (Patrol)

Créez une tâche BT pour générer points patrouille aléatoires : query random loc sur NavMesh, set Blackboard key 'TargetLocation'. Hérite UBTTaskNode pour perf (vs BlueprintBase). Créez classes via Editor (Behavior Tree Task).

Dans BT éditeur : root > Sequence > PatrolTask > MoveTo (Blackboard) > Wait (2s). Analogie : tâche comme une fonction JS async dans un arbre de promesses.

BTTaskPatrol.h

Source/MyProject/BTTaskPatrol.h
#pragma once

#include "CoreMinimal.h"
#include "BehaviorTree/BTTaskNode.h"
#include "NavigationSystem.h"
#include "BTTaskPatrol.generated.h"

UCLASS()
class MYPROJECT_API UBTTaskPatrol : public UBTTaskNode
{
    GENERATED_BODY()

public:
    UBTTaskPatrol();

    virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;

    UPROPERTY(EditAnywhere, Category = "Blackboard")
    struct FBlackboardKeySelector TargetLocationKey;

    UPROPERTY(EditAnywhere, Category = "Patrol")
    float PatrolRadius = 1000.f;

    UPROPERTY(EditAnywhere, Category = "Patrol")
    int32 MaxPatrolAttempts = 10;
};

Déclare ExecuteTask() : cœur logique. BlackboardKeySelector pour éditeur (drag key 'TargetLocation'). Params radius/attempts tweakables. UBTTaskNode lightweight vs Blueprint (20% faster runtime).

BTTaskPatrol.cpp

Source/MyProject/BTTaskPatrol.cpp
#include "BTTaskPatrol.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "BehaviorTree/BehaviorTreeComponent.h"
#include "AIController.h"
#include "Kismet/GameplayStatics.h"

UBTTaskPatrol::UBTTaskPatrol()
{
    NodeName = "Patrol";
}

EBTNodeResult::Type UBTTaskPatrol::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
    Super::ExecuteTask(OwnerComp, NodeMemory);

    if (AAIController* AIController = OwnerComp.GetAIOwner())
    {
        if (APawn* AIPawn = AIController->GetPawn())
        {
            UWorld* World = AIPawn->GetWorld();
            if (UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World))
            {
                FNavLocation RandomLoc;
                int Attempts = 0;
                while (Attempts < MaxPatrolAttempts)
                {
                    FVector RandomPoint = AIPawn->GetActorLocation() + FVector(FMath::RandRange(-PatrolRadius, PatrolRadius),
                                                                              FMath::RandRange(-PatrolRadius, PatrolRadius), 0);
                    if (NavSys->GetRandomReachablePointInRadius(RandomPoint, PatrolRadius, RandomLoc))
                    {
                        OwnerComp.GetBlackboardComponent()->SetValueAsVector(TargetLocationKey.SelectedKeyName, RandomLoc.Location);
                        return EBTNodeResult::Succeeded;
                    }
                    Attempts++;
                }
            }
        }
    }
    return EBTNodeResult::Failed;
}

Génère random reachable point via NavSys (évite stucks). Set Blackboard vector key pour MoveTo successor. Loop attempts anti-loop infini. Retourne Succeeded pour BT flow. Testé UE5.4 : robuste multi-thread.

Étape 5: Compiler, créer assets et tester

Compilez (Ctrl+Shift+B VS ou Editor > Compile). Créez assets :

  1. Blackboard (Content > AI > New Blackboard) : ajoutez VectorKey 'TargetLocation'.
  2. Behavior Tree (New BT) : assignez Blackboard ; root=Selector > PatrolTask (drag votre tâche, set key/radius) > MoveTo (Blackboard TargetLocation) > Wait(2s).
  3. AI BP : enfant de MyAIPawn, set AIControllerClass=MyAIController, BehaviorTree= votre BT.
  4. Level : placez NavMeshBoundsVolume, spawn AI BP.
Play : IA patrouille ! Debug : PrintString en tâche.

Bonnes pratiques

  • Perf : Utilisez UBTTaskNode C++ pour >50 NPCs ; Blueprints ok pour proto.
  • Modularité : Séparez tasks en fichiers (Patrol, Chase, Flee) ; réutilisez en EQS.
  • Debug : Toujours exposez Blackboard keys en BlueprintReadOnly ; use 'View > Behavior Tree' en PIE.
  • Scalable : Ajoutez PerceptionComponent (AIPerception) pour stimuli (son, vue) trigger BT branches.
  • Threading : Évitez GWorld heavy ops en Tick ; batch en ExecuteTask.

Erreurs courantes à éviter

  • No NavMesh : IA stuck – toujours bake NavMesh (Build > Build Paths) ; vérifiez agent radius capsule.
  • Blackboard nil : Oubli GetBlackboardComponent() -> crash ; check if(BlackboardComponent) avant SetValue.
  • Hot-reload fail : Modifs UPROPERTY sans recompil full (fermez Editor, delete Binaries, regen VS).
  • BT non-run : Oubli BehaviorTreeComponent->StartTree() en OnPossess ; ou BT non-assigné en éditeur.

Pour aller plus loin

Comment implémenter une IA avancée en C++ sur Unreal Engine 2026 | Learni