-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathPreyAIController.cpp
156 lines (127 loc) · 5.11 KB
/
PreyAIController.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Fill out your copyright notice in the Description page of Project Settings.
#include "RunForLife.h"
#include "PreyAIController.h"
#include "PreyCharacter.h"
#include "PatrolTargetPoint.h"
// AI module
#include "BehaviorTree/BehaviorTree.h"
#include "BehaviorTree/BehaviorTreeComponent.h"
#include "BehaviorTree/BlackboardComponent.h"
APreyAIController::APreyAIController(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
// Set blackboard key names
IsSeeingHunterKey = "IsSeeingHunter";
HunterKey = "Hunter";
HunterLocationKey = "HunterLocation";
SafeLocationKey = "SafeLocation";
IsPatrollingKey = "IsPatrolling";
SafeDistance = 700.0f;
PatrollingStamina = 70.0f;
RestingStamina = 30.0f;
}
void APreyAIController::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
APreyCharacter* PreyCharacter = Cast<APreyCharacter>(GetPawn());
if (PreyCharacter && BlackboardComp)
{
if (PreyCharacter->GetCurrentStamina() > PatrollingStamina)
{
BlackboardComp->SetValueAsBool(IsPatrollingKey, true);
}
else if (PreyCharacter->GetCurrentStamina() < RestingStamina)
{
BlackboardComp->SetValueAsBool(IsPatrollingKey, false);
}
}
}
void APreyAIController::Possess(APawn* InPawn)
{
Super::Possess(InPawn);
APreyCharacter* PreyCharacter = Cast<APreyCharacter>(InPawn);
if (PreyCharacter && PreyCharacter->BehaviorTree->BlackboardAsset)
{
// Initialize blackboard
BlackboardComp->InitializeBlackboard(*PreyCharacter->BehaviorTree->BlackboardAsset);
// Start behavior tree
BehaviorComp->StartTree(*PreyCharacter->BehaviorTree);
}
// Store all patrol target points
UGameplayStatics::GetAllActorsOfClass(this, APatrolTargetPoint::StaticClass(), PatrolTargetPoints);
}
void APreyAIController::SetHunterLocation(APawn* Hunter)
{
if (BlackboardComp && Hunter)
{
// Unset noise information
BlackboardComp->SetValueAsBool(IsHearingNoiseKey, false);
const FVector NewHunterLocation = Hunter->GetActorLocation();
// No hunter record or the new hunter is closer
if (!BlackboardComp->GetValueAsBool(IsSeeingHunterKey) || IsNewLocationCloser(NewHunterLocation, HunterLocationKey))
{
// Set hunter information
BlackboardComp->SetValueAsBool(IsSeeingHunterKey, true);
BlackboardComp->SetValueAsVector(HunterLocationKey, NewHunterLocation);
SetSafePatrolPoint(NewHunterLocation);
}
}
}
void APreyAIController::SetNoiseLocation(const FVector& Location)
{
if (BlackboardComp)
{
// Not seeing a hunter
if (!BlackboardComp->GetValueAsBool(IsSeeingHunterKey))
{
// No noise record or the new noise is closer
if (!BlackboardComp->GetValueAsBool(IsHearingNoiseKey) || IsNewLocationCloser(Location, NoiseLocationKey))
{
// Set noise information
BlackboardComp->SetValueAsBool(IsHearingNoiseKey, true);
BlackboardComp->SetValueAsVector(NoiseLocationKey, Location);
SetSafePatrolPoint(Location);
}
// Noise is closer implies hunter might chase after prey itself
else if (IsNewLocationCloser(Location, NoiseLocationKey))
{
BlackboardComp->SetValueAsBool(IsHearingNoiseKey, false);
BlackboardComp->SetValueAsBool(IsSeeingHunterKey, true);
BlackboardComp->SetValueAsVector(HunterLocationKey, Location);
SetSafePatrolPoint(Location);
}
}
}
}
void APreyAIController::SetSafePatrolPoint(const FVector& UnsafeLocation)
{
if (BlackboardComp && GetCharacter() && PatrolTargetPoints.Num() > 0)
{
const FVector ActorLocation = GetCharacter()->GetActorLocation();
const FVector UnsafeDirection = UnsafeLocation - ActorLocation;
float MaxDistanceSquared = 0.0f;
// Find a random patrol target point as a starting point for searching safe location
const int32 RandomIndex = FMath::RandRange(0, PatrolTargetPoints.Num() - 1);
int Index = RandomIndex;
int FarthestIndex = Index;
do
{
Index = (Index + 1) % PatrolTargetPoints.Num();
const FVector CandidateLocation = PatrolTargetPoints[Index]->GetActorLocation();
const FVector CandidateDirection = CandidateLocation - ActorLocation;
const float SafeDistanceSquared = SafeDistance * SafeDistance;
const float DistanceSquared = FVector::DistSquaredXY(CandidateLocation, UnsafeLocation);
// Update FarthestIndex if CandidateLocation has the maximum distance from UnsafeLocation
FarthestIndex = (DistanceSquared > MaxDistanceSquared) ? Index : FarthestIndex;
// Check if the distance between CandidateLocation and UnsafeLocation is safe enough
// and the angle between UnsafeDirection and CandidateDirection is larger than or equal to 90 degrees
if (DistanceSquared >= SafeDistanceSquared && UnsafeDirection.CosineAngle2D(CandidateDirection) <= 0.0f)
{
// Set the candidate location as safe locaiton and stop searching
BlackboardComp->SetValueAsVector(SafeLocationKey, CandidateLocation);
return;
}
} while (Index != RandomIndex);
// Set the farthest partol target point from UnsafeLocation as safe location
BlackboardComp->SetValueAsVector(SafeLocationKey, PatrolTargetPoints[FarthestIndex]->GetActorLocation());
}
}