-
Notifications
You must be signed in to change notification settings - Fork 0
created initial setup and algo for agg system #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| { | ||
| "configurations": [ | ||
| { | ||
| "name": "Win32", | ||
| "includePath": [ | ||
| "${workspaceFolder}/**" | ||
| ], | ||
| "defines": [ | ||
| "_DEBUG", | ||
| "UNICODE", | ||
| "_UNICODE" | ||
| ], | ||
| "windowsSdkVersion": "10.0.18362.0", | ||
| "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/bin/Hostx64/x64/cl.exe", | ||
| "cStandard": "c11", | ||
| "cppStandard": "c++17", | ||
| "intelliSenseMode": "msvc-x64" | ||
| } | ||
| ], | ||
| "version": 4 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| // Copyright 2018-2019 The Janus Project | 2034 Complex LLC. All Rights Reserved. | ||
|
|
||
| using UnrealBuildTool; | ||
|
|
||
| public class NujasAI : ModuleRules | ||
| { | ||
| public NujasAI(ReadOnlyTargetRules Target) : base(Target) | ||
| { | ||
| PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; | ||
|
|
||
| PublicIncludePaths.AddRange( | ||
| new string[] { | ||
| // ... add public include paths required here ... | ||
| } | ||
| ); | ||
|
|
||
|
|
||
| PrivateIncludePaths.AddRange( | ||
| new string[] { | ||
| // ... add other private include paths required here ... | ||
| "NujasAI/Private" | ||
| } | ||
| ); | ||
|
|
||
|
|
||
| PublicDependencyModuleNames.AddRange( | ||
| new string[] | ||
| { | ||
| "Core", | ||
| // ... add other public dependencies that you statically link with here ... | ||
| } | ||
| ); | ||
|
|
||
|
|
||
| PrivateDependencyModuleNames.AddRange( | ||
| new string[] | ||
| { | ||
| "CoreUObject", | ||
| "Engine", | ||
| "Slate", | ||
| "SlateCore", | ||
| // ... add private dependencies that you statically link with here ... | ||
| } | ||
| ); | ||
|
|
||
|
|
||
| DynamicallyLoadedModuleNames.AddRange( | ||
| new string[] | ||
| { | ||
| // ... add any modules that your module loads dynamically here ... | ||
| } | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| // Copyright 2018-2019 The Janus Project | 2034 Complex LLC. All Rights Reserved. | ||
|
|
||
| #include "AggressionScoreComponent.h" | ||
| #include "GameFramework/Character.h" | ||
| #include "Kismet/GameplayStatics.h" | ||
| #include "NujasAIGlobals.h" | ||
| #include "TimerManager.h" | ||
|
|
||
| UAggressionScoreComponent::UAggressionScoreComponent() | ||
| { | ||
| PrimaryComponentTick.bCanEverTick = false; | ||
| } | ||
|
|
||
| void UAggressionScoreComponent::BeginPlay() | ||
| { | ||
| Super::BeginPlay(); | ||
| Owner = Cast<ACharacter>(this->GetOwner()); | ||
| InvalidateAggressionScoreTimer(); | ||
| } | ||
|
|
||
| void UAggressionScoreComponent::BeginAggressionSystem(const TArray<AActor*> BotsToConsider) | ||
| { | ||
| if(Owner) | ||
| { | ||
| Bots = BotsToConsider; | ||
|
|
||
| Owner->GetWorldTimerManager().SetTimer | ||
| ( | ||
| AggressionScoreHandle, | ||
| this, | ||
| &UAggressionScoreComponent::CalculateAggressionScores, | ||
| 1.f, | ||
| true | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| void UAggressionScoreComponent::StopAggressionSystem() | ||
| { | ||
| InvalidateAggressionScoreTimer(); | ||
| HeapAggresiveActors.Empty(); | ||
| Bots.Empty(); | ||
| } | ||
|
|
||
| void UAggressionScoreComponent::AddBot(AActor* const Bot) | ||
| { | ||
| Bots.Add(Bot); | ||
| } | ||
|
|
||
| void UAggressionScoreComponent::CalculateAggressionScores() | ||
| { | ||
| if (Bots.Num() > 0) | ||
| { | ||
| HeapAggresiveActors.Empty(); | ||
|
|
||
| // do the aggression scoring here | ||
| for (AActor* const Bot : Bots) | ||
| { | ||
| /* | ||
| * Removing a specific bot from anywhere in the list is expensive. | ||
| * Better to just check if still valid. | ||
| * The list doesn't grow larger than 20 items per battle. | ||
| */ | ||
| if (!Bot) continue; | ||
| float Score = IAggressive::Execute_CanBecomeAggressive(Bot) ? 1.0 : 0.0; | ||
| Score += IAggressive::Execute_GetAggressionPriority(Bot); | ||
|
|
||
| if (IsPlayerCharacter) | ||
| { | ||
| // is bot on screen? (Viewport utility) | ||
| // angle from camera? | ||
| } | ||
|
|
||
| // distance from actor | ||
|
|
||
| HeapAggresiveActors.Add(FAggressiveActorEntry(Bot, Score)); | ||
|
|
||
| } | ||
|
|
||
| // heapify | ||
|
|
||
| // Pop items and make them aggressive until run out of tokens | ||
|
|
||
| // rest should be made calm | ||
| } | ||
| } | ||
|
|
||
| inline void UAggressionScoreComponent::InvalidateAggressionScoreTimer() | ||
| { | ||
| if (Owner) | ||
| { | ||
| Owner->GetWorldTimerManager().ClearTimer(AggressionScoreHandle); | ||
| } | ||
| } | ||
|
|
||
| FAggressiveActorEntry::FAggressiveActorEntry() | ||
| { | ||
| this->Score = 0; | ||
| this->Bot = nullptr; | ||
| } | ||
|
|
||
| FAggressiveActorEntry::FAggressiveActorEntry(AActor* Bot, float Score) | ||
| { | ||
| this->Bot = Bot; | ||
| this->Score = Score; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // Copyright 2018-2019 The Janus Project | 2034 Complex LLC. All Rights Reserved. | ||
|
|
||
| #include "NujasAIModule.h" | ||
|
|
||
| #define LOCTEXT_NAMESPACE "FNujasAIModule" | ||
|
|
||
| void FNujasAIModule::StartupModule() | ||
| { | ||
| // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module | ||
| } | ||
|
|
||
| void FNujasAIModule::ShutdownModule() | ||
| { | ||
| // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, | ||
| // we call this function before unloading the module. | ||
| } | ||
|
|
||
| #undef LOCTEXT_NAMESPACE | ||
|
|
||
| IMPLEMENT_MODULE(FNujasAIModule, NujasAI) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| #pragma once | ||
|
|
||
| #include "CoreMinimal.h" | ||
| #include "Components/ActorComponent.h" | ||
| #include "AggressionScoreComponent.generated.h" | ||
|
|
||
|
|
||
| class ACharacter; | ||
|
|
||
| USTRUCT() | ||
| struct FAggressiveActorEntry | ||
| { | ||
| GENERATED_BODY() | ||
|
|
||
| FAggressiveActorEntry(); | ||
| FAggressiveActorEntry(AActor* Bot, float Score); | ||
|
|
||
| AActor* Bot; | ||
| float Score; | ||
| }; | ||
|
|
||
| static const uint32 DEFAULT_AGGRESSION_TOKENS = 14; | ||
|
|
||
| UCLASS(ClassGroup = (NujasAI), meta = (BlueprintSpawnableComponent), HideCategories=(Tags, Cooking, Collision, ComponentReplication, Activation, AssetUserData)) | ||
| class NUJASAI_API UAggressionScoreComponent : public UActorComponent | ||
| { | ||
| GENERATED_BODY() | ||
|
|
||
| // each enemy with a specific interface should be evaluated periodically (on a timer) by this component | ||
| // | ||
| // each AI has the opportunity to become aggressive based on parameters listed below | ||
| // by default, every AI must not be aggressive | ||
| // | ||
| // each timer iteration all enemies with the Aggressive Interface must be collected, regardless of whether they are aggressive or not | ||
| // every collected actor will be re-evaluated on whether they should remain aggressive or not | ||
| // | ||
| // every ai has an aggression score count and the count will be evaluated based on there paramters: | ||
| // - Primary -> Can I become aggressive? (not in hit reaction or in a state blocking them indefinetely) | ||
| // - Secondary -> Aggression Rank based on distance to target | ||
| // - Misc -> Action Rank: a) "on/off screen" b) angle "from camera" (needs to be toggled) c) distance from target | ||
| // | ||
| // once calculated, send it to a max heap of structs that stores the aggression score and the object | ||
| // once every enemy is in the heap, pop the actors and force them into aggression until run out of available tokens | ||
| // | ||
| // every other actor should be immediately set to calm | ||
|
|
||
| // todo: add player current target later when start revamping the targeting mechanic | ||
|
|
||
| void CalculateAggressionScores(); | ||
|
|
||
| inline void InvalidateAggressionScoreTimer(); | ||
|
|
||
| UPROPERTY() | ||
| // heapify array to sort bots based on their aggression score | ||
| TArray<FAggressiveActorEntry> HeapAggresiveActors; | ||
|
|
||
| UPROPERTY() | ||
| // the bots that will be considered for calculating the aggression score | ||
| TArray<AActor*> Bots; | ||
|
|
||
| UPROPERTY() | ||
| FTimerHandle AggressionScoreHandle; | ||
|
|
||
| UPROPERTY() | ||
| ACharacter* Owner; | ||
|
|
||
| public: | ||
| // Sets default values for this component's properties | ||
| UAggressionScoreComponent(); | ||
|
|
||
| UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Aggression Params", meta = (ClampMin = "0", ClampMax = "100")) | ||
| // How many tokens can the aggression system give out for Bots to become aggressive | ||
| int32 MaxAggressionTokens = DEFAULT_AGGRESSION_TOKENS; | ||
|
|
||
| UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Aggression Params") | ||
| // Is this component attached to the player character | ||
| bool IsPlayerCharacter = true; | ||
|
|
||
| protected: | ||
| // Called when the game starts | ||
| virtual void BeginPlay() override; | ||
|
|
||
| public: | ||
| UFUNCTION(BlueprintCallable, Category = "Aggression") | ||
| void BeginAggressionSystem(const TArray<AActor*> BotsToConsider); | ||
|
|
||
| UFUNCTION(BlueprintCallable, Category = "Aggression") | ||
| void StopAggressionSystem(); | ||
|
|
||
| UFUNCTION(BlueprintCallable, Category = "Aggression") | ||
| /* | ||
| * The system can only add bots. | ||
| * When the encounter is over, the system will clean out leftover garbage. | ||
| */ | ||
| void AddBot(AActor* const Bot); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| // Copyright 2018-2019 The Janus Project | 2034 Complex LLC. All Rights Reserved. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "Interface.h" | ||
| #include "NujasAIGlobals.generated.h" | ||
|
|
||
| UINTERFACE(Blueprintable) | ||
| class NUJASAI_API UAggressive : public UInterface | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are a total of 96 results for There are 59 resutls for This shows that the naming convention with the variable name postfixed with Since the PlatformInterface we have is also using the majority convention: https://github.com/nujas/TheJanusFramework/blob/f7f455342b102c86f840e0c201f9d1dbb2295e33/Source/NujasCore/Public/PlatformInfoInterface.h I would suggest rename this to |
||
| { | ||
| GENERATED_BODY() | ||
| }; | ||
|
|
||
| class IAggressive | ||
| { | ||
| GENERATED_BODY() | ||
|
|
||
| public: | ||
| // callback for when the actor turns aggressive | ||
| UFUNCTION(BlueprintImplementableEvent, Category = "Aggressive") | ||
| bool OnTurAgressive(); | ||
|
|
||
| // callback when the actor is deemed none-aggressive | ||
| UFUNCTION(BlueprintImplementableEvent, Category = "Aggressive") | ||
| bool OnTurnCalm(); | ||
|
|
||
| // helper func to determine the AI's priority. The priority is usually set manually depending on the AI | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This value is also set based on distance. i.e the designer could say that a draugr's priority at 5 meters away from the target is 10 but at 100 meters the priority is 1.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That value is determined on the Bot's side. This function just returns it |
||
| UFUNCTION(BlueprintImplementableEvent, Category = "Aggressive") | ||
| int32 GetAggressionPriority(); | ||
|
|
||
| // helper func to tell how many tokens does an AI take. | ||
| UFUNCTION(BlueprintImplementableEvent, Category = "Aggressive") | ||
| int32 GetTokenTax(); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A static number that a designer could set on the Bot's side. Usually, the Tax should never change during the runtime of the game |
||
|
|
||
| // helper func to determine if the actor is in a state that would prevent them from becoming aggressive | ||
| UFUNCTION(BlueprintImplementableEvent, Category = "Aggressive") | ||
| bool CanBecomeAggressive(); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Evaluated Bot Side. Examples when the AI cannot become aggressive: |
||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| // Copyright 2018-2019 The Janus Project | 2034 Complex LLC. All Rights Reserved. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "CoreMinimal.h" | ||
| #include "Modules/ModuleManager.h" | ||
|
|
||
| class FNujasAIModule : public IModuleInterface | ||
| { | ||
| public: | ||
| /** IModuleInterface implementation */ | ||
| virtual void StartupModule() override; | ||
| virtual void ShutdownModule() override; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really necessary to heapify. I could just sort them based on their priority and be done with it