Fix PS_AI_Behavior compilation errors for UE 5.5
- Remove NavigationSystem from .uplugin Plugins (it's a module, not a plugin) - Fix UInterface naming: IPS_AI_Behavior -> IPS_AI_Behavior_Interface (UHT requirement) - Fix TWeakObjectPtr<AActor> TArray not Blueprint-compatible (remove BlueprintReadOnly) - Fix UseBlackboard TObjectPtr ref: use raw pointer intermediary - Fix FEdMode::HandleClick signature: FInputClick -> FViewportClick (UE 5.5) - Fix SplineNetwork: use OnWorldBeginPlay(UWorld&) override instead of delegate - Fix PerceptionComponent: remove const from methods calling non-const GetActorsPerception - Fix EQS SetScore: use 5-arg float overload (Score, FilterMin, FilterMax) - Fix BTTask_FindCover: add missing Definitions.h include, fix const World - Fix BTTask_FollowSpline: replace AddWeakLambda with polling in TickTask - Fix CoverPoint: initialize Color before switch, add default case - Export LogPS_AI_Behavior with PS_AI_BEHAVIOR_API for cross-module visibility - Remove unused variables (WorldOrigin, WorldDirection) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5d5b85380a
commit
909583a1bb
511
Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/PLAN.md
Normal file
511
Unreal/PS_AI_Agent/Plugins/PS_AI_Behavior/PLAN.md
Normal file
@ -0,0 +1,511 @@
|
|||||||
|
# PS_AI_Behavior — Plan d'implémentation V1
|
||||||
|
|
||||||
|
## Vue d'ensemble
|
||||||
|
Plugin UE5.5 pour gérer les comportements de NPCs (civils et ennemis) via Behavior Trees, EQS et un système de personnalité à scores. Navigation sur NavMesh, détection d'ennemis, combat basique, fuite, couverture.
|
||||||
|
|
||||||
|
Dépendance optionnelle vers PS_AI_ConvAgent (détectée à l'exécution, pas de link-time dependency).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Structure du plugin
|
||||||
|
|
||||||
|
```
|
||||||
|
Plugins/PS_AI_Behavior/
|
||||||
|
├── PS_AI_Behavior.uplugin
|
||||||
|
├── Config/
|
||||||
|
│ └── DefaultPS_AI_Behavior.ini
|
||||||
|
├── Content/
|
||||||
|
│ ├── BehaviorTrees/
|
||||||
|
│ │ ├── BT_Civilian.uasset (BT civils)
|
||||||
|
│ │ └── BT_Enemy.uasset (BT ennemis)
|
||||||
|
│ ├── EQS/
|
||||||
|
│ │ ├── EQS_FindCover.uasset (trouver couverture)
|
||||||
|
│ │ ├── EQS_FindFleePoint.uasset (point de fuite)
|
||||||
|
│ │ └── EQS_FindPatrolPoint.uasset (point de patrouille)
|
||||||
|
│ └── Data/
|
||||||
|
│ ├── DA_Trait_Coward.uasset (exemple Data Asset)
|
||||||
|
│ └── DA_Trait_Aggressive.uasset
|
||||||
|
└── Source/
|
||||||
|
├── PS_AI_Behavior/ (module Runtime)
|
||||||
|
│ ├── PS_AI_Behavior.Build.cs
|
||||||
|
│ ├── Public/
|
||||||
|
│ │ ├── PS_AI_Behavior.h (module def)
|
||||||
|
│ │ ├── PS_AI_Behavior_Definitions.h (enums, structs, log category)
|
||||||
|
│ │ ├── PS_AI_Behavior_Settings.h (Project Settings)
|
||||||
|
│ │ │
|
||||||
|
│ │ ├── PS_AI_Behavior_AIController.h (AIController principal)
|
||||||
|
│ │ ├── PS_AI_Behavior_PersonalityComponent.h (traits de personnalité)
|
||||||
|
│ │ ├── PS_AI_Behavior_PerceptionComponent.h (wrapper AIPerception)
|
||||||
|
│ │ ├── PS_AI_Behavior_CombatComponent.h (état combat)
|
||||||
|
│ │ ├── PS_AI_Behavior_PersonalityProfile.h (Data Asset profil)
|
||||||
|
│ │ │
|
||||||
|
│ │ ├── BT/ (BT Tasks, Services, Decorators)
|
||||||
|
│ │ │ ├── PS_AI_Behavior_BTTask_FindCover.h
|
||||||
|
│ │ │ ├── PS_AI_Behavior_BTTask_FleeFrom.h
|
||||||
|
│ │ │ ├── PS_AI_Behavior_BTTask_Attack.h
|
||||||
|
│ │ │ ├── PS_AI_Behavior_BTTask_Patrol.h
|
||||||
|
│ │ │ ├── PS_AI_Behavior_BTService_UpdateThreat.h
|
||||||
|
│ │ │ ├── PS_AI_Behavior_BTService_EvaluateReaction.h
|
||||||
|
│ │ │ └── PS_AI_Behavior_BTDecorator_CheckTrait.h
|
||||||
|
│ │ │
|
||||||
|
│ │ └── EQS/
|
||||||
|
│ │ ├── PS_AI_Behavior_EQSContext_Threat.h
|
||||||
|
│ │ └── PS_AI_Behavior_EQSTest_CoverQuality.h
|
||||||
|
│ │
|
||||||
|
│ └── Private/
|
||||||
|
│ ├── PS_AI_Behavior.cpp
|
||||||
|
│ ├── PS_AI_Behavior_Settings.cpp
|
||||||
|
│ ├── PS_AI_Behavior_AIController.cpp
|
||||||
|
│ ├── PS_AI_Behavior_PersonalityComponent.cpp
|
||||||
|
│ ├── PS_AI_Behavior_PerceptionComponent.cpp
|
||||||
|
│ ├── PS_AI_Behavior_CombatComponent.cpp
|
||||||
|
│ ├── PS_AI_Behavior_PersonalityProfile.cpp
|
||||||
|
│ ├── BT/
|
||||||
|
│ │ ├── PS_AI_Behavior_BTTask_FindCover.cpp
|
||||||
|
│ │ ├── PS_AI_Behavior_BTTask_FleeFrom.cpp
|
||||||
|
│ │ ├── PS_AI_Behavior_BTTask_Attack.cpp
|
||||||
|
│ │ ├── PS_AI_Behavior_BTTask_Patrol.cpp
|
||||||
|
│ │ ├── PS_AI_Behavior_BTService_UpdateThreat.cpp
|
||||||
|
│ │ ├── PS_AI_Behavior_BTService_EvaluateReaction.cpp
|
||||||
|
│ │ └── PS_AI_Behavior_BTDecorator_CheckTrait.cpp
|
||||||
|
│ └── EQS/
|
||||||
|
│ ├── PS_AI_Behavior_EQSContext_Threat.cpp
|
||||||
|
│ └── PS_AI_Behavior_EQSTest_CoverQuality.cpp
|
||||||
|
│
|
||||||
|
└── PS_AI_BehaviorEditor/ (module Editor — futur, pas V1)
|
||||||
|
├── PS_AI_BehaviorEditor.Build.cs
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Classes principales
|
||||||
|
|
||||||
|
### 2.1 Definitions (`PS_AI_Behavior_Definitions.h`)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Log category
|
||||||
|
DECLARE_LOG_CATEGORY_EXTERN(LogPS_AI_Behavior, Log, All);
|
||||||
|
|
||||||
|
// Type de NPC
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class EPS_AI_Behavior_NPCType : uint8
|
||||||
|
{
|
||||||
|
Civilian,
|
||||||
|
Enemy,
|
||||||
|
Neutral
|
||||||
|
};
|
||||||
|
|
||||||
|
// État comportemental haut-niveau
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class EPS_AI_Behavior_State : uint8
|
||||||
|
{
|
||||||
|
Idle,
|
||||||
|
Patrol,
|
||||||
|
Alerted,
|
||||||
|
Combat,
|
||||||
|
Fleeing,
|
||||||
|
TakingCover,
|
||||||
|
Dead
|
||||||
|
};
|
||||||
|
|
||||||
|
// Axes de personnalité (scores 0.0 → 1.0)
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class EPS_AI_Behavior_TraitAxis : uint8
|
||||||
|
{
|
||||||
|
Courage, // 0 = lâche, 1 = téméraire
|
||||||
|
Aggressivity, // 0 = pacifique, 1 = violent
|
||||||
|
Loyalty, // 0 = égoïste, 1 = dévoué
|
||||||
|
Caution, // 0 = imprudent, 1 = prudent
|
||||||
|
Discipline // 0 = indiscipliné, 1 = discipliné
|
||||||
|
};
|
||||||
|
|
||||||
|
// Struct pour un trait + valeur
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FPS_AI_Behavior_TraitScore
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(ClampMin=0.0, ClampMax=1.0))
|
||||||
|
float Value = 0.5f;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 PersonalityProfile — Data Asset (`PS_AI_Behavior_PersonalityProfile.h`)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
UCLASS(BlueprintType)
|
||||||
|
class PS_AI_BEHAVIOR_API UPS_AI_Behavior_PersonalityProfile : public UPrimaryDataAsset
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
public:
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Personality")
|
||||||
|
FText ProfileName;
|
||||||
|
|
||||||
|
// Scores par axe : TMap<EPS_AI_Behavior_TraitAxis, float>
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Personality")
|
||||||
|
TMap<EPS_AI_Behavior_TraitAxis, float> TraitScores;
|
||||||
|
|
||||||
|
// Seuils de réaction
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Reaction Thresholds",
|
||||||
|
meta=(ClampMin=0.0, ClampMax=1.0))
|
||||||
|
float FleeThreshold = 0.6f; // Threat level au-delà duquel on fuit (modulé par Courage)
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Reaction Thresholds",
|
||||||
|
meta=(ClampMin=0.0, ClampMax=1.0))
|
||||||
|
float AttackThreshold = 0.4f; // Threat level au-delà duquel on attaque (modulé par Aggressivity)
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Behavior")
|
||||||
|
EPS_AI_Behavior_NPCType DefaultNPCType = EPS_AI_Behavior_NPCType::Civilian;
|
||||||
|
|
||||||
|
// Behavior Tree à utiliser (peut être overridé par l'AIController)
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Behavior")
|
||||||
|
TSoftObjectPtr<UBehaviorTree> DefaultBehaviorTree;
|
||||||
|
|
||||||
|
// Helper
|
||||||
|
float GetTrait(EPS_AI_Behavior_TraitAxis Axis) const;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 PersonalityComponent (`PS_AI_Behavior_PersonalityComponent.h`)
|
||||||
|
|
||||||
|
Attaché au Pawn. Fournit l'accès runtime aux traits, modifie les seuils dynamiquement.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
UCLASS(ClassGroup="PS AI Behavior", meta=(BlueprintSpawnableComponent))
|
||||||
|
class PS_AI_BEHAVIOR_API UPS_AI_Behavior_PersonalityComponent : public UActorComponent
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
public:
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Personality")
|
||||||
|
UPS_AI_Behavior_PersonalityProfile* Profile;
|
||||||
|
|
||||||
|
// Runtime overrides (initialisés depuis Profile au BeginPlay)
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="Personality|Runtime")
|
||||||
|
TMap<EPS_AI_Behavior_TraitAxis, float> RuntimeTraits;
|
||||||
|
|
||||||
|
// Threat level perçu (mis à jour par BTService_UpdateThreat)
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="Personality|Runtime")
|
||||||
|
float PerceivedThreatLevel = 0.0f;
|
||||||
|
|
||||||
|
// Décision finale basée sur traits + threat
|
||||||
|
UFUNCTION(BlueprintCallable, Category="PS AI Behavior|Personality")
|
||||||
|
EPS_AI_Behavior_State EvaluateReaction() const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="PS AI Behavior|Personality")
|
||||||
|
float GetTrait(EPS_AI_Behavior_TraitAxis Axis) const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="PS AI Behavior|Personality")
|
||||||
|
void ModifyTrait(EPS_AI_Behavior_TraitAxis Axis, float Delta);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Logique `EvaluateReaction()`** :
|
||||||
|
```
|
||||||
|
EffectiveCourage = RuntimeTraits[Courage] * (1 - PerceivedThreatLevel * 0.5)
|
||||||
|
if PerceivedThreatLevel > FleeThreshold * (1 + EffectiveCourage) → Fleeing
|
||||||
|
if PerceivedThreatLevel > AttackThreshold * (1 - Aggressivity) → Combat
|
||||||
|
if PerceivedThreatLevel > 0.1 → Alerted
|
||||||
|
else → Idle/Patrol
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4 AIController (`PS_AI_Behavior_AIController.h`)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
UCLASS()
|
||||||
|
class PS_AI_BEHAVIOR_API APS_AI_Behavior_AIController : public AAIController
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
public:
|
||||||
|
APS_AI_Behavior_AIController();
|
||||||
|
|
||||||
|
// Blackboard keys (nom constants)
|
||||||
|
static const FName BB_State; // EPS_AI_Behavior_State
|
||||||
|
static const FName BB_ThreatActor; // UObject*
|
||||||
|
static const FName BB_ThreatLocation; // FVector
|
||||||
|
static const FName BB_ThreatLevel; // float
|
||||||
|
static const FName BB_CoverLocation; // FVector
|
||||||
|
static const FName BB_PatrolIndex; // int32
|
||||||
|
static const FName BB_HomeLocation; // FVector
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Behavior")
|
||||||
|
UBehaviorTree* BehaviorTreeAsset;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Behavior")
|
||||||
|
UBlackboardData* BlackboardAsset;
|
||||||
|
|
||||||
|
// Patrol waypoints (set par level designer ou spawner)
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Patrol")
|
||||||
|
TArray<FVector> PatrolPoints;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void OnPossess(APawn* InPawn) override;
|
||||||
|
virtual void OnUnPossess() override;
|
||||||
|
|
||||||
|
// Auto-détection optionnelle de PS_AI_ConvAgent
|
||||||
|
void TryBindConversationAgent();
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**`OnPossess`** :
|
||||||
|
1. Trouve `PersonalityComponent` sur le Pawn
|
||||||
|
2. Crée/initialise le Blackboard
|
||||||
|
3. Lit `DefaultBehaviorTree` du ProfileData (ou utilise `BehaviorTreeAsset`)
|
||||||
|
4. Lance `RunBehaviorTree()`
|
||||||
|
5. Appelle `TryBindConversationAgent()`
|
||||||
|
|
||||||
|
**`TryBindConversationAgent()`** :
|
||||||
|
- Via `FindComponentByClass` (pas de include direct, utilise `FindObject` ou interface)
|
||||||
|
- Si trouvé : bind OnAgentActionRequested pour injecter des actions dans le BT
|
||||||
|
|
||||||
|
### 2.5 PerceptionComponent (`PS_AI_Behavior_PerceptionComponent.h`)
|
||||||
|
|
||||||
|
Wrapper configuré autour de `UAIPerceptionComponent` :
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
UCLASS(ClassGroup="PS AI Behavior", meta=(BlueprintSpawnableComponent))
|
||||||
|
class PS_AI_BEHAVIOR_API UPS_AI_Behavior_PerceptionComponent : public UAIPerceptionComponent
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
public:
|
||||||
|
UPS_AI_Behavior_PerceptionComponent();
|
||||||
|
|
||||||
|
// Pré-configure : Sight (60m, 90° FOV) + Hearing (30m) + Damage
|
||||||
|
virtual void BeginPlay() override;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="PS AI Behavior|Perception")
|
||||||
|
AActor* GetHighestThreat() const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="PS AI Behavior|Perception")
|
||||||
|
float CalculateThreatLevel() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
UFUNCTION()
|
||||||
|
void OnPerceptionUpdated(const TArray<AActor*>& UpdatedActors);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.6 CombatComponent (`PS_AI_Behavior_CombatComponent.h`)
|
||||||
|
|
||||||
|
Gère l'état combat, les distances, le cooldown d'attaque :
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
UCLASS(ClassGroup="PS AI Behavior", meta=(BlueprintSpawnableComponent))
|
||||||
|
class PS_AI_BEHAVIOR_API UPS_AI_Behavior_CombatComponent : public UActorComponent
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
public:
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Combat")
|
||||||
|
float AttackRange = 200.0f;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Combat")
|
||||||
|
float AttackCooldown = 1.5f;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Combat")
|
||||||
|
float AttackDamage = 20.0f;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="PS AI Behavior|Combat")
|
||||||
|
bool CanAttack() const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="PS AI Behavior|Combat")
|
||||||
|
void ExecuteAttack(AActor* Target);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="PS AI Behavior|Combat")
|
||||||
|
bool IsInAttackRange(AActor* Target) const;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Behavior Tree — Nodes
|
||||||
|
|
||||||
|
### 3.1 Services (tournent en continu)
|
||||||
|
|
||||||
|
**BTService_UpdateThreat** :
|
||||||
|
- Lit `PerceptionComponent::CalculateThreatLevel()`
|
||||||
|
- Écrit `BB_ThreatLevel`, `BB_ThreatActor`, `BB_ThreatLocation`
|
||||||
|
- Met à jour `PersonalityComponent::PerceivedThreatLevel`
|
||||||
|
|
||||||
|
**BTService_EvaluateReaction** :
|
||||||
|
- Appelle `PersonalityComponent::EvaluateReaction()`
|
||||||
|
- Écrit `BB_State` dans le Blackboard
|
||||||
|
- Le BT utilise des decorators pour brancher sur cet état
|
||||||
|
|
||||||
|
### 3.2 Tasks
|
||||||
|
|
||||||
|
**BTTask_Patrol** :
|
||||||
|
- Lit `BB_PatrolIndex`, navigue vers `PatrolPoints[idx]`
|
||||||
|
- Au succès, incrémente l'index (cyclique)
|
||||||
|
- Supporte pause aléatoire aux waypoints
|
||||||
|
|
||||||
|
**BTTask_FleeFrom** :
|
||||||
|
- Lit `BB_ThreatLocation`
|
||||||
|
- Utilise EQS `EQS_FindFleePoint` (direction opposée à la menace)
|
||||||
|
- `MoveTo()` vers le point trouvé
|
||||||
|
|
||||||
|
**BTTask_FindCover** :
|
||||||
|
- Lance EQS `EQS_FindCover` (scoring : distance menace, line-of-sight block, distance au NPC)
|
||||||
|
- Navigue vers le meilleur point
|
||||||
|
- Écrit `BB_CoverLocation`
|
||||||
|
|
||||||
|
**BTTask_Attack** :
|
||||||
|
- Vérifie `CombatComponent::CanAttack()`
|
||||||
|
- Si hors range : `MoveTo(Target)`
|
||||||
|
- Si in range : `CombatComponent::ExecuteAttack(Target)`
|
||||||
|
|
||||||
|
### 3.3 Decorators
|
||||||
|
|
||||||
|
**BTDecorator_CheckTrait** :
|
||||||
|
- Paramètres : `TraitAxis`, `ComparisonOp` (>, <, ==), `Threshold`
|
||||||
|
- Lit le trait depuis `PersonalityComponent`
|
||||||
|
- Exemple : "Exécuter seulement si Courage > 0.5"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. EQS
|
||||||
|
|
||||||
|
### EQS_FindCover
|
||||||
|
- **Generator** : Points sur grille autour du NPC (rayon 15m)
|
||||||
|
- **Tests** :
|
||||||
|
- Distance à la menace (préfère mi-distance, pas trop loin)
|
||||||
|
- Trace visibility (préfère les points non-visibles depuis la menace)
|
||||||
|
- Distance au NPC (préfère les points proches)
|
||||||
|
- `EQSTest_CoverQuality` (custom) : raycasts multiples pour évaluer la qualité de couverture
|
||||||
|
|
||||||
|
### EQS_FindFleePoint
|
||||||
|
- **Generator** : Points sur donut (rayon 10-25m)
|
||||||
|
- **Tests** :
|
||||||
|
- Dot product direction (opposé à la menace : score max)
|
||||||
|
- PathExistence (doit être atteignable sur NavMesh)
|
||||||
|
- Distance à la menace (préfère loin)
|
||||||
|
|
||||||
|
### EQS_FindPatrolPoint
|
||||||
|
- **Generator** : Points depuis la liste PatrolPoints de l'AIController
|
||||||
|
- **Tests** :
|
||||||
|
- Distance au NPC (préfère le plus proche non-visité)
|
||||||
|
|
||||||
|
### EQSContext_Threat
|
||||||
|
- Renvoie l'acteur/location de `BB_ThreatActor` / `BB_ThreatLocation`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Intégration optionnelle PS_AI_ConvAgent
|
||||||
|
|
||||||
|
**Mécanisme** : Pas de `#include` direct. L'AIController utilise `FindComponentByClass` avec le nom de classe via UObject reflection :
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void APS_AI_Behavior_AIController::TryBindConversationAgent()
|
||||||
|
{
|
||||||
|
// Soft reference — no link-time dependency
|
||||||
|
UActorComponent* ConvComp = GetPawn()->FindComponentByClass(
|
||||||
|
LoadClass<UActorComponent>(nullptr,
|
||||||
|
TEXT("/Script/PS_AI_ConvAgent.PS_AI_ConvAgent_ElevenLabsComponent")));
|
||||||
|
if (ConvComp)
|
||||||
|
{
|
||||||
|
// Bind to OnAgentActionRequested via dynamic delegate
|
||||||
|
// Actions from conversation can inject BT state changes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Cela permet :
|
||||||
|
- Un NPC conversationnel qui reçoit "Fuis !" via ElevenLabs → injecte State=Fleeing dans le BT
|
||||||
|
- Aucune dépendance de compilation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Build.cs — Dépendances
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// PS_AI_Behavior.Build.cs
|
||||||
|
PublicDependencyModuleNames.AddRange(new string[] {
|
||||||
|
"Core", "CoreUObject", "Engine",
|
||||||
|
"AIModule", // AAIController, BehaviorTree, Blackboard
|
||||||
|
"GameplayTasks", // UGameplayTask (requis par BT tasks)
|
||||||
|
"NavigationSystem", // NavMesh queries
|
||||||
|
});
|
||||||
|
|
||||||
|
PrivateDependencyModuleNames.AddRange(new string[] {
|
||||||
|
"Settings", // ISettingsModule
|
||||||
|
});
|
||||||
|
|
||||||
|
// PAS de dépendance vers PS_AI_ConvAgent
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Ordre d'implémentation (étapes)
|
||||||
|
|
||||||
|
### Étape 1 — Squelette plugin
|
||||||
|
- [ ] Créer la structure de fichiers du plugin
|
||||||
|
- [ ] `.uplugin`, `Build.cs`, module class
|
||||||
|
- [ ] `Definitions.h` (enums, structs, log category)
|
||||||
|
- [ ] `Settings.h/cpp` (settings vides pour l'instant)
|
||||||
|
- [ ] Ajouter au `.uproject`
|
||||||
|
- [ ] **Vérification** : compile sans erreur
|
||||||
|
|
||||||
|
### Étape 2 — Personality System
|
||||||
|
- [ ] `PersonalityProfile` (Data Asset)
|
||||||
|
- [ ] `PersonalityComponent` avec `EvaluateReaction()`
|
||||||
|
- [ ] **Vérification** : peut créer un Data Asset dans l'éditeur, lire les traits en BP
|
||||||
|
|
||||||
|
### Étape 3 — AIController + Perception
|
||||||
|
- [ ] `PS_AI_Behavior_AIController` avec Blackboard setup
|
||||||
|
- [ ] `PS_AI_Behavior_PerceptionComponent` (sight + hearing)
|
||||||
|
- [ ] `BlackboardData` asset par défaut
|
||||||
|
- [ ] **Vérification** : un NPC spawné détecte les acteurs proches
|
||||||
|
|
||||||
|
### Étape 4 — BT Services + Decorators
|
||||||
|
- [ ] `BTService_UpdateThreat`
|
||||||
|
- [ ] `BTService_EvaluateReaction`
|
||||||
|
- [ ] `BTDecorator_CheckTrait`
|
||||||
|
- [ ] **Vérification** : le Blackboard se met à jour en jeu
|
||||||
|
|
||||||
|
### Étape 5 — BT Tasks (Navigation)
|
||||||
|
- [ ] `BTTask_Patrol`
|
||||||
|
- [ ] `BTTask_FleeFrom`
|
||||||
|
- [ ] `BTTask_FindCover`
|
||||||
|
- [ ] **Vérification** : NPC patrouille et fuit
|
||||||
|
|
||||||
|
### Étape 6 — Combat
|
||||||
|
- [ ] `CombatComponent`
|
||||||
|
- [ ] `BTTask_Attack`
|
||||||
|
- [ ] **Vérification** : NPC ennemi attaque le joueur
|
||||||
|
|
||||||
|
### Étape 7 — EQS
|
||||||
|
- [ ] `EQSContext_Threat`
|
||||||
|
- [ ] `EQSTest_CoverQuality`
|
||||||
|
- [ ] Assets EQS dans Content/
|
||||||
|
- [ ] **Vérification** : NPC trouve des couvertures intelligemment
|
||||||
|
|
||||||
|
### Étape 8 — Intégration ConvAgent (optionnelle)
|
||||||
|
- [ ] `TryBindConversationAgent()` soft binding
|
||||||
|
- [ ] Test avec un NPC qui a les deux plugins
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Résumé des fichiers à créer
|
||||||
|
|
||||||
|
| # | Fichier | Rôle |
|
||||||
|
|---|---------|------|
|
||||||
|
| 1 | `PS_AI_Behavior.uplugin` | Plugin descriptor |
|
||||||
|
| 2 | `PS_AI_Behavior.Build.cs` | Module dependencies |
|
||||||
|
| 3 | `PS_AI_Behavior.h / .cpp` | Module class (register settings) |
|
||||||
|
| 4 | `PS_AI_Behavior_Definitions.h` | Enums, structs, log |
|
||||||
|
| 5 | `PS_AI_Behavior_Settings.h / .cpp` | Project settings |
|
||||||
|
| 6 | `PS_AI_Behavior_PersonalityProfile.h / .cpp` | Data Asset |
|
||||||
|
| 7 | `PS_AI_Behavior_PersonalityComponent.h / .cpp` | Personality runtime |
|
||||||
|
| 8 | `PS_AI_Behavior_AIController.h / .cpp` | AIController |
|
||||||
|
| 9 | `PS_AI_Behavior_PerceptionComponent.h / .cpp` | AI Perception |
|
||||||
|
| 10 | `PS_AI_Behavior_CombatComponent.h / .cpp` | Combat state |
|
||||||
|
| 11 | `BT/PS_AI_Behavior_BTTask_Patrol.h / .cpp` | Patrol task |
|
||||||
|
| 12 | `BT/PS_AI_Behavior_BTTask_FleeFrom.h / .cpp` | Flee task |
|
||||||
|
| 13 | `BT/PS_AI_Behavior_BTTask_FindCover.h / .cpp` | Cover task |
|
||||||
|
| 14 | `BT/PS_AI_Behavior_BTTask_Attack.h / .cpp` | Attack task |
|
||||||
|
| 15 | `BT/PS_AI_Behavior_BTService_UpdateThreat.h / .cpp` | Threat service |
|
||||||
|
| 16 | `BT/PS_AI_Behavior_BTService_EvaluateReaction.h / .cpp` | Reaction service |
|
||||||
|
| 17 | `BT/PS_AI_Behavior_BTDecorator_CheckTrait.h / .cpp` | Trait decorator |
|
||||||
|
| 18 | `EQS/PS_AI_Behavior_EQSContext_Threat.h / .cpp` | EQS context |
|
||||||
|
| 19 | `EQS/PS_AI_Behavior_EQSTest_CoverQuality.h / .cpp` | EQS test |
|
||||||
|
|
||||||
|
**Total : ~38 fichiers C++ (19 paires h/cpp) + 1 .uplugin + 1 .Build.cs**
|
||||||
@ -30,10 +30,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Plugins": [
|
"Plugins": []
|
||||||
{
|
|
||||||
"Name": "NavigationSystem",
|
|
||||||
"Enabled": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FindCover::ExecuteTask(
|
|||||||
UBlackboardComponent* BB = OwnerComp.GetBlackboardComponent();
|
UBlackboardComponent* BB = OwnerComp.GetBlackboardComponent();
|
||||||
if (!BB) return EBTNodeResult::Failed;
|
if (!BB) return EBTNodeResult::Failed;
|
||||||
|
|
||||||
const UWorld* World = GetWorld();
|
UWorld* World = GetWorld();
|
||||||
if (!World) return EBTNodeResult::Failed;
|
if (!World) return EBTNodeResult::Failed;
|
||||||
|
|
||||||
const FVector NpcLoc = AIC->GetPawn()->GetActorLocation();
|
const FVector NpcLoc = AIC->GetPawn()->GetActorLocation();
|
||||||
|
|||||||
@ -42,18 +42,11 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FollowSpline::ExecuteTask(
|
|||||||
Follower->ResumeFollowing();
|
Follower->ResumeFollowing();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen for end-of-spline
|
// Initialize memory — TickTask will poll bIsFollowing to detect end-of-spline
|
||||||
FFollowMemory* Memory = reinterpret_cast<FFollowMemory*>(NodeMemory);
|
FFollowMemory* Memory = reinterpret_cast<FFollowMemory*>(NodeMemory);
|
||||||
Memory->Elapsed = 0.0f;
|
Memory->Elapsed = 0.0f;
|
||||||
Memory->bEndReached = false;
|
Memory->bEndReached = false;
|
||||||
|
|
||||||
// Bind to end delegate
|
|
||||||
Follower->OnSplineEndReached.AddWeakLambda(this,
|
|
||||||
[Memory](APS_AI_Behavior_SplinePath* /*Spline*/)
|
|
||||||
{
|
|
||||||
Memory->bEndReached = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return EBTNodeResult::InProgress;
|
return EBTNodeResult::InProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,11 +55,17 @@ void UPS_AI_Behavior_BTTask_FollowSpline::TickTask(
|
|||||||
{
|
{
|
||||||
FFollowMemory* Memory = reinterpret_cast<FFollowMemory*>(NodeMemory);
|
FFollowMemory* Memory = reinterpret_cast<FFollowMemory*>(NodeMemory);
|
||||||
|
|
||||||
// Check if spline end was reached
|
// Check if spline end was reached (poll bIsFollowing — set to false by SplineFollowerComponent)
|
||||||
if (Memory->bEndReached)
|
AAIController* AICCheck = OwnerComp.GetAIOwner();
|
||||||
|
if (AICCheck && AICCheck->GetPawn())
|
||||||
{
|
{
|
||||||
FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
|
UPS_AI_Behavior_SplineFollowerComponent* FollowerCheck =
|
||||||
return;
|
AICCheck->GetPawn()->FindComponentByClass<UPS_AI_Behavior_SplineFollowerComponent>();
|
||||||
|
if (FollowerCheck && !FollowerCheck->bIsFollowing)
|
||||||
|
{
|
||||||
|
FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Time limit check
|
// Time limit check
|
||||||
@ -91,20 +90,13 @@ void UPS_AI_Behavior_BTTask_FollowSpline::TickTask(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify follower is still active
|
// Verify pawn is still valid
|
||||||
AAIController* AIC = OwnerComp.GetAIOwner();
|
AAIController* AIC = OwnerComp.GetAIOwner();
|
||||||
if (!AIC || !AIC->GetPawn())
|
if (!AIC || !AIC->GetPawn())
|
||||||
{
|
{
|
||||||
FinishLatentTask(OwnerComp, EBTNodeResult::Failed);
|
FinishLatentTask(OwnerComp, EBTNodeResult::Failed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UPS_AI_Behavior_SplineFollowerComponent* Follower =
|
|
||||||
AIC->GetPawn()->FindComponentByClass<UPS_AI_Behavior_SplineFollowerComponent>();
|
|
||||||
if (!Follower || !Follower->bIsFollowing)
|
|
||||||
{
|
|
||||||
FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EBTNodeResult::Type UPS_AI_Behavior_BTTask_FollowSpline::AbortTask(
|
EBTNodeResult::Type UPS_AI_Behavior_BTTask_FollowSpline::AbortTask(
|
||||||
|
|||||||
@ -34,7 +34,7 @@ void UPS_AI_Behavior_EQSGenerator_CoverPoints::GenerateItems(FEnvQueryInstance&
|
|||||||
{
|
{
|
||||||
if (QuerierPawn->Implements<UPS_AI_Behavior_Interface>())
|
if (QuerierPawn->Implements<UPS_AI_Behavior_Interface>())
|
||||||
{
|
{
|
||||||
NPCType = IPS_AI_Behavior::Execute_GetBehaviorNPCType(const_cast<APawn*>(QuerierPawn));
|
NPCType = IPS_AI_Behavior_Interface::Execute_GetBehaviorNPCType(const_cast<APawn*>(QuerierPawn));
|
||||||
}
|
}
|
||||||
else if (const auto* PC = QuerierPawn->FindComponentByClass<UPS_AI_Behavior_PersonalityComponent>())
|
else if (const auto* PC = QuerierPawn->FindComponentByClass<UPS_AI_Behavior_PersonalityComponent>())
|
||||||
{
|
{
|
||||||
|
|||||||
@ -71,8 +71,8 @@ void UPS_AI_Behavior_EQSTest_CoverQuality::RunTest(FEnvQueryInstance& QueryInsta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Score: ratio of blocked traces (0.0 = fully exposed, 1.0 = fully covered)
|
// Score: ratio of blocked traces (0.0 = fully exposed, 1.0 = fully covered)
|
||||||
const float Score = BlockedCount / TraceHeights.Num();
|
const float Score = BlockedCount / static_cast<float>(TraceHeights.Num());
|
||||||
It.SetScore(TestPurpose, FilterType, Score);
|
It.SetScore(TestPurpose, FilterType, Score, 0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -47,10 +47,10 @@ void APS_AI_Behavior_AIController::OnPossess(APawn* InPawn)
|
|||||||
if (InPawn->Implements<UPS_AI_Behavior_Interface>())
|
if (InPawn->Implements<UPS_AI_Behavior_Interface>())
|
||||||
{
|
{
|
||||||
// Use the interface — the host project controls the storage
|
// Use the interface — the host project controls the storage
|
||||||
NPCType = IPS_AI_Behavior::Execute_GetBehaviorNPCType(InPawn);
|
NPCType = IPS_AI_Behavior_Interface::Execute_GetBehaviorNPCType(InPawn);
|
||||||
|
|
||||||
// Also check if the interface provides a specific TeamId
|
// Also check if the interface provides a specific TeamId
|
||||||
const uint8 InterfaceTeamId = IPS_AI_Behavior::Execute_GetBehaviorTeamId(InPawn);
|
const uint8 InterfaceTeamId = IPS_AI_Behavior_Interface::Execute_GetBehaviorTeamId(InPawn);
|
||||||
if (InterfaceTeamId != FGenericTeamId::NoTeam)
|
if (InterfaceTeamId != FGenericTeamId::NoTeam)
|
||||||
{
|
{
|
||||||
TeamId = InterfaceTeamId;
|
TeamId = InterfaceTeamId;
|
||||||
@ -73,7 +73,7 @@ void APS_AI_Behavior_AIController::OnPossess(APawn* InPawn)
|
|||||||
case EPS_AI_Behavior_NPCType::Enemy:
|
case EPS_AI_Behavior_NPCType::Enemy:
|
||||||
// Check if infiltrated (hostile=false → disguised as civilian)
|
// Check if infiltrated (hostile=false → disguised as civilian)
|
||||||
if (InPawn->Implements<UPS_AI_Behavior_Interface>() &&
|
if (InPawn->Implements<UPS_AI_Behavior_Interface>() &&
|
||||||
!IPS_AI_Behavior::Execute_IsBehaviorHostile(InPawn))
|
!IPS_AI_Behavior_Interface::Execute_IsBehaviorHostile(InPawn))
|
||||||
{
|
{
|
||||||
TeamId = 1; // Disguised as Civilian
|
TeamId = 1; // Disguised as Civilian
|
||||||
}
|
}
|
||||||
@ -187,7 +187,9 @@ void APS_AI_Behavior_AIController::SetupBlackboard()
|
|||||||
BlackboardAsset->Keys.Add(SplineProgressEntry);
|
BlackboardAsset->Keys.Add(SplineProgressEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
UseBlackboard(BlackboardAsset, Blackboard);
|
UBlackboardComponent* RawBBComp = nullptr;
|
||||||
|
UseBlackboard(BlackboardAsset, RawBBComp);
|
||||||
|
Blackboard = RawBBComp;
|
||||||
|
|
||||||
// Initialize home location to pawn's spawn position
|
// Initialize home location to pawn's spawn position
|
||||||
if (Blackboard && GetPawn())
|
if (Blackboard && GetPawn())
|
||||||
@ -280,7 +282,7 @@ ETeamAttitude::Type APS_AI_Behavior_AIController::GetTeamAttitudeTowards(const A
|
|||||||
// Check via IPS_AI_Behavior interface
|
// Check via IPS_AI_Behavior interface
|
||||||
else if (OtherPawn->Implements<UPS_AI_Behavior_Interface>())
|
else if (OtherPawn->Implements<UPS_AI_Behavior_Interface>())
|
||||||
{
|
{
|
||||||
OtherTeam = IPS_AI_Behavior::Execute_GetBehaviorTeamId(const_cast<APawn*>(OtherPawn));
|
OtherTeam = IPS_AI_Behavior_Interface::Execute_GetBehaviorTeamId(const_cast<APawn*>(OtherPawn));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -163,7 +163,7 @@ void APS_AI_Behavior_CoverPoint::UpdateVisualization()
|
|||||||
#if WITH_EDITORONLY_DATA
|
#if WITH_EDITORONLY_DATA
|
||||||
if (!ArrowComp) return;
|
if (!ArrowComp) return;
|
||||||
|
|
||||||
FLinearColor Color;
|
FLinearColor Color = FLinearColor::White;
|
||||||
switch (PointType)
|
switch (PointType)
|
||||||
{
|
{
|
||||||
case EPS_AI_Behavior_CoverPointType::Cover:
|
case EPS_AI_Behavior_CoverPointType::Cover:
|
||||||
@ -172,6 +172,8 @@ void APS_AI_Behavior_CoverPoint::UpdateVisualization()
|
|||||||
case EPS_AI_Behavior_CoverPointType::HidingSpot:
|
case EPS_AI_Behavior_CoverPointType::HidingSpot:
|
||||||
Color = FLinearColor(1.0f, 0.85f, 0.0f); // Yellow
|
Color = FLinearColor(1.0f, 0.85f, 0.0f); // Yellow
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bEnabled)
|
if (!bEnabled)
|
||||||
|
|||||||
@ -87,7 +87,7 @@ EPS_AI_Behavior_TargetType UPS_AI_Behavior_PerceptionComponent::ClassifyActor(co
|
|||||||
if (Actor->Implements<UPS_AI_Behavior_Interface>())
|
if (Actor->Implements<UPS_AI_Behavior_Interface>())
|
||||||
{
|
{
|
||||||
const EPS_AI_Behavior_NPCType NPCType =
|
const EPS_AI_Behavior_NPCType NPCType =
|
||||||
IPS_AI_Behavior::Execute_GetBehaviorNPCType(const_cast<AActor*>(Actor));
|
IPS_AI_Behavior_Interface::Execute_GetBehaviorNPCType(const_cast<AActor*>(Actor));
|
||||||
|
|
||||||
switch (NPCType)
|
switch (NPCType)
|
||||||
{
|
{
|
||||||
@ -118,7 +118,7 @@ EPS_AI_Behavior_TargetType UPS_AI_Behavior_PerceptionComponent::ClassifyActor(co
|
|||||||
|
|
||||||
// ─── Target Selection ───────────────────────────────────────────────────────
|
// ─── Target Selection ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor() const
|
AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor()
|
||||||
{
|
{
|
||||||
// Get priority from PersonalityProfile if available
|
// Get priority from PersonalityProfile if available
|
||||||
TArray<EPS_AI_Behavior_TargetType> Priority;
|
TArray<EPS_AI_Behavior_TargetType> Priority;
|
||||||
@ -146,7 +146,7 @@ AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor(
|
AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor(
|
||||||
const TArray<EPS_AI_Behavior_TargetType>& TargetPriority) const
|
const TArray<EPS_AI_Behavior_TargetType>& TargetPriority)
|
||||||
{
|
{
|
||||||
// Gather all perceived actors from all senses
|
// Gather all perceived actors from all senses
|
||||||
TArray<AActor*> PerceivedActors;
|
TArray<AActor*> PerceivedActors;
|
||||||
@ -241,7 +241,7 @@ AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor(
|
|||||||
return BestThreat;
|
return BestThreat;
|
||||||
}
|
}
|
||||||
|
|
||||||
float UPS_AI_Behavior_PerceptionComponent::CalculateThreatLevel() const
|
float UPS_AI_Behavior_PerceptionComponent::CalculateThreatLevel()
|
||||||
{
|
{
|
||||||
const AActor* Owner = GetOwner();
|
const AActor* Owner = GetOwner();
|
||||||
if (!Owner) return 0.0f;
|
if (!Owner) return 0.0f;
|
||||||
@ -252,7 +252,7 @@ float UPS_AI_Behavior_PerceptionComponent::CalculateThreatLevel() const
|
|||||||
TArray<AActor*> PerceivedActors;
|
TArray<AActor*> PerceivedActors;
|
||||||
GetCurrentlyPerceivedActors(nullptr, PerceivedActors); // All senses
|
GetCurrentlyPerceivedActors(nullptr, PerceivedActors); // All senses
|
||||||
|
|
||||||
for (const AActor* Actor : PerceivedActors)
|
for (AActor* Actor : PerceivedActors)
|
||||||
{
|
{
|
||||||
if (!Actor) continue;
|
if (!Actor) continue;
|
||||||
|
|
||||||
@ -293,7 +293,7 @@ float UPS_AI_Behavior_PerceptionComponent::CalculateThreatLevel() const
|
|||||||
return FMath::Min(TotalThreat, 2.0f);
|
return FMath::Min(TotalThreat, 2.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UPS_AI_Behavior_PerceptionComponent::GetThreatLocation(FVector& OutLocation) const
|
bool UPS_AI_Behavior_PerceptionComponent::GetThreatLocation(FVector& OutLocation)
|
||||||
{
|
{
|
||||||
AActor* Threat = GetHighestThreatActor();
|
AActor* Threat = GetHighestThreatActor();
|
||||||
if (Threat)
|
if (Threat)
|
||||||
|
|||||||
@ -151,7 +151,7 @@ void UPS_AI_Behavior_PersonalityComponent::OnRep_CurrentState(EPS_AI_Behavior_St
|
|||||||
AActor* Owner = GetOwner();
|
AActor* Owner = GetOwner();
|
||||||
if (Owner && Owner->Implements<UPS_AI_Behavior_Interface>())
|
if (Owner && Owner->Implements<UPS_AI_Behavior_Interface>())
|
||||||
{
|
{
|
||||||
IPS_AI_Behavior::Execute_OnBehaviorStateChanged(Owner, CurrentState, OldState);
|
IPS_AI_Behavior_Interface::Execute_OnBehaviorStateChanged(Owner, CurrentState, OldState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,10 +168,10 @@ void UPS_AI_Behavior_PersonalityComponent::HandleStateChanged(
|
|||||||
if (Owner->Implements<UPS_AI_Behavior_Interface>())
|
if (Owner->Implements<UPS_AI_Behavior_Interface>())
|
||||||
{
|
{
|
||||||
float NewSpeed = Profile ? Profile->GetSpeedForState(NewState) : 150.0f;
|
float NewSpeed = Profile ? Profile->GetSpeedForState(NewState) : 150.0f;
|
||||||
IPS_AI_Behavior::Execute_SetBehaviorMovementSpeed(Owner, NewSpeed);
|
IPS_AI_Behavior_Interface::Execute_SetBehaviorMovementSpeed(Owner, NewSpeed);
|
||||||
|
|
||||||
// 3. Notify the Pawn of the state change
|
// 3. Notify the Pawn of the state change
|
||||||
IPS_AI_Behavior::Execute_OnBehaviorStateChanged(Owner, NewState, OldState);
|
IPS_AI_Behavior_Interface::Execute_OnBehaviorStateChanged(Owner, NewState, OldState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ EPS_AI_Behavior_NPCType UPS_AI_Behavior_PersonalityComponent::GetNPCType() const
|
|||||||
AActor* Owner = GetOwner();
|
AActor* Owner = GetOwner();
|
||||||
if (Owner && Owner->Implements<UPS_AI_Behavior_Interface>())
|
if (Owner && Owner->Implements<UPS_AI_Behavior_Interface>())
|
||||||
{
|
{
|
||||||
return IPS_AI_Behavior::Execute_GetBehaviorNPCType(Owner);
|
return IPS_AI_Behavior_Interface::Execute_GetBehaviorNPCType(Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: read from PersonalityProfile
|
// Fallback: read from PersonalityProfile
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include "PS_AI_Behavior_SplinePath.h"
|
#include "PS_AI_Behavior_SplinePath.h"
|
||||||
#include "PS_AI_Behavior_SplineNetwork.h"
|
#include "PS_AI_Behavior_SplineNetwork.h"
|
||||||
#include "PS_AI_Behavior_PersonalityComponent.h"
|
#include "PS_AI_Behavior_PersonalityComponent.h"
|
||||||
|
#include "Components/SplineComponent.h"
|
||||||
#include "GameFramework/Character.h"
|
#include "GameFramework/Character.h"
|
||||||
#include "GameFramework/CharacterMovementComponent.h"
|
#include "GameFramework/CharacterMovementComponent.h"
|
||||||
#include "Net/UnrealNetwork.h"
|
#include "Net/UnrealNetwork.h"
|
||||||
|
|||||||
@ -11,22 +11,10 @@
|
|||||||
void UPS_AI_Behavior_SplineNetwork::Initialize(FSubsystemCollectionBase& Collection)
|
void UPS_AI_Behavior_SplineNetwork::Initialize(FSubsystemCollectionBase& Collection)
|
||||||
{
|
{
|
||||||
Super::Initialize(Collection);
|
Super::Initialize(Collection);
|
||||||
|
|
||||||
UWorld* World = GetWorld();
|
|
||||||
if (World)
|
|
||||||
{
|
|
||||||
BeginPlayHandle = World->OnWorldBeginPlay.AddUObject(this, &UPS_AI_Behavior_SplineNetwork::OnWorldBeginPlay);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UPS_AI_Behavior_SplineNetwork::Deinitialize()
|
void UPS_AI_Behavior_SplineNetwork::Deinitialize()
|
||||||
{
|
{
|
||||||
UWorld* World = GetWorld();
|
|
||||||
if (World && BeginPlayHandle.IsValid())
|
|
||||||
{
|
|
||||||
World->OnWorldBeginPlay.Remove(BeginPlayHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
AllSplines.Empty();
|
AllSplines.Empty();
|
||||||
TotalJunctions = 0;
|
TotalJunctions = 0;
|
||||||
Super::Deinitialize();
|
Super::Deinitialize();
|
||||||
@ -34,6 +22,7 @@ void UPS_AI_Behavior_SplineNetwork::Deinitialize()
|
|||||||
|
|
||||||
void UPS_AI_Behavior_SplineNetwork::OnWorldBeginPlay(UWorld& InWorld)
|
void UPS_AI_Behavior_SplineNetwork::OnWorldBeginPlay(UWorld& InWorld)
|
||||||
{
|
{
|
||||||
|
Super::OnWorldBeginPlay(InWorld);
|
||||||
RebuildNetwork();
|
RebuildNetwork();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "BehaviorTree/BTTaskNode.h"
|
#include "BehaviorTree/BTTaskNode.h"
|
||||||
|
#include "PS_AI_Behavior_Definitions.h"
|
||||||
#include "PS_AI_Behavior_BTTask_FindCover.generated.h"
|
#include "PS_AI_Behavior_BTTask_FindCover.generated.h"
|
||||||
|
|
||||||
class APS_AI_Behavior_CoverPoint;
|
class APS_AI_Behavior_CoverPoint;
|
||||||
|
|||||||
@ -81,7 +81,7 @@ public:
|
|||||||
// ─── Runtime (server-only) ──────────────────────────────────────────
|
// ─── Runtime (server-only) ──────────────────────────────────────────
|
||||||
|
|
||||||
/** Current occupants. Managed by the BT / EQS. */
|
/** Current occupants. Managed by the BT / EQS. */
|
||||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "Cover Point|Runtime")
|
UPROPERTY(Transient)
|
||||||
TArray<TWeakObjectPtr<AActor>> CurrentOccupants;
|
TArray<TWeakObjectPtr<AActor>> CurrentOccupants;
|
||||||
|
|
||||||
// ─── API ────────────────────────────────────────────────────────────
|
// ─── API ────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
// ─── Log Category ───────────────────────────────────────────────────────────
|
// ─── Log Category ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
DECLARE_LOG_CATEGORY_EXTERN(LogPS_AI_Behavior, Log, All);
|
PS_AI_BEHAVIOR_API DECLARE_LOG_CATEGORY_EXTERN(LogPS_AI_Behavior, Log, All);
|
||||||
|
|
||||||
// ─── API Macro ──────────────────────────────────────────────────────────────
|
// ─── API Macro ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ class PS_AI_BEHAVIOR_API UPS_AI_Behavior_Interface : public UInterface
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
};
|
};
|
||||||
|
|
||||||
class PS_AI_BEHAVIOR_API IPS_AI_Behavior
|
class PS_AI_BEHAVIOR_API IPS_AI_Behavior_Interface
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
|||||||
@ -33,17 +33,17 @@ public:
|
|||||||
* @return The most threatening actor, or nullptr if none perceived.
|
* @return The most threatening actor, or nullptr if none perceived.
|
||||||
*/
|
*/
|
||||||
UFUNCTION(BlueprintCallable, Category = "PS AI Behavior|Perception")
|
UFUNCTION(BlueprintCallable, Category = "PS AI Behavior|Perception")
|
||||||
AActor* GetHighestThreatActor(const TArray<EPS_AI_Behavior_TargetType>& TargetPriority) const;
|
AActor* GetHighestThreatActor(const TArray<EPS_AI_Behavior_TargetType>& TargetPriority);
|
||||||
|
|
||||||
/** Convenience overload — reads priority from the Pawn's PersonalityProfile. */
|
/** Convenience overload — reads priority from the Pawn's PersonalityProfile. */
|
||||||
AActor* GetHighestThreatActor() const;
|
AActor* GetHighestThreatActor();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute an aggregate threat level from all currently perceived hostile stimuli.
|
* Compute an aggregate threat level from all currently perceived hostile stimuli.
|
||||||
* Returns 0.0 (no threat) to 1.0+ (extreme danger).
|
* Returns 0.0 (no threat) to 1.0+ (extreme danger).
|
||||||
*/
|
*/
|
||||||
UFUNCTION(BlueprintCallable, Category = "PS AI Behavior|Perception")
|
UFUNCTION(BlueprintCallable, Category = "PS AI Behavior|Perception")
|
||||||
float CalculateThreatLevel() const;
|
float CalculateThreatLevel();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the location of the last known threat stimulus.
|
* Get the location of the last known threat stimulus.
|
||||||
@ -51,7 +51,7 @@ public:
|
|||||||
* @return True if a threat was found.
|
* @return True if a threat was found.
|
||||||
*/
|
*/
|
||||||
UFUNCTION(BlueprintCallable, Category = "PS AI Behavior|Perception")
|
UFUNCTION(BlueprintCallable, Category = "PS AI Behavior|Perception")
|
||||||
bool GetThreatLocation(FVector& OutLocation) const;
|
bool GetThreatLocation(FVector& OutLocation);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void BeginPlay() override;
|
virtual void BeginPlay() override;
|
||||||
|
|||||||
@ -103,8 +103,6 @@ private:
|
|||||||
void DetectJunctions(APS_AI_Behavior_SplinePath* SplineA,
|
void DetectJunctions(APS_AI_Behavior_SplinePath* SplineA,
|
||||||
APS_AI_Behavior_SplinePath* SplineB, float Tolerance);
|
APS_AI_Behavior_SplinePath* SplineB, float Tolerance);
|
||||||
|
|
||||||
/** World init callback. */
|
/** UWorldSubsystem override — called when world begins play. */
|
||||||
void OnWorldBeginPlay(UWorld& InWorld);
|
virtual void OnWorldBeginPlay(UWorld& InWorld) override;
|
||||||
|
|
||||||
FDelegateHandle BeginPlayHandle;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -54,9 +54,9 @@ void FPS_AI_Behavior_SplineEdMode::Exit()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool FPS_AI_Behavior_SplineEdMode::HandleClick(
|
bool FPS_AI_Behavior_SplineEdMode::HandleClick(
|
||||||
FEditorViewportClient* InViewportClient, HHitProxy* HitProxy, const FInputClick& Click)
|
FEditorViewportClient* InViewportClient, HHitProxy* HitProxy, const FViewportClick& Click)
|
||||||
{
|
{
|
||||||
if (Click.Key != EKeys::LeftMouseButton)
|
if (Click.GetKey() != EKeys::LeftMouseButton)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -76,9 +76,6 @@ bool FPS_AI_Behavior_SplineEdMode::HandleClick(
|
|||||||
InViewportClient->EngineShowFlags));
|
InViewportClient->EngineShowFlags));
|
||||||
FSceneView* View = InViewportClient->CalcSceneView(&ViewFamily);
|
FSceneView* View = InViewportClient->CalcSceneView(&ViewFamily);
|
||||||
|
|
||||||
const FVector WorldOrigin = View->ViewMatrices.GetViewOrigin();
|
|
||||||
FVector WorldDirection;
|
|
||||||
|
|
||||||
// Deproject mouse to world
|
// Deproject mouse to world
|
||||||
FVector2D MousePos(HitX, HitY);
|
FVector2D MousePos(HitX, HitY);
|
||||||
FVector RayOrigin, RayDirection;
|
FVector RayOrigin, RayDirection;
|
||||||
@ -100,7 +97,7 @@ bool FPS_AI_Behavior_SplineEdMode::HandleClick(
|
|||||||
FVector ClickLocation = Hit.ImpactPoint;
|
FVector ClickLocation = Hit.ImpactPoint;
|
||||||
|
|
||||||
// Ctrl+Click on existing spline → select for extension
|
// Ctrl+Click on existing spline → select for extension
|
||||||
if (Click.bControlDown)
|
if (Click.IsControlDown())
|
||||||
{
|
{
|
||||||
// Check if we hit a SplinePath
|
// Check if we hit a SplinePath
|
||||||
AActor* HitActor = Hit.GetActor();
|
AActor* HitActor = Hit.GetActor();
|
||||||
|
|||||||
@ -37,7 +37,7 @@ public:
|
|||||||
virtual void Exit() override;
|
virtual void Exit() override;
|
||||||
|
|
||||||
virtual bool HandleClick(FEditorViewportClient* InViewportClient,
|
virtual bool HandleClick(FEditorViewportClient* InViewportClient,
|
||||||
HHitProxy* HitProxy, const FInputClick& Click) override;
|
HHitProxy* HitProxy, const FViewportClick& Click) override;
|
||||||
virtual bool InputKey(FEditorViewportClient* ViewportClient,
|
virtual bool InputKey(FEditorViewportClient* ViewportClient,
|
||||||
FViewport* Viewport, FKey Key, EInputEvent Event) override;
|
FViewport* Viewport, FKey Key, EInputEvent Event) override;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user