# AI_BaseAgentController — AI Controller ## Blueprint Spec — UE 5.5–5.7 --- ### Parent Class `AIController` ### Dependencies - [`BP_EnemyBase`](58_BP_EnemyBase.md) — Pawn - [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) — Sensing - [`BB_AgentBoard`](BB_AgentBoard.md) — Decision-making - [`BPC_AIStateMachine`](61_BPC_AIStateMachine.md) — High-level states - [`BPC_AlertSystem`](60_BPC_AlertSystem.md) — Threat awareness - [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) — Self health - [`I_Damageable`](../01-core/03_I_InterfaceLibrary.md) — Damage reception - `DA_AIProfile` — AI configuration ### Purpose Central brain for enemy AI characters. Manages perception, decision-making via Behavior Trees, high-level state machine transitions, alert level propagation, and combat coordination. Possesses `BP_EnemyBase` pawns and controls their actions through Blackboard values and Behavior Tree execution. ### Variables | Name | Type | Description | |------|------|-------------| | `AIProfile` | DA_AIProfile | Configuration asset | | `PossessedEnemy` | BP_EnemyBase | Current pawn reference | | `BlackboardComp` | UBlackboardComponent | Blackboard instance | | `BehaviorTreeComp` | UBehaviorTreeComponent | Behavior tree runner | | `PerceptionComp` | BPC_AIPerceptionSystem | Sensory input | | `StateMachine` | BPC_AIStateMachine | State logic | | `AlertSystem` | BPC_AlertSystem | Threat level | | `bIsActive` | Bool | Enabled / disabled | | `AggressionRange` | Float | Engage distance | | `SuspicionRange` | Float | Investigate distance | | `CombatRange` | Float | Preferred combat distance | | `HomeLocation` | FVector | Spawn or patrol anchor | | `LastKnownPlayerLocation` | FVector | Blackboard updated | | `bHasLineOfSight` | Bool | LOS status | | `LostPlayerTimer` | Float | Seconds since last sighting | ### Blackboard Keys | Key Name | Type | Description | |----------|------|-------------| | `SelfActor` | Object | Self reference | | `TargetActor` | Object | Current threat | | `TargetLocation` | Vector | Last known threat location | | `HomeLocation` | Vector | Patrol anchor | | `AIState` | Enum (EAIState) | Current state | | `AlertLevel` | Float | 0.0–1.0 awareness | | `bHasLOS` | Bool | Line of sight | | `bIsInvestigating` | Bool | Searching location | | `PatrolIndex` | Int | Current waypoint | | `CombatStance` | Enum | Aggressive / Defensive | | `StimulusLocation` | Vector | Sound/sight trigger point | ### Enums | Enum | Values | Description | |------|--------|-------------| | `EAIState` | Idle, Patrol, Suspicious, Alerted, Combat, Searching, Fleeing, Disabled | AI behavior state | ### Functions | Name | Inputs | Outputs | Description | |------|--------|---------|-------------| | `OnPossess` | Pawn: APawn | — | Bind blackboard, run BT | | `OnUnPossess` | — | — | Cleanup | | `InitializeAI` | Profile: DA_AIProfile | — | Set up components | | `SetAIState` | NewState: EAIState | — | State machine transition | | `UpdatePerception` | Stimulus: FAIStimulus | — | Process sensory input | | `SetTarget` | Target: AActor | — | Update blackboard target | | `ClearTarget` | — | — | Target lost | | `StartInvestigation` | Location: FVector | — | Move to investigate | | `RequestReinforcements` | — | — | Alert nearby allies | | `GetCombatReadiness` | — | Float | Based on health, ammo, alert | | `OnTakeDamage` | DamageResult: FDamageResult | — | React to being hit | | `CanSeeTarget` | — | Bool | LOS check | | `LostSightOfTarget` | — | — | Start search timer | | `ReturnToPatrol` | — | — | Reset to patrol state | | `GetHomeLocation` | — | FVector | Patrol anchor | | `SetFocalPoint` | Location: FVector | — | Aim facing direction | ### Event Dispatchers | Name | Parameters | Fired When | |------|-----------|-----------| | `OnAIStateChanged` | OldState: EAIState, NewState: EAIState | State transition | | `OnTargetAcquired` | Target: AActor | New threat detected | | `OnTargetLost` | — | Target out of range/perception | | `OnAlertRaised` | AlertLevel: Float | Alert threshold crossed | | `OnReinforcementRequested` | Location: FVector | Call for backup | ### Blueprint Flow ``` [OnPossess] └─► PossessedEnemy = Cast(Pawn) └─► AIProfile = PossessedEnemy.AIProfile (or set from spawner) └─► InitializeAI(AIProfile) └─► RunBehaviorTree(AIProfile.BehaviorTree) └─► SetHomeLocation(Pawn.GetActorLocation) [InitializeAI] └─► Create BPC_AIPerceptionSystem if not exists └─► Create BPC_AIStateMachine └─► Create BPC_AlertSystem └─► PerceptionComp.Initialize(AIProfile.PerceptionConfig) └─► StateMachine.Initialize(EAIState.Patrol) └─► AlertSystem.Initialize() └─► Set AIState = Patrol in Blackboard [UpdatePerception — called by PerceptionComp dispatcher] └─► If Stimulus.Type == Sight && Stimulus.bSuccess: LastKnownPlayerLocation = Stimulus.StimulusLocation AlertSystem.RaiseAlert(SightAlertValue) If AlertSystem.AlertLevel >= AIProfile.CombatThreshold: SetTarget(Stimulus.Instigator) SetAIState(Combat) Else: SetAIState(Suspicious) └─► If Stimulus.Type == Hearing && Stimulus.bSuccess: AlertSystem.RaiseAlert(HearingAlertValue) StartInvestigation(Stimulus.StimulusLocation) SetAIState(Searching) └─► If Stimulus.Type == Damage: SetTarget(Stimulus.Instigator) AlertSystem.RaiseAlert(DamageAlertValue) SetAIState(Combat) RequestReinforcements() [OnTakeDamage] └─► AlertSystem.RaiseAlert(MaxAlertValue) └─► SetTarget(DamageResult.Instigator) └─► SetAIState(Combat) ``` ### Communications With | Target | Method | Why | |--------|--------|-----| | [`BP_EnemyBase`](58_BP_EnemyBase.md) | Possessed pawn | Movement, animation, abilities | | [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) | Get Component | Sensory stimulus processing | | [`BB_AgentBoard`](BB_AgentBoard.md) | Get Component | Blackboard updates | | [`BPC_AIStateMachine`](61_BPC_AIStateMachine.md) | Get Component | State transitions | | [`BPC_AlertSystem`](60_BPC_AlertSystem.md) | Get Component | Threat level queries | | [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) | Get on Pawn | Health events | | Nearby AI Controllers | Direct / Event | Reinforcement calls | ### Reuse Notes - Designed as parent for all enemy types (humanoid, creature, drone) - Perception, alerts, and state machine are swappable components - Behavior tree is data-driven via AIProfile - All decision values (ranges, thresholds, speeds) come from data asset - Renamed from `BPC_AIControllerBase` to `AI_BaseAgentController` per Master naming convention. - Cross-references updated: `BPC_PerceptionComponent` → `BPC_AIPerceptionSystem`, `BPC_BehaviorTreeManager` → `BB_AgentBoard`, `BPC_HealthComponent` → `BPC_HealthSystem`. --- ## Manual Implementation Guide ### Class Setup 1. Create Blueprint Class: Parent = `AIController`, Name = `AI_BaseAgentController` 2. Save to: `Content/Framework/AI/` 3. In Class Defaults: set `Auto Possess AI` = `Placed in World or Spawned` ### Variables (Add to Class Defaults) | Variable | Type | Default | Instance Editable | |----------|------|---------|-------------------| | `AIProfile` | `DA_AIProfile` | None | ✓ | | `PossessedEnemy` | `BP_EnemyBase` (Object Ref) | None | | | `BlackboardComp` | `Blackboard Component` (Object Ref) | None | | | `BehaviorTreeComp` | `Behavior Tree Component` (Object Ref) | None | | | `PerceptionComp` | `BPC_AIPerceptionSystem` (Object Ref) | None | | | `StateMachine` | `BPC_AIStateMachine` (Object Ref) | None | | | `AlertSystem` | `BPC_AlertSystem` (Object Ref) | None | | | `bIsActive` | `Boolean` | `true` | ✓ | | `AggressionRange` | `Float` | `1500.0` | ✓ | | `SuspicionRange` | `Float` | `3000.0` | ✓ | | `CombatRange` | `Float` | `800.0` | ✓ | | `HomeLocation` | `Vector` | `(0,0,0)` | | | `LastKnownPlayerLocation` | `Vector` | `(0,0,0)` | | | `LastKnownPlayerLocation` | `Vector` | `(0,0,0)` | | | `bHasLineOfSight` | `Boolean` | `false` | | | `LostPlayerTimer` | `Float` | `0.0` | | ### Function Implementations #### `Event OnPossess(Pawn: Pawn)` ``` [Event OnPossess] Step 1: Call Parent OnPossess Step 2: Cast Pawn to BP_EnemyBase → Set PossessedEnemy Step 3: Get AIProfile from PossessedEnemy (or a spawner data) Step 4: Get Blackboard Component → Set BlackboardComp Step 5: Get Behavior Tree Component → Set BehaviorTreeComp Step 6: Get Component by Class (BPC_AIPerceptionSystem) → Set PerceptionComp └─ If not found on Pawn: Create and attach dynamically Step 7: Get Component by Class (BPC_AIStateMachine) → Set StateMachine Step 8: Get Component by Class (BPC_AlertSystem) → Set AlertSystem Step 9: Set HomeLocation = Pawn.GetActorLocation() Step 10: Set Blackboard value "SelfActor" to Self Step 11: Set Blackboard value "HomeLocation" to HomeLocation Step 12: Set Blackboard value "AIState" to EAIState::Patrol Step 13: Run Behavior Tree (AIProfile.BehaviorTree) Step 14: PerceptionComp.Initialize(AIProfile.PerceptionConfig) Step 15: StateMachine.Initialize(EAIState::Patrol) Step 16: AlertSystem.Initialize() ``` **Nodes:** `Cast To BP_EnemyBase`, `Get Blackboard`, `Set Value as Object/Vector/Enum`, `Run Behavior Tree`, `Create Component (if needed)`, `Get Actor Location` #### `Event OnUnPossess(Pawn: Pawn)` ``` [Event OnUnPossess] Step 1: Stop Behavior Tree Step 2: Clear Blackboard values (TargetActor = None, AIState = Disabled) Step 3: PerceptionComp.Deinitialize() Step 4: Call Parent OnUnPossess ``` #### `UpdatePerception(Stimulus: AIStimulus)` → `void` *This is called by BPC_AIPerceptionSystem dispatcher when any sense triggers.* ``` [Function: UpdatePerception] Step 1: Branch on Stimulus.WasSuccessfullySensed() False → Return (ignore failed perception) Step 2: Switch on Stimulus.Type: Case Sight: Step 2a: Set LastKnownPlayerLocation = Stimulus.StimulusLocation Step 2b: AlertSystem.RaiseAlert(SightAlertValue from AIProfile) Step 2c: Branch on AlertSystem.AlertLevel >= AIProfile.CombatThreshold: True: - SetTarget(Stimulus.StimulusLocation → find closest Actor at that location) - SetAIState(EAIState::Combat) - Set Blackboard "bHasLOS" = true False: - SetAIState(EAIState::Suspicious) - Set Blackboard "StimulusLocation" = Stimulus.StimulusLocation - Set Blackboard "bIsInvestigating" = true Case Hearing: Step 3a: AlertSystem.RaiseAlert(HearingAlertValue from AIProfile) Step 3b: StartInvestigation(Stimulus.StimulusLocation) Step 3c: SetAIState(EAIState::Searching) Step 3d: Set Blackboard "StimulusLocation" = Stimulus.StimulusLocation Case Damage: Step 4a: SetTarget(Stimulus.StimulusLocation → find instigator) Step 4b: AlertSystem.RaiseAlert(MaxAlertValue) Step 4c: SetAIState(EAIState::Combat) Step 4d: Call RequestReinforcements() if team-aware Step 5: Fire OnTargetAcquired(Stimulus.StimulusLocation nearest Actor) if target found ``` **Nodes:** `Switch on EAIPerceptionSense`, `RaiseAlert`, `Set Blackboard Value`, `Branch`, `Find Nearest Actor` or `Get Actor at Location` #### `SetAIState(NewState: EAIState)` → `void` ``` [Function: SetAIState] Step 1: Get old state from Blackboard "AIState" Step 2: If NewState == OldState → Return Step 3: Set Blackboard "AIState" = NewState Step 4: Call StateMachine.SetState(NewState) Step 5: Fire OnAIStateChanged(OldState, NewState) Step 6: If NewState == Combat: Call RequestReinforcements() ``` #### `SetTarget(Target: Actor)` → `void` ``` [Function: SetTarget] Step 1: Set Blackboard "TargetActor" = Target Step 2: If Target is valid: - Set Blackboard "TargetLocation" = Target.GetActorLocation() - SetFocus(Target) - Fire OnTargetAcquired(Target) Else: - ClearFocus() - Fire OnTargetLost() ``` #### `StartInvestigation(Location: Vector)` → `void` ``` [Function: StartInvestigation] Step 1: Set Blackboard "bIsInvestigating" = true Step 2: Set Blackboard "TargetLocation" = Location Step 3: Call AI MoveTo(Location) Step 4: On Move Completed: - If target not found at location: - Set Blackboard "bIsInvestigating" = false - ReturnToPatrol() ``` #### `RequestReinforcements()` → `void` ``` [Function: RequestReinforcements] Step 1: Get all AI_BaseAgentController in radius (ReinforcementRange from AIProfile) Step 2: ForEach nearby AI: - If AI.StateMachine.CurrentState == Patrol OR Suspicious: - Call AI.SetTarget(Self.GetBlackboard("TargetActor")) - Call AI.SetAIState(Alerted) Step 3: Fire OnReinforcementRequested(Self.GetActorLocation()) ``` **Nodes:** `Get All Actors of Class (AI_BaseAgentController)`, `ForEachLoop`, `Get Distance To`, `Branch`, `Call SetTarget/SetAIState on other AI` #### `LostSightOfTarget()` → `void` ``` [Function: LostSightOfTarget] Step 1: Set Blackboard "bHasLOS" = false Step 2: Start LostPlayerTimer (timer loops 0.1s) └─ Each tick: LostPlayerTimer += 0.1 Step 3: Branch on LostPlayerTimer >= AIProfile.LostTargetTimeout: True: - ClearTarget() - SetAIState(EAIState::Searching) - StartInvestigation(LastKnownPlayerLocation) - Clear LostPlayerTimer False: continue waiting ``` #### `CanSeeTarget()` → `Boolean` ``` [Function: CanSeeTarget] (Pure) Step 1: TargetActor = Get Blackboard "TargetActor" Step 2: If TargetActor NOT valid → Return false Step 3: Line Trace by Channel from Self location to TargetActor location - Trace channel: Visibility - Ignore Self Step 4: Return (Hit Actor == TargetActor) — true if nothing blocks line of sight ``` ### Blueprint Build Checklist - [ ] Create AI_BaseAgentController (Parent: AIController) - [ ] Add all variables from the table above - [ ] Create Blackboard asset: BB_AgentBoard with all key names - [ ] Configure Behavior Tree asset (or use placeholder) - [ ] Implement OnPossess: cache components, init perception/state/alert, run BT - [ ] Implement UpdatePerception: route Sight/Hearing/Damage to correct states - [ ] Implement SetAIState with blackboard update and dispatcher - [ ] Implement SetTarget/ClearTarget with blackboard sync - [ ] Implement StartInvestigation with AI MoveTo - [ ] Implement RequestReinforcements (nearby AI coordination) - [ ] Implement LostSightOfTarget with timeout → search - [ ] Implement CanSeeTarget with line trace - [ ] Create BP_EnemyBase child classes that use this controller