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"
|
||||
|
||||
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[PreCondition.Key] != PreCondition.Value) return false;
|
||||
if (WorldState[PreCondition.Key] < PreCondition.Value) return false;
|
||||
}
|
||||
}
|
||||
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))
|
||||
{
|
||||
WorldState[Effect.Key] = Effect.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
WorldState.Add(Effect.Key, Effect.Value);
|
||||
WorldState[Effect.Key] -= Effect.Value;
|
||||
}
|
||||
}
|
||||
return WorldState;
|
||||
|
@ -15,10 +15,18 @@ class COMP250_1_2101327_AI_API UGOAPAction : public UObject
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual bool CheckPreConditions(TMap<FString, int32> WorldState);
|
||||
TMap<FString, int32> ApplyEffects(TMap<FString, int32> WorldState);
|
||||
float ActionCost = 1.0f;
|
||||
|
||||
UFUNCTION()
|
||||
bool CheckPreConditions(TMap<FString, int> WorldState);
|
||||
UFUNCTION()
|
||||
TMap<FString, int> ApplyEffects(TMap<FString, int> WorldState);
|
||||
|
||||
protected:
|
||||
TMap<FString, int32> PreConditions;
|
||||
TMap<FString, int32> Effects;
|
||||
UPROPERTY()
|
||||
TMap<FString, int> PreConditions;
|
||||
UPROPERTY()
|
||||
TMap<FString, int> Effects;
|
||||
|
||||
UFUNCTION()
|
||||
virtual void Init(){}
|
||||
};
|
||||
|
@ -3,6 +3,9 @@
|
||||
|
||||
#include "GOAPAgent.h"
|
||||
|
||||
#include "Actions/Combo_P.h"
|
||||
#include "Actions/DefaultAttack.h"
|
||||
|
||||
|
||||
// Sets default values for this component's properties
|
||||
UGOAPAgent::UGOAPAgent()
|
||||
@ -20,11 +23,61 @@ 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()
|
||||
{
|
||||
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
|
||||
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
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GOAPAction.h"
|
||||
#include "COMP250_1_2101327_AI/TurnBasedCombatV2/TurnBaseCombatV2.h"
|
||||
#include "Components/ActorComponent.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))
|
||||
class COMP250_1_2101327_AI_API UGOAPAgent : public UActorComponent
|
||||
@ -16,11 +37,32 @@ public:
|
||||
// Sets default values for this component's properties
|
||||
UGOAPAgent();
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
TArray<UGOAPAction*> AvailableActions;
|
||||
|
||||
UPROPERTY()
|
||||
TMap<FString, int> Goals
|
||||
{
|
||||
{"PlayerHealth", 0}
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
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();
|
||||
};
|
||||
|
@ -16,7 +16,7 @@ public:
|
||||
APlayerCharacter();
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
float Health = 100.0f;
|
||||
int Health = 100.0f;
|
||||
|
||||
protected:
|
||||
// Called when the game starts or when spawned
|
||||
|
@ -24,7 +24,7 @@ public:
|
||||
ATurnBaseCombatV2();
|
||||
|
||||
int* EnemyHealth = nullptr;
|
||||
float* PlayerHealth = nullptr;
|
||||
int* PlayerHealth = nullptr;
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
int DefaultActionPoints = 3;
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
|
Loading…
Reference in New Issue
Block a user