Optimised GOAP Search

via Cancelling Previous States & Optimising Action Order
This commit is contained in:
Philip W 2023-03-22 05:37:21 +00:00
parent 08fbc2180d
commit 3121176310
4 changed files with 43 additions and 40 deletions

BIN
COMP250_1_2101327_AI/Content/Main.umap (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

View File

@ -4,7 +4,6 @@
#include "GOAPAgent.h" #include "GOAPAgent.h"
#include "Actions/Combo_P.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
@ -23,30 +22,21 @@ void UGOAPAgent::BeginPlay()
{ {
Super::BeginPlay(); Super::BeginPlay();
CombatSystem = Cast<ATurnBaseCombatV2>(GetWorld()->GetGameState()); //Gets all classes that inherit from UGOAPAction and adds them to the AvailableActions array
TArray<UClass*> ActionClasses;
GetDerivedClasses(UGOAPAction::StaticClass(), ActionClasses, false);
for (UClass* ActionClass : ActionClasses)
{
UGOAPAction* Action = NewObject<UGOAPAction>(GetWorld(), ActionClass);
Action->Init();
AvailableActions.Add(Action);
}
UGOAPAction* DefaultAttack = NewObject<UGOAPAction>(GetWorld(), UDefaultAttack::StaticClass()); //Sort AvailableActions by cost
DefaultAttack->Init(); AvailableActions.Sort([](const UGOAPAction& A, const UGOAPAction& B) { return A.ActionCost < B.ActionCost; });
AvailableActions.Add(DefaultAttack);
UGOAPAction* Combo_P = NewObject<UGOAPAction>(GetWorld(), UCombo_P::StaticClass());
Combo_P->Init();
AvailableActions.Add(Combo_P);
} }
bool UGOAPAgent::BuildActionGraph(UActionNode* Parent, TArray<UActionNode*>& SuccessfulLeaves, UWorldState* WorldState, TMap<FString, int> AgentGoals)
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; bool bFoundAPlan = false;
@ -54,17 +44,18 @@ bool UGOAPAgent::BuildActionGraph(UActionNode* Parent, TArray<UActionNode*>& Suc
{ {
if (Action->CheckPreConditions(WorldState)) if (Action->CheckPreConditions(WorldState))
{ {
TMap<FString, int> NewWorldState = Action->ApplyEffects(WorldState); UWorldState* NewWorldState = Action->ApplyEffects(WorldState);
UActionNode* NewNode = NewObject<UActionNode>(); UActionNode* NewNode = NewObject<UActionNode>();
NewNode->Init(Parent, Action, NewWorldState, Parent->Cost + Action->ActionCost); NewNode->Init(Parent, Action, NewWorldState, Parent->Cost + Action->ActionCost);
if (NewWorldState["PlayerHealth"] <= 0) if (NewWorldState->State["PlayerHealth"] <= 0)
{ {
SuccessfulLeaves.Add(NewNode); SuccessfulLeaves.Add(NewNode);
bFoundAPlan = true; bFoundAPlan = true;
} }
else else if (!SeenStates.Contains(NewWorldState) && !bFoundAPlan)
{ {
SeenStates.Add(NewWorldState);
TArray<UActionNode*> NewLeaves; TArray<UActionNode*> NewLeaves;
if (BuildActionGraph(NewNode, NewLeaves, NewWorldState, AgentGoals)) if (BuildActionGraph(NewNode, NewLeaves, NewWorldState, AgentGoals))
{ {
@ -75,6 +66,17 @@ bool UGOAPAgent::BuildActionGraph(UActionNode* Parent, TArray<UActionNode*>& Suc
} }
} }
//Log the plan
if (bFoundAPlan)
{
UE_LOG(LogTemp, Warning, TEXT("Found a plan!"));
// UActionNode* CurrentNode = SuccessfulLeaves[0];
// while (CurrentNode->Parent != nullptr)
// {
// UE_LOG(LogTemp, Warning, TEXT("%s"), *CurrentNode->Action->GetName());
// CurrentNode = CurrentNode->Parent;
// }
}
return bFoundAPlan; return bFoundAPlan;
} }
@ -86,13 +88,16 @@ void UGOAPAgent::TickComponent(float DeltaTime, ELevelTick TickType, FActorCompo
// ... // ...
} }
TArray<UGOAPAction*> UGOAPAgent::Plan(TMap<FString, int> WorldState, TMap<FString, int> AgentGoals) TArray<UGOAPAction*> UGOAPAgent::Plan(UWorldState* WorldState, TMap<FString, int> AgentGoals)
{ {
TArray<UGOAPAction*> ActionPlan; TArray<UGOAPAction*> ActionPlan;
UActionNode* RootActionNode = NewObject<UActionNode>(); UActionNode* RootActionNode = NewObject<UActionNode>();
RootActionNode->Init(nullptr, nullptr, WorldState, 0); RootActionNode->Init(nullptr, nullptr, WorldState, 0);
TArray<UActionNode*> SuccessfulLeaves; TArray<UActionNode*> SuccessfulLeaves;
SeenStates.Empty();
SeenStates.Add(WorldState);
if (BuildActionGraph(RootActionNode, SuccessfulLeaves, WorldState, AgentGoals)) if (BuildActionGraph(RootActionNode, SuccessfulLeaves, WorldState, AgentGoals))
{ {
//Find the cheapest leaf //Find the cheapest leaf
@ -123,5 +128,7 @@ TArray<UGOAPAction*> UGOAPAgent::Plan(TMap<FString, int> WorldState, TMap<FStrin
} }
} }
//Remove the first action as it is null due to being the root node
ActionPlan.RemoveAt(0);
return ActionPlan; return ActionPlan;
} }

View File

@ -4,8 +4,9 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "GOAPAction.h" #include "GOAPAction.h"
#include "COMP250_1_2101327_AI/TurnBasedCombatV2/TurnBaseCombatV2.h"
#include "Components/ActorComponent.h" #include "Components/ActorComponent.h"
#include "Containers/UnrealString.h"
#include "WorldState.h"
#include "GOAPAgent.generated.h" #include "GOAPAgent.generated.h"
UCLASS() UCLASS()
@ -16,10 +17,10 @@ class UActionNode : public UObject
public: public:
UActionNode* Parent; UActionNode* Parent;
UGOAPAction* Action; UGOAPAction* Action;
TMap<FString, int> State; UWorldState* State;
float Cost; float Cost;
void Init(UActionNode* ParentNode, UGOAPAction* GOAPAction, TMap<FString, int> ProceduralState, float TotalCost) void Init(UActionNode* ParentNode, UGOAPAction* GOAPAction, UWorldState* ProceduralState, float TotalCost)
{ {
Parent = ParentNode; Parent = ParentNode;
Action = GOAPAction; Action = GOAPAction;
@ -50,19 +51,14 @@ protected:
// Called when the game starts // Called when the game starts
virtual void BeginPlay() override; virtual void BeginPlay() override;
UPROPERTY()
ATurnBaseCombatV2* CombatSystem;
UFUNCTION() UFUNCTION()
bool BuildActionGraph(UActionNode* Parent, TArray<UActionNode*>& SuccessfulLeaves, TMap<FString, int> WorldState, TMap<FString, int> AgentGoals); bool BuildActionGraph(UActionNode* Parent, TArray<UActionNode*>& SuccessfulLeaves, UWorldState* WorldState, TMap<FString, int> AgentGoals);
TSet<UWorldState*> SeenStates;
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() UFUNCTION()
TArray<UGOAPAction*> Plan(TMap<FString, int> WorldState, TMap<FString, int> AgentGoals); TArray<UGOAPAction*> Plan(UWorldState* WorldState, TMap<FString, int> AgentGoals);
UFUNCTION()
TMap<FString, int> GetWorldState();
}; };