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": [
|
||||
{
|
||||
"Name": "NavigationSystem",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
"Plugins": []
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FindCover::ExecuteTask(
|
||||
UBlackboardComponent* BB = OwnerComp.GetBlackboardComponent();
|
||||
if (!BB) return EBTNodeResult::Failed;
|
||||
|
||||
const UWorld* World = GetWorld();
|
||||
UWorld* World = GetWorld();
|
||||
if (!World) return EBTNodeResult::Failed;
|
||||
|
||||
const FVector NpcLoc = AIC->GetPawn()->GetActorLocation();
|
||||
|
||||
@ -42,18 +42,11 @@ EBTNodeResult::Type UPS_AI_Behavior_BTTask_FollowSpline::ExecuteTask(
|
||||
Follower->ResumeFollowing();
|
||||
}
|
||||
|
||||
// Listen for end-of-spline
|
||||
// Initialize memory — TickTask will poll bIsFollowing to detect end-of-spline
|
||||
FFollowMemory* Memory = reinterpret_cast<FFollowMemory*>(NodeMemory);
|
||||
Memory->Elapsed = 0.0f;
|
||||
Memory->bEndReached = false;
|
||||
|
||||
// Bind to end delegate
|
||||
Follower->OnSplineEndReached.AddWeakLambda(this,
|
||||
[Memory](APS_AI_Behavior_SplinePath* /*Spline*/)
|
||||
{
|
||||
Memory->bEndReached = true;
|
||||
});
|
||||
|
||||
return EBTNodeResult::InProgress;
|
||||
}
|
||||
|
||||
@ -62,11 +55,17 @@ void UPS_AI_Behavior_BTTask_FollowSpline::TickTask(
|
||||
{
|
||||
FFollowMemory* Memory = reinterpret_cast<FFollowMemory*>(NodeMemory);
|
||||
|
||||
// Check if spline end was reached
|
||||
if (Memory->bEndReached)
|
||||
// Check if spline end was reached (poll bIsFollowing — set to false by SplineFollowerComponent)
|
||||
AAIController* AICCheck = OwnerComp.GetAIOwner();
|
||||
if (AICCheck && AICCheck->GetPawn())
|
||||
{
|
||||
FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
|
||||
return;
|
||||
UPS_AI_Behavior_SplineFollowerComponent* FollowerCheck =
|
||||
AICCheck->GetPawn()->FindComponentByClass<UPS_AI_Behavior_SplineFollowerComponent>();
|
||||
if (FollowerCheck && !FollowerCheck->bIsFollowing)
|
||||
{
|
||||
FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
if (!AIC || !AIC->GetPawn())
|
||||
{
|
||||
FinishLatentTask(OwnerComp, EBTNodeResult::Failed);
|
||||
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(
|
||||
|
||||
@ -34,7 +34,7 @@ void UPS_AI_Behavior_EQSGenerator_CoverPoints::GenerateItems(FEnvQueryInstance&
|
||||
{
|
||||
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>())
|
||||
{
|
||||
|
||||
@ -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)
|
||||
const float Score = BlockedCount / TraceHeights.Num();
|
||||
It.SetScore(TestPurpose, FilterType, Score);
|
||||
const float Score = BlockedCount / static_cast<float>(TraceHeights.Num());
|
||||
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>())
|
||||
{
|
||||
// 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
|
||||
const uint8 InterfaceTeamId = IPS_AI_Behavior::Execute_GetBehaviorTeamId(InPawn);
|
||||
const uint8 InterfaceTeamId = IPS_AI_Behavior_Interface::Execute_GetBehaviorTeamId(InPawn);
|
||||
if (InterfaceTeamId != FGenericTeamId::NoTeam)
|
||||
{
|
||||
TeamId = InterfaceTeamId;
|
||||
@ -73,7 +73,7 @@ void APS_AI_Behavior_AIController::OnPossess(APawn* InPawn)
|
||||
case EPS_AI_Behavior_NPCType::Enemy:
|
||||
// Check if infiltrated (hostile=false → disguised as civilian)
|
||||
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
|
||||
}
|
||||
@ -187,7 +187,9 @@ void APS_AI_Behavior_AIController::SetupBlackboard()
|
||||
BlackboardAsset->Keys.Add(SplineProgressEntry);
|
||||
}
|
||||
|
||||
UseBlackboard(BlackboardAsset, Blackboard);
|
||||
UBlackboardComponent* RawBBComp = nullptr;
|
||||
UseBlackboard(BlackboardAsset, RawBBComp);
|
||||
Blackboard = RawBBComp;
|
||||
|
||||
// Initialize home location to pawn's spawn position
|
||||
if (Blackboard && GetPawn())
|
||||
@ -280,7 +282,7 @@ ETeamAttitude::Type APS_AI_Behavior_AIController::GetTeamAttitudeTowards(const A
|
||||
// Check via IPS_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 (!ArrowComp) return;
|
||||
|
||||
FLinearColor Color;
|
||||
FLinearColor Color = FLinearColor::White;
|
||||
switch (PointType)
|
||||
{
|
||||
case EPS_AI_Behavior_CoverPointType::Cover:
|
||||
@ -172,6 +172,8 @@ void APS_AI_Behavior_CoverPoint::UpdateVisualization()
|
||||
case EPS_AI_Behavior_CoverPointType::HidingSpot:
|
||||
Color = FLinearColor(1.0f, 0.85f, 0.0f); // Yellow
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bEnabled)
|
||||
|
||||
@ -87,7 +87,7 @@ EPS_AI_Behavior_TargetType UPS_AI_Behavior_PerceptionComponent::ClassifyActor(co
|
||||
if (Actor->Implements<UPS_AI_Behavior_Interface>())
|
||||
{
|
||||
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)
|
||||
{
|
||||
@ -118,7 +118,7 @@ EPS_AI_Behavior_TargetType UPS_AI_Behavior_PerceptionComponent::ClassifyActor(co
|
||||
|
||||
// ─── Target Selection ───────────────────────────────────────────────────────
|
||||
|
||||
AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor() const
|
||||
AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor()
|
||||
{
|
||||
// Get priority from PersonalityProfile if available
|
||||
TArray<EPS_AI_Behavior_TargetType> Priority;
|
||||
@ -146,7 +146,7 @@ AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor() const
|
||||
}
|
||||
|
||||
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
|
||||
TArray<AActor*> PerceivedActors;
|
||||
@ -241,7 +241,7 @@ AActor* UPS_AI_Behavior_PerceptionComponent::GetHighestThreatActor(
|
||||
return BestThreat;
|
||||
}
|
||||
|
||||
float UPS_AI_Behavior_PerceptionComponent::CalculateThreatLevel() const
|
||||
float UPS_AI_Behavior_PerceptionComponent::CalculateThreatLevel()
|
||||
{
|
||||
const AActor* Owner = GetOwner();
|
||||
if (!Owner) return 0.0f;
|
||||
@ -252,7 +252,7 @@ float UPS_AI_Behavior_PerceptionComponent::CalculateThreatLevel() const
|
||||
TArray<AActor*> PerceivedActors;
|
||||
GetCurrentlyPerceivedActors(nullptr, PerceivedActors); // All senses
|
||||
|
||||
for (const AActor* Actor : PerceivedActors)
|
||||
for (AActor* Actor : PerceivedActors)
|
||||
{
|
||||
if (!Actor) continue;
|
||||
|
||||
@ -293,7 +293,7 @@ float UPS_AI_Behavior_PerceptionComponent::CalculateThreatLevel() const
|
||||
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();
|
||||
if (Threat)
|
||||
|
||||
@ -151,7 +151,7 @@ void UPS_AI_Behavior_PersonalityComponent::OnRep_CurrentState(EPS_AI_Behavior_St
|
||||
AActor* Owner = GetOwner();
|
||||
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>())
|
||||
{
|
||||
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
|
||||
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();
|
||||
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
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "PS_AI_Behavior_SplinePath.h"
|
||||
#include "PS_AI_Behavior_SplineNetwork.h"
|
||||
#include "PS_AI_Behavior_PersonalityComponent.h"
|
||||
#include "Components/SplineComponent.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
|
||||
@ -11,22 +11,10 @@
|
||||
void UPS_AI_Behavior_SplineNetwork::Initialize(FSubsystemCollectionBase& 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()
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
if (World && BeginPlayHandle.IsValid())
|
||||
{
|
||||
World->OnWorldBeginPlay.Remove(BeginPlayHandle);
|
||||
}
|
||||
|
||||
AllSplines.Empty();
|
||||
TotalJunctions = 0;
|
||||
Super::Deinitialize();
|
||||
@ -34,6 +22,7 @@ void UPS_AI_Behavior_SplineNetwork::Deinitialize()
|
||||
|
||||
void UPS_AI_Behavior_SplineNetwork::OnWorldBeginPlay(UWorld& InWorld)
|
||||
{
|
||||
Super::OnWorldBeginPlay(InWorld);
|
||||
RebuildNetwork();
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "BehaviorTree/BTTaskNode.h"
|
||||
#include "PS_AI_Behavior_Definitions.h"
|
||||
#include "PS_AI_Behavior_BTTask_FindCover.generated.h"
|
||||
|
||||
class APS_AI_Behavior_CoverPoint;
|
||||
|
||||
@ -81,7 +81,7 @@ public:
|
||||
// ─── Runtime (server-only) ──────────────────────────────────────────
|
||||
|
||||
/** Current occupants. Managed by the BT / EQS. */
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "Cover Point|Runtime")
|
||||
UPROPERTY(Transient)
|
||||
TArray<TWeakObjectPtr<AActor>> CurrentOccupants;
|
||||
|
||||
// ─── API ────────────────────────────────────────────────────────────
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
// ─── 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 ──────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ class PS_AI_BEHAVIOR_API UPS_AI_Behavior_Interface : public UInterface
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
class PS_AI_BEHAVIOR_API IPS_AI_Behavior
|
||||
class PS_AI_BEHAVIOR_API IPS_AI_Behavior_Interface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
@ -33,17 +33,17 @@ public:
|
||||
* @return The most threatening actor, or nullptr if none perceived.
|
||||
*/
|
||||
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. */
|
||||
AActor* GetHighestThreatActor() const;
|
||||
AActor* GetHighestThreatActor();
|
||||
|
||||
/**
|
||||
* Compute an aggregate threat level from all currently perceived hostile stimuli.
|
||||
* Returns 0.0 (no threat) to 1.0+ (extreme danger).
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "PS AI Behavior|Perception")
|
||||
float CalculateThreatLevel() const;
|
||||
float CalculateThreatLevel();
|
||||
|
||||
/**
|
||||
* Get the location of the last known threat stimulus.
|
||||
@ -51,7 +51,7 @@ public:
|
||||
* @return True if a threat was found.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "PS AI Behavior|Perception")
|
||||
bool GetThreatLocation(FVector& OutLocation) const;
|
||||
bool GetThreatLocation(FVector& OutLocation);
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
@ -103,8 +103,6 @@ private:
|
||||
void DetectJunctions(APS_AI_Behavior_SplinePath* SplineA,
|
||||
APS_AI_Behavior_SplinePath* SplineB, float Tolerance);
|
||||
|
||||
/** World init callback. */
|
||||
void OnWorldBeginPlay(UWorld& InWorld);
|
||||
|
||||
FDelegateHandle BeginPlayHandle;
|
||||
/** UWorldSubsystem override — called when world begins play. */
|
||||
virtual void OnWorldBeginPlay(UWorld& InWorld) override;
|
||||
};
|
||||
|
||||
@ -54,9 +54,9 @@ void FPS_AI_Behavior_SplineEdMode::Exit()
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -76,9 +76,6 @@ bool FPS_AI_Behavior_SplineEdMode::HandleClick(
|
||||
InViewportClient->EngineShowFlags));
|
||||
FSceneView* View = InViewportClient->CalcSceneView(&ViewFamily);
|
||||
|
||||
const FVector WorldOrigin = View->ViewMatrices.GetViewOrigin();
|
||||
FVector WorldDirection;
|
||||
|
||||
// Deproject mouse to world
|
||||
FVector2D MousePos(HitX, HitY);
|
||||
FVector RayOrigin, RayDirection;
|
||||
@ -100,7 +97,7 @@ bool FPS_AI_Behavior_SplineEdMode::HandleClick(
|
||||
FVector ClickLocation = Hit.ImpactPoint;
|
||||
|
||||
// Ctrl+Click on existing spline → select for extension
|
||||
if (Click.bControlDown)
|
||||
if (Click.IsControlDown())
|
||||
{
|
||||
// Check if we hit a SplinePath
|
||||
AActor* HitActor = Hit.GetActor();
|
||||
|
||||
@ -37,7 +37,7 @@ public:
|
||||
virtual void Exit() override;
|
||||
|
||||
virtual bool HandleClick(FEditorViewportClient* InViewportClient,
|
||||
HHitProxy* HitProxy, const FInputClick& Click) override;
|
||||
HHitProxy* HitProxy, const FViewportClick& Click) override;
|
||||
virtual bool InputKey(FEditorViewportClient* ViewportClient,
|
||||
FViewport* Viewport, FKey Key, EInputEvent Event) override;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user