Added Comments to GOAP Functions

This commit is contained in:
Philip W 2023-03-23 01:11:25 +00:00
parent 4639a4d1ac
commit 2b43f174a2
3 changed files with 48 additions and 18 deletions

View File

@ -3,6 +3,11 @@
#include "GOAPAction.h" #include "GOAPAction.h"
/**
* @brief Checks if the preconditions are met in the world state given
* @param WorldState A pointer to the world state to check
* @return True if the preconditions are met and false if not
*/
bool UGOAPAction::CheckPreConditions(UWorldState* WorldState) bool UGOAPAction::CheckPreConditions(UWorldState* WorldState)
{ {
for (TPair<FString, int> PreCondition : PreConditions) for (TPair<FString, int> PreCondition : PreConditions)
@ -15,6 +20,11 @@ bool UGOAPAction::CheckPreConditions(UWorldState* WorldState)
return true; return true;
} }
/**
* @brief Applies the effects of the action to the world state given
* @param WorldState A pointer to the world state to apply the effects to
* @return A pointer to the new world state
*/
UWorldState* UGOAPAction::ApplyEffects(UWorldState* WorldState) UWorldState* UGOAPAction::ApplyEffects(UWorldState* WorldState)
{ {
UWorldState* NewWorldState = NewObject<UWorldState>(); UWorldState* NewWorldState = NewObject<UWorldState>();
@ -29,6 +39,10 @@ UWorldState* UGOAPAction::ApplyEffects(UWorldState* WorldState)
return NewWorldState; return NewWorldState;
} }
/**
* @brief Performs the action
* @return A map of the effects of the action
*/
TMap<FString, int> UGOAPAction::Perform() TMap<FString, int> UGOAPAction::Perform()
{ {
return Effects; return Effects;

View File

@ -32,10 +32,18 @@ void UGOAPAgent::BeginPlay()
AvailableActions.Add(Action); AvailableActions.Add(Action);
} }
//Sort AvailableActions by cost //Sorts AvailableActions by cost to optimise the search by choosing the cheapest action first
AvailableActions.Sort([](const UGOAPAction& A, const UGOAPAction& B) { return A.ActionCost < B.ActionCost; }); AvailableActions.Sort([](const UGOAPAction& A, const UGOAPAction& B) { return A.ActionCost < B.ActionCost; });
} }
/**
* @brief Recursive function to build a tree of possible actions
* @param Parent The parent node containing the previous action or null if this is the root node
* @param SuccessfulLeaves The array of nodes that lead to a successful goal state
* @param WorldState The state of the world with it's applicable effects applied
* @param AgentGoals The goals of the agent
* @return True if a plan was found and false if not
*/
bool UGOAPAgent::BuildActionGraph(UActionNode* Parent, TArray<UActionNode*>& SuccessfulLeaves, UWorldState* WorldState, TMap<FString, int> AgentGoals) bool UGOAPAgent::BuildActionGraph(UActionNode* Parent, TArray<UActionNode*>& SuccessfulLeaves, UWorldState* WorldState, TMap<FString, int> AgentGoals)
{ {
bool bFoundAPlan = false; bool bFoundAPlan = false;
@ -48,12 +56,24 @@ bool UGOAPAgent::BuildActionGraph(UActionNode* Parent, TArray<UActionNode*>& Suc
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->State["PlayerHealth"] <= 0) //Check if the new state meets the agent's goals
bool bMetGoals = true;
for (TTuple<FString, int> Goal : AgentGoals)
{
if (NewWorldState->State[Goal.Key] > Goal.Value)
{
bMetGoals = false;
break;
}
}
if (bMetGoals)
{ {
SuccessfulLeaves.Add(NewNode); SuccessfulLeaves.Add(NewNode);
bFoundAPlan = true; bFoundAPlan = true;
} }
else if (!SeenStates.Contains(NewWorldState) && !bFoundAPlan)
//Check if the new state has been seen before and if a plan has been found otherwise recurse
if (!SeenStates.Contains(NewWorldState) && !bFoundAPlan)
{ {
SeenStates.Add(NewWorldState); SeenStates.Add(NewWorldState);
TArray<UActionNode*> NewLeaves; TArray<UActionNode*> NewLeaves;
@ -66,17 +86,6 @@ 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;
} }
@ -88,6 +97,13 @@ void UGOAPAgent::TickComponent(float DeltaTime, ELevelTick TickType, FActorCompo
// ... // ...
} }
/**
* @brief Creates a plan of actions to achieve the agent's goals
* @param WorldState The current state of the world
* @param AgentGoals The goals of the agent
* @return An array of actions to achieve the agent's goals efficiently with it's current resources or
* an empty array if no plan was found
*/
TArray<UGOAPAction*> UGOAPAgent::Plan(UWorldState* WorldState, TMap<FString, int> AgentGoals) TArray<UGOAPAction*> UGOAPAgent::Plan(UWorldState* WorldState, TMap<FString, int> AgentGoals)
{ {
TArray<UGOAPAction*> ActionPlan; TArray<UGOAPAction*> ActionPlan;
@ -128,7 +144,7 @@ TArray<UGOAPAction*> UGOAPAgent::Plan(UWorldState* WorldState, TMap<FString, int
} }
} }
//Remove the first action as it is null due to being the root node //Remove the first action as it's action is null due to being the root node
ActionPlan.RemoveAt(0); ActionPlan.RemoveAt(0);
return ActionPlan; return ActionPlan;
} }

View File

@ -534,7 +534,7 @@ void ATurnBaseCombatV2::StealButtonOnClick()
{ {
StealButton->SetIsEnabled(false); StealButton->SetIsEnabled(false);
ClearActionPlanWidget(); ClearActionPlanWidget();
TArray<int*> Resources = {&EnemyProbertiumResource, &EnemyEisResource, &EnemyAzosResource, &EnemyIroquoidResource}; TArray Resources = {&EnemyProbertiumResource, &EnemyEisResource, &EnemyAzosResource, &EnemyIroquoidResource};
Resources.Sort([](const int& A, const int& B) { return A > B; }); Resources.Sort([](const int& A, const int& B) { return A > B; });
for (int* Resource : Resources) for (int* Resource : Resources)
{ {