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); | 
 | ||||||
|  | 	UFUNCTION() | ||||||
|  | 	bool CheckPreConditions(TMap<FString, int> WorldState); | ||||||
|  | 	UFUNCTION() | ||||||
|  | 	TMap<FString, int> ApplyEffects(TMap<FString, int> WorldState); | ||||||
| 	 | 	 | ||||||
| protected: | 	UPROPERTY() | ||||||
| 	TMap<FString, int32> PreConditions; | 	TMap<FString, int> PreConditions; | ||||||
| 	TMap<FString, int32> Effects; | 	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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user