From 3a729caab0dd98a5b6716d21742b8032f2c902ef Mon Sep 17 00:00:00 2001 From: baz Date: Thu, 22 Aug 2024 22:11:24 +0100 Subject: [PATCH] Add Enemy Batching --- Source/vampires/EnemyCharacter.cpp | 28 ++++++---- Source/vampires/EnemyCharacter.h | 12 +++-- Source/vampires/HealthComponent.cpp | 3 +- Source/vampires/ObjectPoolComponent.cpp | 5 ++ Source/vampires/ObjectPoolComponent.h | 21 ++++++++ Source/vampires/ObjectPoolManager.cpp | 69 +++++++++++++++++++++++++ Source/vampires/ObjectPoolManager.h | 30 +++++++++++ Source/vampires/VampireAIController.cpp | 4 +- Source/vampires/VampireGameMode.cpp | 36 +++++++++---- Source/vampires/VampireGameMode.h | 8 ++- 10 files changed, 187 insertions(+), 29 deletions(-) create mode 100644 Source/vampires/ObjectPoolComponent.cpp create mode 100644 Source/vampires/ObjectPoolComponent.h create mode 100644 Source/vampires/ObjectPoolManager.cpp create mode 100644 Source/vampires/ObjectPoolManager.h diff --git a/Source/vampires/EnemyCharacter.cpp b/Source/vampires/EnemyCharacter.cpp index 00989ab..9bf6088 100644 --- a/Source/vampires/EnemyCharacter.cpp +++ b/Source/vampires/EnemyCharacter.cpp @@ -8,6 +8,7 @@ AEnemyCharacter::AEnemyCharacter(const FObjectInitializer& ObjectInitializer) { + ObjectPoolComponent = CreateDefaultSubobject(TEXT("Object Pool Component")); } void AEnemyCharacter::BeginPlay() @@ -15,6 +16,8 @@ void AEnemyCharacter::BeginPlay() Super::BeginPlay(); GetHealthComponent()->OnDamaged.BindUFunction(this, "OnDamaged"); GetHealthComponent()->OnDeath.BindUFunction(this, "OnDeath"); + + ObjectPoolComponent->OnRetrieve.BindUFunction(this, "ResetHealth"); } void AEnemyCharacter::Tick(float DeltaTime) @@ -33,17 +36,20 @@ void AEnemyCharacter::OnDamaged() void AEnemyCharacter::OnDeath() { - //if (IsValid(EXPPickupTemplate)) - //{ - FActorSpawnParameters actorSpawnParameters; - actorSpawnParameters.Owner = this; - actorSpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; - actorSpawnParameters.TransformScaleMethod = ESpawnActorScaleMethod::MultiplyWithRoot; + FActorSpawnParameters actorSpawnParameters; + actorSpawnParameters.Owner = this; + actorSpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; + actorSpawnParameters.TransformScaleMethod = ESpawnActorScaleMethod::MultiplyWithRoot; - GetWorld()->SpawnActor(EXPPickupTemplate, GetActorLocation(), FRotator::ZeroRotator, - actorSpawnParameters); + GetWorld()->SpawnActor(EXPPickupTemplate, GetActorLocation(), FRotator::ZeroRotator, + actorSpawnParameters); - AVampireGameMode* gamemode = Cast(UGameplayStatics::GetGameMode(GetWorld())); - gamemode->IncrementEnemyDeathCount(); - //} + AVampireGameMode* gamemode = Cast(UGameplayStatics::GetGameMode(GetWorld())); + gamemode->IncrementEnemyDeathCount(); + gamemode->GetEnemyObjectPoolManager()->ReturnObject(this); +} + +void AEnemyCharacter::ResetHealth() +{ + GetHealthComponent()->ResetHealth(); } diff --git a/Source/vampires/EnemyCharacter.h b/Source/vampires/EnemyCharacter.h index a1af1e2..b26a27c 100644 --- a/Source/vampires/EnemyCharacter.h +++ b/Source/vampires/EnemyCharacter.h @@ -4,6 +4,7 @@ #include "CoreMinimal.h" #include "EXPPickup.h" +#include "ObjectPoolComponent.h" #include "VampireCharacter.h" #include "BehaviorTree/BehaviorTree.h" #include "EnemyCharacter.generated.h" @@ -19,12 +20,13 @@ class VAMPIRES_API AEnemyCharacter : public AVampireCharacter public: UPROPERTY(EditDefaultsOnly) TSubclassOf EXPPickupTemplate = nullptr; - -private: +private: UPROPERTY(EditDefaultsOnly, Meta = (AllowPrivateAccess = "true")) UBehaviorTree* BehaviorTree = nullptr; - + + UObjectPoolComponent* ObjectPoolComponent = nullptr; + public: AEnemyCharacter(const FObjectInitializer& ObjectInitializer); @@ -41,4 +43,8 @@ public: UFUNCTION() virtual void OnDeath(); + +private: + UFUNCTION() + void ResetHealth(); }; diff --git a/Source/vampires/HealthComponent.cpp b/Source/vampires/HealthComponent.cpp index 93a8d8e..7dca1a1 100644 --- a/Source/vampires/HealthComponent.cpp +++ b/Source/vampires/HealthComponent.cpp @@ -70,6 +70,7 @@ void UHealthComponent::SetCurrentHealth(float value) void UHealthComponent::ResetHealth() { CurrentHealth = MaxHealth; + IsDead = false; } void UHealthComponent::RecoverHealth(float value) @@ -105,4 +106,4 @@ void UHealthComponent::BeginPlay() Super::BeginPlay(); ResetHealth(); -} \ No newline at end of file +} diff --git a/Source/vampires/ObjectPoolComponent.cpp b/Source/vampires/ObjectPoolComponent.cpp new file mode 100644 index 0000000..b6768fb --- /dev/null +++ b/Source/vampires/ObjectPoolComponent.cpp @@ -0,0 +1,5 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ObjectPoolComponent.h" + diff --git a/Source/vampires/ObjectPoolComponent.h b/Source/vampires/ObjectPoolComponent.h new file mode 100644 index 0000000..d6b09ed --- /dev/null +++ b/Source/vampires/ObjectPoolComponent.h @@ -0,0 +1,21 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "ObjectPoolComponent.generated.h" + + +DECLARE_DELEGATE(FOnRetrieve) +DECLARE_DELEGATE(FOnReturn) + +UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent)) +class VAMPIRES_API UObjectPoolComponent : public UActorComponent +{ + GENERATED_BODY() + +public: + FOnRetrieve OnRetrieve; + FOnReturn OnReturn; +}; diff --git a/Source/vampires/ObjectPoolManager.cpp b/Source/vampires/ObjectPoolManager.cpp new file mode 100644 index 0000000..2dc0da0 --- /dev/null +++ b/Source/vampires/ObjectPoolManager.cpp @@ -0,0 +1,69 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ObjectPoolManager.h" + +#include "ObjectPoolComponent.h" + +// Called when the game starts or when spawned +void AObjectPoolManager::BeginPlay() +{ + Super::BeginPlay(); +} + +void AObjectPoolManager::InitializeObjectPool(TSubclassOf Object, const int InitialObjectPoolSize) +{ + for (int i = 0; i < InitialObjectPoolSize; i++) + { + AActor* object = GetWorld()->SpawnActor(Object); + SetObjectStatus(false, object); + ObjectPool.Add(object); + } +} + +void AObjectPoolManager::InitializeObjectPool(UClass* Object, int InitialObjectPoolSize) +{ + for (int i = 0; i < InitialObjectPoolSize; i++) + { + AActor* object = GetWorld()->SpawnActor(Object); + SetObjectStatus(false, object); + ObjectPool.Add(object); + } +} + +AActor* AObjectPoolManager::GetObject() +{ + for (AActor* object : ObjectPool) + { + if (object->IsHidden()) + { + SetObjectStatus(true, object); + + if (UObjectPoolComponent* objectPoolComponent = object->GetComponentByClass()) + { + objectPoolComponent->OnRetrieve.ExecuteIfBound(); + } + + return object; + } + } + + return nullptr; +} + +void AObjectPoolManager::ReturnObject(AActor* object) +{ + SetObjectStatus(false, object); + + if (UObjectPoolComponent* objectPoolComponent = object->GetComponentByClass()) + { + objectPoolComponent->OnReturn.ExecuteIfBound(); + } +} + +void AObjectPoolManager::SetObjectStatus(bool enabled, AActor* object) +{ + object->SetActorHiddenInGame(!enabled); + object->SetActorTickEnabled(enabled); + object->SetActorEnableCollision(enabled); +} diff --git a/Source/vampires/ObjectPoolManager.h b/Source/vampires/ObjectPoolManager.h new file mode 100644 index 0000000..800a14c --- /dev/null +++ b/Source/vampires/ObjectPoolManager.h @@ -0,0 +1,30 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "ObjectPoolManager.generated.h" + +UCLASS() +class VAMPIRES_API AObjectPoolManager : public AActor +{ + GENERATED_BODY() + + TArray ObjectPool = TArray(); + +public: + void InitializeObjectPool(TSubclassOf Object, int InitialObjectPoolSize = 400); + void InitializeObjectPool(UClass* Object, int InitialObjectPoolSize = 400); + + AActor* GetObject(); + + void ReturnObject(AActor* object); + +protected: + // Called when the game starts or when spawned + virtual void BeginPlay() override; + +private: + void SetObjectStatus(bool enabled, AActor* object); +}; diff --git a/Source/vampires/VampireAIController.cpp b/Source/vampires/VampireAIController.cpp index 3358083..b501224 100644 --- a/Source/vampires/VampireAIController.cpp +++ b/Source/vampires/VampireAIController.cpp @@ -61,7 +61,7 @@ void AVampireAIController::OnDeath(FDamageInfo info) { // TODO: Do stuff here EnemyCharacter->OnDeath(); - EnemyCharacter->DetachFromControllerPendingDestroy(); + /*EnemyCharacter->DetachFromControllerPendingDestroy(); EnemyCharacter->GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision); EnemyCharacter->GetCapsuleComponent()->SetCollisionResponseToAllChannels(ECR_Ignore); @@ -72,7 +72,7 @@ void AVampireAIController::OnDeath(FDamageInfo info) characterMovementComponent->SetComponentTickEnabled(false); } GetWorldTimerManager().ClearTimer(PawnMoveToTimerHandle); - EnemyCharacter->SetLifeSpan(0.1f); + EnemyCharacter->SetLifeSpan(0.1f);*/ } void AVampireAIController::PawnMoveTo() diff --git a/Source/vampires/VampireGameMode.cpp b/Source/vampires/VampireGameMode.cpp index b877325..02a8501 100644 --- a/Source/vampires/VampireGameMode.cpp +++ b/Source/vampires/VampireGameMode.cpp @@ -57,21 +57,37 @@ void AVampireGameMode::SpawnEnemy() UE_LOG(LogTemp, Warning, TEXT("Something broke")); break; } - + SpawnLocation.Z = PlayerCharacter->GetActorLocation().Z; FTransform Transform; Transform.SetLocation(SpawnLocation); - + FActorSpawnParameters SpawnParameters; SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; - - AEnemyCharacter* Actor = GetWorld()->SpawnActor(EnemyTemplate, Transform, SpawnParameters); - float CapsuleRadius = Actor->GetCapsuleComponent()->GetScaledCapsuleRadius(); - FVector Direction = SpawnLocation - PlayerCharacter->GetActorLocation(); - Direction.Normalize(); - Direction *= CapsuleRadius; - Actor->SetActorLocation(SpawnLocation + Direction); - Actor->SpawnDefaultController(); + + if (AActor* object = GetEnemyObjectPoolManager()->GetObject()) + { + AEnemyCharacter* Actor = Cast(object); + Actor->SetActorTransform(Transform); + float CapsuleRadius = Actor->GetCapsuleComponent()->GetScaledCapsuleRadius(); + FVector Direction = SpawnLocation - PlayerCharacter->GetActorLocation(); + Direction.Normalize(); + Direction *= CapsuleRadius; + Actor->SetActorLocation(SpawnLocation + Direction); + Actor->SpawnDefaultController(); + } +} + +AObjectPoolManager* AVampireGameMode::GetEnemyObjectPoolManager() +{ + if (EnemyObjectPoolManager == nullptr) + { + EnemyObjectPoolManager = GetWorld()->SpawnActor(); + TSubclassOf enemyTemplate = EnemyTemplate; + EnemyObjectPoolManager->InitializeObjectPool(enemyTemplate); + } + + return EnemyObjectPoolManager; } void AVampireGameMode::IncrementEnemyDeathCount() diff --git a/Source/vampires/VampireGameMode.h b/Source/vampires/VampireGameMode.h index 8209d84..def66c6 100644 --- a/Source/vampires/VampireGameMode.h +++ b/Source/vampires/VampireGameMode.h @@ -4,6 +4,7 @@ #include "CoreMinimal.h" #include "EnemyCharacter.h" +#include "ObjectPoolManager.h" #include "PlayerCharacter.h" #include "VampirePlayerController.h" #include "GameFramework/GameMode.h" @@ -30,18 +31,21 @@ private: int EnemyDeathCount = 0; + AObjectPoolManager* EnemyObjectPoolManager = nullptr; + protected: virtual void BeginPlay() override; public: UFUNCTION(BlueprintCallable, BlueprintPure) int GetEnemyDeathCount(); - + UFUNCTION() void IncrementEnemyDeathCount(); + AObjectPoolManager* GetEnemyObjectPoolManager(); + protected: - UFUNCTION() void SpawnEnemy(); };