Added GOAPAgent Functionality
This commit is contained in:
parent
2e03f7cf19
commit
279669ebcd
BIN
COMP250_1_2101327_AI/Content/Blueprints/BP_Enemy.uasset
(Stored with Git LFS)
BIN
COMP250_1_2101327_AI/Content/Blueprints/BP_Enemy.uasset
(Stored with Git LFS)
Binary file not shown.
BIN
COMP250_1_2101327_AI/Content/Main.umap
(Stored with Git LFS)
BIN
COMP250_1_2101327_AI/Content/Main.umap
(Stored with Git LFS)
Binary file not shown.
BIN
COMP250_1_2101327_AI/Content/StarterContent/Blueprints/Blueprint_CeilingLight.uasset
(Stored with Git LFS)
BIN
COMP250_1_2101327_AI/Content/StarterContent/Blueprints/Blueprint_CeilingLight.uasset
(Stored with Git LFS)
Binary file not shown.
BIN
COMP250_1_2101327_AI/Content/StarterContent/Particles/P_Ambient_Dust.uasset
(Stored with Git LFS)
BIN
COMP250_1_2101327_AI/Content/StarterContent/Particles/P_Ambient_Dust.uasset
(Stored with Git LFS)
Binary file not shown.
@ -3,29 +3,25 @@
|
|||||||
|
|
||||||
#include "GOAPAction.h"
|
#include "GOAPAction.h"
|
||||||
|
|
||||||
bool UGOAPAction::CheckPreConditions(TMap<FString, int32> WorldState)
|
bool UGOAPAction::CheckPreConditions(TMap<FString, int> WorldState)
|
||||||
{
|
{
|
||||||
for (TPair<FString, int32> PreCondition : PreConditions)
|
for (TPair<FString, int> PreCondition : PreConditions)
|
||||||
{
|
{
|
||||||
if (WorldState.Contains(PreCondition.Key))
|
if (WorldState.Contains(PreCondition.Key))
|
||||||
{
|
{
|
||||||
if (WorldState[PreCondition.Key] != PreCondition.Value) return false;
|
if (WorldState[PreCondition.Key] < PreCondition.Value) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TMap<FString, int32> UGOAPAction::ApplyEffects(TMap<FString, int32> WorldState)
|
TMap<FString, int> UGOAPAction::ApplyEffects(TMap<FString, int> WorldState)
|
||||||
{
|
{
|
||||||
for (TPair<FString, int32> Effect : Effects)
|
for (TPair<FString, int> Effect : Effects)
|
||||||
{
|
{
|
||||||
if (WorldState.Contains(Effect.Key))
|
if (WorldState.Contains(Effect.Key))
|
||||||
{
|
{
|
||||||
WorldState[Effect.Key] = Effect.Value;
|
WorldState[Effect.Key] -= Effect.Value;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WorldState.Add(Effect.Key, Effect.Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return WorldState;
|
return WorldState;
|
||||||
|
@ -15,10 +15,18 @@ class COMP250_1_2101327_AI_API UGOAPAction : public UObject
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual bool CheckPreConditions(TMap<FString, int32> WorldState);
|
float ActionCost = 1.0f;
|
||||||
TMap<FString, int32> ApplyEffects(TMap<FString, int32> WorldState);
|
|
||||||
|
|
||||||
protected:
|
UFUNCTION()
|
||||||
TMap<FString, int32> PreConditions;
|
bool CheckPreConditions(TMap<FString, int> WorldState);
|
||||||
TMap<FString, int32> Effects;
|
UFUNCTION()
|
||||||
|
TMap<FString, int> ApplyEffects(TMap<FString, int> WorldState);
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
TMap<FString, int> PreConditions;
|
||||||
|
UPROPERTY()
|
||||||
|
TMap<FString, int> Effects;
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
virtual void Init(){}
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
#include "GOAPAgent.h"
|
#include "GOAPAgent.h"
|
||||||
|
|
||||||
|
#include "Actions/Combo_P.h"
|
||||||
|
#include "Actions/DefaultAttack.h"
|
||||||
|
|
||||||
|
|
||||||
// Sets default values for this component's properties
|
// Sets default values for this component's properties
|
||||||
UGOAPAgent::UGOAPAgent()
|
UGOAPAgent::UGOAPAgent()
|
||||||
@ -20,11 +23,61 @@ void UGOAPAgent::BeginPlay()
|
|||||||
{
|
{
|
||||||
Super::BeginPlay();
|
Super::BeginPlay();
|
||||||
|
|
||||||
// ...
|
CombatSystem = Cast<ATurnBaseCombatV2>(GetWorld()->GetGameState());
|
||||||
|
|
||||||
|
UGOAPAction* DefaultAttack = NewObject<UGOAPAction>(GetWorld(), UDefaultAttack::StaticClass());
|
||||||
|
DefaultAttack->Init();
|
||||||
|
AvailableActions.Add(DefaultAttack);
|
||||||
|
UGOAPAction* Combo_P = NewObject<UGOAPAction>(GetWorld(), UCombo_P::StaticClass());
|
||||||
|
Combo_P->Init();
|
||||||
|
AvailableActions.Add(Combo_P);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TMap<FString, int> UGOAPAgent::GetWorldState()
|
||||||
|
{
|
||||||
|
TMap<FString, int> WorldState;
|
||||||
|
WorldState.Add("PlayerHealth", *CombatSystem->PlayerHealth);
|
||||||
|
WorldState.Add("EnemyHealth", *CombatSystem->EnemyHealth);
|
||||||
|
WorldState.Add("ProbertiumResource", CombatSystem->EnemyProbertiumResource);
|
||||||
|
WorldState.Add("EisResource", CombatSystem->EnemyEisResource);
|
||||||
|
WorldState.Add("AzosResource", CombatSystem->EnemyAzosResource);
|
||||||
|
WorldState.Add("IroquoidResource", CombatSystem->EnemyIroquoidResource);
|
||||||
|
return WorldState;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UGOAPAgent::BuildActionGraph(UActionNode* Parent, TArray<UActionNode*>& SuccessfulLeaves, TMap<FString, int> WorldState, TMap<FString, int> AgentGoals)
|
||||||
|
{
|
||||||
|
bool bFoundAPlan = false;
|
||||||
|
|
||||||
|
for (UGOAPAction* Action : AvailableActions)
|
||||||
|
{
|
||||||
|
if (Action->CheckPreConditions(WorldState))
|
||||||
|
{
|
||||||
|
TMap<FString, int> NewWorldState = Action->ApplyEffects(WorldState);
|
||||||
|
UActionNode* NewNode = NewObject<UActionNode>();
|
||||||
|
NewNode->Init(Parent, Action, NewWorldState, Parent->Cost + Action->ActionCost);
|
||||||
|
|
||||||
|
if (NewWorldState["PlayerHealth"] <= 0)
|
||||||
|
{
|
||||||
|
SuccessfulLeaves.Add(NewNode);
|
||||||
|
bFoundAPlan = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TArray<UActionNode*> NewLeaves;
|
||||||
|
if (BuildActionGraph(NewNode, NewLeaves, NewWorldState, AgentGoals))
|
||||||
|
{
|
||||||
|
SuccessfulLeaves.Append(NewLeaves);
|
||||||
|
bFoundAPlan = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bFoundAPlan;
|
||||||
|
}
|
||||||
|
|
||||||
// Called every frame
|
// Called every frame
|
||||||
void UGOAPAgent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
void UGOAPAgent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||||
{
|
{
|
||||||
@ -33,3 +86,42 @@ void UGOAPAgent::TickComponent(float DeltaTime, ELevelTick TickType, FActorCompo
|
|||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TArray<UGOAPAction*> UGOAPAgent::Plan(TMap<FString, int> WorldState, TMap<FString, int> AgentGoals)
|
||||||
|
{
|
||||||
|
TArray<UGOAPAction*> ActionPlan;
|
||||||
|
UActionNode* RootActionNode = NewObject<UActionNode>();
|
||||||
|
RootActionNode->Init(nullptr, nullptr, WorldState, 0);
|
||||||
|
TArray<UActionNode*> SuccessfulLeaves;
|
||||||
|
|
||||||
|
if (BuildActionGraph(RootActionNode, SuccessfulLeaves, WorldState, AgentGoals))
|
||||||
|
{
|
||||||
|
//Find the cheapest leaf
|
||||||
|
float LowestRunningCost = 9999999;
|
||||||
|
UActionNode* CheapestLeaf = nullptr;
|
||||||
|
for (UActionNode* Leaf : SuccessfulLeaves)
|
||||||
|
{
|
||||||
|
if (Leaf->Cost < LowestRunningCost)
|
||||||
|
{
|
||||||
|
LowestRunningCost = Leaf->Cost;
|
||||||
|
CheapestLeaf = Leaf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the action plan
|
||||||
|
TArray<UActionNode*> Result;
|
||||||
|
UActionNode* ActionNode = CheapestLeaf;
|
||||||
|
while (ActionNode != nullptr)
|
||||||
|
{
|
||||||
|
Result.Add(ActionNode);
|
||||||
|
ActionNode = ActionNode->Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Reverse the array and only get the actions
|
||||||
|
for (int i = Result.Num() - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
ActionPlan.Add(Result[i]->Action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionPlan;
|
||||||
|
}
|
||||||
|
@ -3,9 +3,30 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
|
#include "GOAPAction.h"
|
||||||
|
#include "COMP250_1_2101327_AI/TurnBasedCombatV2/TurnBaseCombatV2.h"
|
||||||
#include "Components/ActorComponent.h"
|
#include "Components/ActorComponent.h"
|
||||||
#include "GOAPAgent.generated.h"
|
#include "GOAPAgent.generated.h"
|
||||||
|
|
||||||
|
UCLASS()
|
||||||
|
class UActionNode : public UObject
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UActionNode* Parent;
|
||||||
|
UGOAPAction* Action;
|
||||||
|
TMap<FString, int> State;
|
||||||
|
float Cost;
|
||||||
|
|
||||||
|
void Init(UActionNode* ParentNode, UGOAPAction* GOAPAction, TMap<FString, int> ProceduralState, float TotalCost)
|
||||||
|
{
|
||||||
|
Parent = ParentNode;
|
||||||
|
Action = GOAPAction;
|
||||||
|
State = ProceduralState;
|
||||||
|
Cost = TotalCost;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
|
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
|
||||||
class COMP250_1_2101327_AI_API UGOAPAgent : public UActorComponent
|
class COMP250_1_2101327_AI_API UGOAPAgent : public UActorComponent
|
||||||
@ -16,11 +37,32 @@ public:
|
|||||||
// Sets default values for this component's properties
|
// Sets default values for this component's properties
|
||||||
UGOAPAgent();
|
UGOAPAgent();
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere)
|
||||||
|
TArray<UGOAPAction*> AvailableActions;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
TMap<FString, int> Goals
|
||||||
|
{
|
||||||
|
{"PlayerHealth", 0}
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Called when the game starts
|
// Called when the game starts
|
||||||
virtual void BeginPlay() override;
|
virtual void BeginPlay() override;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
ATurnBaseCombatV2* CombatSystem;
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
bool BuildActionGraph(UActionNode* Parent, TArray<UActionNode*>& SuccessfulLeaves, TMap<FString, int> WorldState, TMap<FString, int> AgentGoals);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Called every frame
|
// Called every frame
|
||||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
TArray<UGOAPAction*> Plan(TMap<FString, int> WorldState, TMap<FString, int> AgentGoals);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
TMap<FString, int> GetWorldState();
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,7 @@ public:
|
|||||||
APlayerCharacter();
|
APlayerCharacter();
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||||
float Health = 100.0f;
|
int Health = 100.0f;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Called when the game starts or when spawned
|
// Called when the game starts or when spawned
|
||||||
|
@ -24,7 +24,7 @@ public:
|
|||||||
ATurnBaseCombatV2();
|
ATurnBaseCombatV2();
|
||||||
|
|
||||||
int* EnemyHealth = nullptr;
|
int* EnemyHealth = nullptr;
|
||||||
float* PlayerHealth = nullptr;
|
int* PlayerHealth = nullptr;
|
||||||
UPROPERTY(EditDefaultsOnly)
|
UPROPERTY(EditDefaultsOnly)
|
||||||
int DefaultActionPoints = 3;
|
int DefaultActionPoints = 3;
|
||||||
UPROPERTY(EditDefaultsOnly)
|
UPROPERTY(EditDefaultsOnly)
|
||||||
|
Loading…
Reference in New Issue
Block a user