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 "Actions/Combo_P.h"
#include "Actions/DefaultAttack.h"
// Sets default values for this component's properties
@ -23,30 +22,21 @@ void UGOAPAgent::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()
//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)
{
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;
UGOAPAction* Action = NewObject<UGOAPAction>(GetWorld(), ActionClass);
Action->Init();
AvailableActions.Add(Action);
}
bool UGOAPAgent::BuildActionGraph(UActionNode* Parent, TArray<UActionNode*>& SuccessfulLeaves, TMap<FString, int> WorldState, TMap<FString, int> AgentGoals)
//Sort AvailableActions by cost
AvailableActions.Sort([](const UGOAPAction& A, const UGOAPAction& B) { return A.ActionCost < B.ActionCost; });
}
bool UGOAPAgent::BuildActionGraph(UActionNode* Parent, TArray<UActionNode*>& SuccessfulLeaves, UWorldState* WorldState, TMap<FString, int> AgentGoals)
{
bool bFoundAPlan = false;
@ -54,17 +44,18 @@ bool UGOAPAgent::BuildActionGraph(UActionNode* Parent, TArray<UActionNode*>& Suc
{
if (Action->CheckPreConditions(WorldState))
{
TMap<FString, int> NewWorldState = Action->ApplyEffects(WorldState);
UWorldState* NewWorldState = Action->ApplyEffects(WorldState);
UActionNode* NewNode = NewObject<UActionNode>();
NewNode->Init(Parent, Action, NewWorldState, Parent->Cost + Action->ActionCost);
if (NewWorldState["PlayerHealth"] <= 0)
if (NewWorldState->State["PlayerHealth"] <= 0)
{
SuccessfulLeaves.Add(NewNode);
bFoundAPlan = true;
}
else
else if (!SeenStates.Contains(NewWorldState) && !bFoundAPlan)
{
SeenStates.Add(NewWorldState);
TArray<UActionNode*> NewLeaves;
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;
}
@ -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;
UActionNode* RootActionNode = NewObject<UActionNode>();
RootActionNode->Init(nullptr, nullptr, WorldState, 0);
TArray<UActionNode*> SuccessfulLeaves;
SeenStates.Empty();
SeenStates.Add(WorldState);
if (BuildActionGraph(RootActionNode, SuccessfulLeaves, WorldState, AgentGoals))
{
//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;
}

View File

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