├── Plugins └── ObjectPoolSystem │ ├── .gitignore │ ├── Doc │ ├── Image │ │ ├── Tick.PNG │ │ ├── DestroyEnd.PNG │ │ ├── DestroyTime.png │ │ ├── GarbageTime.png │ │ ├── SpawnActors.PNG │ │ ├── DestroyBegin.PNG │ │ ├── GarbageCollect.PNG │ │ ├── TestFinishSpawning.PNG │ │ └── DestroyTimeAndActorsCount.png │ └── ActorSpawnDestroy.md │ ├── Resources │ └── Icon128.png │ ├── Source │ ├── ObjectPool │ │ ├── LevelObjectPoolUI.cpp │ │ ├── Chunk.h │ │ ├── LevelObjectPoolUI.h │ │ ├── LevelObjectPool.h │ │ ├── SingleClassObjectPool.h │ │ ├── SingleClassObjectPool.cpp │ │ └── LevelObjectPool.cpp │ ├── Test │ │ ├── EmitSpawner │ │ │ ├── EmitSpawnerBase.cpp │ │ │ ├── RainEmitter.cpp │ │ │ ├── RainEmitter.h │ │ │ ├── EmitSpawnerBase.h │ │ │ ├── EmitSpawnerActor.h │ │ │ └── EmitSpawnerActor.cpp │ │ ├── PerformanceTest │ │ │ ├── ActorSpawnPerformanceTest.h │ │ │ └── ActorSpawnPerformanceTest.cpp │ │ └── CubeSpawner │ │ │ ├── SpawnTestUI.cpp │ │ │ ├── SpawnTestUI.h │ │ │ ├── SpawnTestActor.h │ │ │ └── SpawnTestActor.cpp │ ├── DataAsset │ │ ├── PoolClassDataAsset.cpp │ │ ├── LevelObjectPoolDataAsset.h │ │ ├── PoolClassDataAsset.h │ │ └── LevelObjectPoolDataAsset.cpp │ ├── ObjectPoolSystem.h │ ├── ObjectPoolSystem.Build.cs │ ├── PoolInterface │ │ ├── PoolInterface.cpp │ │ ├── PoolInterface.h │ │ ├── PoolActorInterface.h │ │ └── PoolActorInterface.cpp │ ├── Common │ │ ├── PoolActor.cpp │ │ ├── PoolActor.h │ │ ├── LevelLoadObjectPoolActor.h │ │ └── LevelLoadObjectPoolActor.cpp │ ├── ObjectPoolSystem.cpp │ ├── Private │ │ └── Macro │ │ │ └── ObjectPoolSystemMacro.h │ ├── ObjectPoolSubsystem.h │ └── ObjectPoolSubsystem.cpp │ ├── Content │ └── SpawnTest │ │ ├── BP_SpawnTestActor.uasset │ │ ├── UI │ │ ├── Input │ │ │ ├── IA_Reset.uasset │ │ │ ├── IA_SpawnPool.uasset │ │ │ ├── IA_StayCount.uasset │ │ │ ├── IA_ActiveSpawn.uasset │ │ │ ├── IA_SpawnPerTick.uasset │ │ │ ├── IMC_SpawnTestUI.uasset │ │ │ └── IA_SpawnBlueprint.uasset │ │ ├── BP_UI_SpawnTest.uasset │ │ ├── BP_UI_LevelObjectPool.uasset │ │ └── BP_UI_SingleClassPool.uasset │ │ ├── BP_LevelLoadObjectActor.uasset │ │ ├── Spawner │ │ ├── CubeSpawner │ │ │ ├── BP_PoolActor.uasset │ │ │ ├── BP_CubeSpawnerActor.uasset │ │ │ └── BP_SpawnNormalActor.uasset │ │ ├── EmitSpawner │ │ │ ├── DA_RainEmit.uasset │ │ │ ├── BP_PoolSpawn_EmitBall.uasset │ │ │ ├── BP_EmitRainSpawnerActor.uasset │ │ │ └── BP_NormalSpawn_EmitBall.uasset │ │ └── SM_NaniteMeterialSphere.uasset │ │ ├── LevelObjectPool │ │ ├── BP_LoadObjectPool.uasset │ │ ├── DA_PoolClassObject_BP_EmitBall.uasset │ │ ├── DA_LevelObjectPool_FirstPersonMap.uasset │ │ └── DA_PoolClassObject_BP_SpawnPoolActor.uasset │ │ └── PerformanceTest │ │ └── BP_PerformanceTest.uasset │ └── ObjectPoolSystem.uplugin ├── .gitignore └── README.md /Plugins/ObjectPoolSystem/.gitignore: -------------------------------------------------------------------------------- 1 | /Binaries 2 | /Intermediate -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /* 2 | !/.gitignore 3 | !/Plugins 4 | !/README.md 5 | 6 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Doc/Image/Tick.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Doc/Image/Tick.PNG -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Resources/Icon128.png -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Doc/Image/DestroyEnd.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Doc/Image/DestroyEnd.PNG -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Doc/Image/DestroyTime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Doc/Image/DestroyTime.png -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Doc/Image/GarbageTime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Doc/Image/GarbageTime.png -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Doc/Image/SpawnActors.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Doc/Image/SpawnActors.PNG -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Doc/Image/DestroyBegin.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Doc/Image/DestroyBegin.PNG -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Doc/Image/GarbageCollect.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Doc/Image/GarbageCollect.PNG -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Doc/Image/TestFinishSpawning.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Doc/Image/TestFinishSpawning.PNG -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Doc/Image/DestroyTimeAndActorsCount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Doc/Image/DestroyTimeAndActorsCount.png -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/ObjectPool/LevelObjectPoolUI.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "LevelObjectPoolUI.h" 5 | 6 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/BP_SpawnTestActor.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/BP_SpawnTestActor.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/UI/Input/IA_Reset.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/UI/Input/IA_Reset.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Test/EmitSpawner/EmitSpawnerBase.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "EmitSpawnerBase.h" 5 | 6 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/UI/BP_UI_SpawnTest.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/UI/BP_UI_SpawnTest.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/UI/Input/IA_SpawnPool.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/UI/Input/IA_SpawnPool.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/UI/Input/IA_StayCount.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/UI/Input/IA_StayCount.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/BP_LevelLoadObjectActor.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/BP_LevelLoadObjectActor.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/UI/BP_UI_LevelObjectPool.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/UI/BP_UI_LevelObjectPool.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/UI/BP_UI_SingleClassPool.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/UI/BP_UI_SingleClassPool.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/UI/Input/IA_ActiveSpawn.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/UI/Input/IA_ActiveSpawn.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/UI/Input/IA_SpawnPerTick.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/UI/Input/IA_SpawnPerTick.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/UI/Input/IMC_SpawnTestUI.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/UI/Input/IMC_SpawnTestUI.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/UI/Input/IA_SpawnBlueprint.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/UI/Input/IA_SpawnBlueprint.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/CubeSpawner/BP_PoolActor.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/CubeSpawner/BP_PoolActor.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/EmitSpawner/DA_RainEmit.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/EmitSpawner/DA_RainEmit.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/SM_NaniteMeterialSphere.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/SM_NaniteMeterialSphere.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/LevelObjectPool/BP_LoadObjectPool.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/LevelObjectPool/BP_LoadObjectPool.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/PerformanceTest/BP_PerformanceTest.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/PerformanceTest/BP_PerformanceTest.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/CubeSpawner/BP_CubeSpawnerActor.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/CubeSpawner/BP_CubeSpawnerActor.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/CubeSpawner/BP_SpawnNormalActor.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/CubeSpawner/BP_SpawnNormalActor.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/EmitSpawner/BP_PoolSpawn_EmitBall.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/EmitSpawner/BP_PoolSpawn_EmitBall.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/EmitSpawner/BP_EmitRainSpawnerActor.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/EmitSpawner/BP_EmitRainSpawnerActor.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/EmitSpawner/BP_NormalSpawn_EmitBall.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/Spawner/EmitSpawner/BP_NormalSpawn_EmitBall.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/LevelObjectPool/DA_PoolClassObject_BP_EmitBall.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/LevelObjectPool/DA_PoolClassObject_BP_EmitBall.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/LevelObjectPool/DA_LevelObjectPool_FirstPersonMap.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/LevelObjectPool/DA_LevelObjectPool_FirstPersonMap.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Content/SpawnTest/LevelObjectPool/DA_PoolClassObject_BP_SpawnPoolActor.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HankTassadar/ObjectPoolSubsystem/HEAD/Plugins/ObjectPoolSystem/Content/SpawnTest/LevelObjectPool/DA_PoolClassObject_BP_SpawnPoolActor.uasset -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/DataAsset/PoolClassDataAsset.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "PoolClassDataAsset.h" 5 | #include "PoolInterface/PoolInterface.h" 6 | #include "Private/Macro/ObjectPoolSystemMacro.h" 7 | 8 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/ObjectPoolSystem.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FObjectPoolSystemModule : public IModuleInterface 9 | { 10 | public: 11 | 12 | /** IModuleInterface implementation */ 13 | virtual void StartupModule() override; 14 | virtual void ShutdownModule() override; 15 | }; 16 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/ObjectPoolSystem.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class ObjectPoolSystem : ModuleRules 6 | { 7 | public ObjectPoolSystem(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "UMG" }); 12 | } 13 | } -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Test/EmitSpawner/RainEmitter.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "RainEmitter.h" 5 | 6 | FTransform URainEmitter::GenerateSpawnTransform() 7 | { 8 | //Generate Location and Rotation 9 | //Location is in a plane 10 | FTransform transform; 11 | FVector2D location(FMath::FRand(), FMath::FRand()); 12 | transform.SetLocation(FVector(location.X * SpawnRange.X, location.Y * SpawnRange.Y, 0)); 13 | return transform; 14 | } 15 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/ObjectPoolSystem.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "0.1", 5 | "FriendlyName": "ObjectPoolSystem", 6 | "Description": "", 7 | "Category": "Other", 8 | "CreatedBy": "Hank Tardas", 9 | "CreatedByURL": "", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": true, 14 | "IsBetaVersion": false, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "ObjectPoolSystem", 20 | "Type": "Runtime", 21 | "LoadingPhase": "Default" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/ObjectPool/Chunk.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | struct FObjectChunk 8 | { 9 | FObjectChunk() { 10 | ReSet(); 11 | } 12 | void ReSet() 13 | { 14 | Object = nullptr; 15 | bIsAllocated = false; 16 | } 17 | UObject* Object; 18 | bool bIsAllocated; 19 | }; 20 | 21 | struct FChunkPool 22 | { 23 | FChunkPool() = delete; 24 | FChunkPool(uint32 size) { 25 | ChunkArray.SetNum(size); 26 | } 27 | TArray ChunkArray; 28 | }; -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Test/EmitSpawner/RainEmitter.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "EmitSpawnerBase.h" 7 | 8 | #include "RainEmitter.generated.h" 9 | 10 | /** 11 | * 12 | */ 13 | UCLASS() 14 | class OBJECTPOOLSYSTEM_API URainEmitter : public UEmitSpawnerBase 15 | { 16 | GENERATED_BODY() 17 | 18 | public: 19 | virtual FTransform GenerateSpawnTransform() override; 20 | 21 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "EmitSpawner") 22 | FVector2D SpawnRange = FVector2D(0, 0); 23 | }; 24 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/PoolInterface/PoolInterface.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "PoolInterface.h" 5 | #include "Private/Macro/ObjectPoolSystemMacro.h" 6 | 7 | // Add default functionality here for any IPoolInterface functions that are not pure virtual. 8 | 9 | void IPoolInterface::ReleaseToPool() 10 | { 11 | check(pool); 12 | check(node); 13 | 14 | CastChecked(this); 15 | 16 | pool->FreeObject(node); 17 | } 18 | 19 | void IPoolInterface::OnNewObject() 20 | { 21 | } 22 | 23 | void IPoolInterface::OnObjectDestroy() 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Common/PoolActor.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "PoolActor.h" 5 | 6 | // Sets default values 7 | APoolActor::APoolActor() 8 | { 9 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 10 | PrimaryActorTick.bCanEverTick = true; 11 | 12 | } 13 | 14 | // Called when the game starts or when spawned 15 | void APoolActor::BeginPlay() 16 | { 17 | Super::BeginPlay(); 18 | 19 | } 20 | 21 | // Called every frame 22 | void APoolActor::Tick(float DeltaTime) 23 | { 24 | Super::Tick(DeltaTime); 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/ObjectPoolSystem.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "ObjectPoolSystem.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FObjectPoolSystemModule" 6 | 7 | void FObjectPoolSystemModule::StartupModule() 8 | { 9 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 10 | } 11 | 12 | void FObjectPoolSystemModule::ShutdownModule() 13 | { 14 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 15 | // we call this function before unloading the module. 16 | } 17 | 18 | #undef LOCTEXT_NAMESPACE 19 | 20 | IMPLEMENT_MODULE(FObjectPoolSystemModule, ObjectPoolSystem) -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Private/Macro/ObjectPoolSystemMacro.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | 8 | DECLARE_LOG_CATEGORY_CLASS(LogObjectPool, Log, All) 9 | 10 | #define USE_UE_LOG_WITH_FILE_LINE_FUNCTION //If you want to use UE_LOG with file, line and function, uncomment this line 11 | 12 | //disable warning C4005 13 | #pragma warning(disable:4005) 14 | 15 | #ifdef USE_UE_LOG_WITH_FILE_LINE_FUNCTION 16 | //Redefine UE_LOG to add file, line and function 17 | #define UE_LOG( LogCategroy, Verbosity, Format, ... ) \ 18 | UE_CLOG(true, LogCategroy, Verbosity, TEXT("%s:%d [%s] ") Format, TEXT(__FILE__), __LINE__, TEXT("" __FUNCTION__ ""), ##__VA_ARGS__); 19 | #endif // USE_UE_LOG_WITH_FILE_LINE_FUNCTION 20 | 21 | #pragma warning(default:4005) -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Test/EmitSpawner/EmitSpawnerBase.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Engine/DataAsset.h" 7 | 8 | #include "EmitSpawnerBase.generated.h" 9 | 10 | /** 11 | * 12 | */ 13 | UCLASS() 14 | class OBJECTPOOLSYSTEM_API UEmitSpawnerBase : public UPrimaryDataAsset 15 | { 16 | GENERATED_BODY() 17 | 18 | public: 19 | virtual FTransform GenerateSpawnTransform() {return FTransform();} 20 | 21 | int32 GetSpawnCountPerTick() const {return SpawnCountPerTick;} 22 | 23 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "EmitSpawner") 24 | int32 SpawnCountPerTick = 0; 25 | 26 | //if 0, will spawn every tick 27 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "EmitSpawner") 28 | float SpawnInterval = 0; 29 | }; 30 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Common/PoolActor.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | #include "../PoolInterface/PoolActorInterface.h" 8 | 9 | #include "PoolActor.generated.h" 10 | 11 | /* 12 | * An Actor that can be pooled, managed by a ObjectPoolSubsystem, should use ReleaseToPool() instead of Destroy() 13 | */ 14 | UCLASS(BlueprintType, Blueprintable) 15 | class OBJECTPOOLSYSTEM_API APoolActor : public AActor, public IPoolActorInterface 16 | { 17 | GENERATED_BODY() 18 | 19 | public: 20 | // Sets default values for this actor's properties 21 | APoolActor(); 22 | 23 | protected: 24 | // Called when the game starts or when spawned 25 | virtual void BeginPlay() override; 26 | 27 | public: 28 | // Called every frame 29 | virtual void Tick(float DeltaTime) override; 30 | 31 | }; 32 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Test/PerformanceTest/ActorSpawnPerformanceTest.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | #include "ActorSpawnPerformanceTest.generated.h" 8 | 9 | UCLASS() 10 | class OBJECTPOOLSYSTEM_API AActorSpawnPerformanceTest : public AActor 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | // Sets default values for this actor's properties 16 | AActorSpawnPerformanceTest(); 17 | 18 | protected: 19 | // Called when the game starts or when spawned 20 | virtual void BeginPlay() override; 21 | 22 | public: 23 | // Called every frame 24 | virtual void Tick(float DeltaTime) override; 25 | 26 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Performance Test") 27 | TSubclassOf ActorToSpawn; 28 | 29 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Performance Test") 30 | int32 SpawnCountPerTick = 100; 31 | 32 | private: 33 | AActor* SpawnActors(); 34 | 35 | void TestFinishSpawning(AActor* actor); 36 | 37 | int32 SpawnNameUnique = 0; 38 | 39 | UPROPERTY() 40 | TSet SpawnedActors; 41 | }; 42 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/DataAsset/LevelObjectPoolDataAsset.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Engine/DataAsset.h" 7 | #include "PoolClassDataAsset.h" 8 | #include "LevelObjectPoolDataAsset.generated.h" 9 | 10 | class ULevel; 11 | /** 12 | * 13 | */ 14 | UCLASS() 15 | class OBJECTPOOLSYSTEM_API ULevelObjectPoolDataAsset : public UPrimaryDataAsset 16 | { 17 | GENERATED_BODY() 18 | public: 19 | bool RegisterToObjectPoolSubsystem(ULevel* level); 20 | 21 | UFUNCTION(BlueprintCallable, Category = "ObjectPool", meta = (Keywords = "Regist", DisplayName = "Regist To Pool", ScriptName = "RegistToPool")) 22 | bool K2_RegisterToObjectPoolSubsystem(ULevel* level); 23 | 24 | void UnRegisterFromObjectPoolSubsystem(ULevel* level); 25 | 26 | UFUNCTION(BlueprintCallable, Category = "ObjectPool", meta = (Keywords = "UnRegist", DisplayName = "UnRegist From Pool", ScriptName = "UnRegistFromPool")) 27 | void K2_UnRegisterFromObjectPoolSubsystem(ULevel* level); 28 | 29 | public: 30 | /* 31 | * The level to pool objects for 32 | * when a level try to get object from pool, if mutiple levels have the same poolclass, it will get from self's pool 33 | */ 34 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Object Pool") 35 | TArray> PoolClassArray; 36 | 37 | }; 38 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Common/LevelLoadObjectPoolActor.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | #include "LevelLoadObjectPoolActor.generated.h" 8 | 9 | class ULevelObjectPoolDataAsset; 10 | class ULevelObjectPoolUI; 11 | class ULevelObjectPool; 12 | 13 | UCLASS() 14 | class OBJECTPOOLSYSTEM_API ALevelLoadObjectPoolActor : public AActor 15 | { 16 | GENERATED_BODY() 17 | 18 | public: 19 | // Sets default values for this actor's properties 20 | ALevelLoadObjectPoolActor(); 21 | 22 | protected: 23 | // Called when the game starts or when spawned 24 | virtual void BeginPlay() override; 25 | 26 | virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; 27 | public: 28 | // Called every frame 29 | virtual void Tick(float DeltaTime) override; 30 | 31 | private: 32 | void LoadPoolInfoUI(); 33 | public: 34 | UPROPERTY(EditDefaultsOnly, Category = "Object Pool") 35 | TObjectPtr PoolDataAsset; 36 | 37 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Object Pool") 38 | bool bShowPoolInfoUI = false; 39 | 40 | UPROPERTY(EditDefaultsOnly, Category = "Object Pool") 41 | TSubclassOf PoolInfoUIClass; 42 | 43 | UPROPERTY(BluePrintReadOnly, Category = "Object Pool") 44 | TObjectPtr PoolInfoUI; 45 | }; 46 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Test/EmitSpawner/EmitSpawnerActor.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | #include "EmitSpawnerBase.h" 8 | 9 | #include "EmitSpawnerActor.generated.h" 10 | 11 | UCLASS() 12 | class OBJECTPOOLSYSTEM_API AEmitSpawnerActor : public AActor 13 | { 14 | GENERATED_BODY() 15 | 16 | public: 17 | // Sets default values for this actor's properties 18 | AEmitSpawnerActor(); 19 | 20 | protected: 21 | // Called when the game starts or when spawned 22 | virtual void BeginPlay() override; 23 | 24 | public: 25 | // Called every frame 26 | virtual void Tick(float DeltaTime) override; 27 | 28 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "EmitSpawner") 29 | TObjectPtr Emitter; 30 | 31 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "EmitSpawner") 32 | TSubclassOf NormalSpawnActorClass; 33 | 34 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "EmitSpawner", Meta = (MustImplement = "PoolActorInterface")) 35 | TSubclassOf ObjectPoolSpawnActorClass; 36 | 37 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "EmitSpawner") 38 | bool bUseObjectPool = false; 39 | private: 40 | void SpawnNormal(FTransform& SpawnTransform); 41 | void SpawnObjectPool(FTransform& SpawnTransform); 42 | 43 | float SpawnTimer = 0.f; 44 | }; 45 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/PoolInterface/PoolInterface.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "UObject/Interface.h" 7 | #include "../ObjectPool/SingleClassObjectPool.h" 8 | 9 | #include "PoolInterface.generated.h" 10 | 11 | // This class does not need to be modified. 12 | UINTERFACE(MinimalAPI, NotBlueprintable) 13 | class UPoolInterface : public UInterface 14 | { 15 | GENERATED_BODY() 16 | }; 17 | 18 | /** 19 | * 20 | */ 21 | class OBJECTPOOLSYSTEM_API IPoolInterface 22 | { 23 | GENERATED_BODY() 24 | 25 | // Add interface functions to this class. This is the class that will be inherited to implement this interface. 26 | public: 27 | /* 28 | * called to release this object back to ObjectPool 29 | */ 30 | virtual void ReleaseToPool(); 31 | 32 | private: 33 | //These variables and functions are only used access by ObjectPoolSubsystem, so make it private 34 | friend class USingleClassObjectPool; 35 | 36 | /* 37 | * called on object new from system 38 | * should be override by subclassinterface 39 | */ 40 | virtual void OnNewObject(); 41 | 42 | /* 43 | * called on object real destroy 44 | * could be override by subclassinterface 45 | */ 46 | virtual void OnObjectDestroy(); 47 | private: 48 | 49 | 50 | //node in singleclasspool double linked list 51 | USingleClassObjectPool::ChunkListNode* node; 52 | 53 | //pool owner this object 54 | TObjectPtr pool; 55 | }; 56 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Test/CubeSpawner/SpawnTestUI.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "SpawnTestUI.h" 5 | #include "Private/Macro/ObjectPoolSystemMacro.h" 6 | 7 | void USpawnTestUI::ResetAll() 8 | { 9 | UIData.ResetAll(); 10 | OnDataChange.Broadcast(); 11 | } 12 | 13 | void USpawnTestUI::SwitchUseObjectPool() 14 | { 15 | UIData.bUseObjectPool = !UIData.bUseObjectPool; 16 | OnDataChange.Broadcast(); 17 | } 18 | 19 | void USpawnTestUI::SwitchStartSpawn() 20 | { 21 | UIData.bStartSpawn = !UIData.bStartSpawn; 22 | OnDataChange.Broadcast(); 23 | } 24 | 25 | void USpawnTestUI::SwitchBlueprintSpawn() 26 | { 27 | UIData.bBlueprintSpawn = !UIData.bBlueprintSpawn; 28 | OnDataChange.Broadcast(); 29 | } 30 | 31 | void USpawnTestUI::ChangeSpawnCountPerTick(const FText& test) 32 | { 33 | if (!FCString::IsNumeric(*test.ToString())) 34 | { 35 | return; 36 | } 37 | UIData.SpawnCountPerTick = FCString::Atoi(*test.ToString()); 38 | OnDataChange.Broadcast(); 39 | } 40 | 41 | void USpawnTestUI::ChangeStayCount(const FText& test) 42 | { 43 | if (!FCString::IsNumeric(*test.ToString())) 44 | { 45 | return; 46 | } 47 | UIData.StayCount = FCString::Atoi(*test.ToString()); 48 | OnDataChange.Broadcast(); 49 | } 50 | 51 | void USpawnTestUI::AddHandleOnUIDataChange(const FScriptDelegate& handle) 52 | { 53 | OnDataChange.Add(handle); 54 | } 55 | 56 | 57 | void USpawnTestUI::ClearHandleOnUIDataChange() 58 | { 59 | OnDataChange.Clear(); 60 | } 61 | 62 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/ObjectPool/LevelObjectPoolUI.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Blueprint/UserWidget.h" 7 | 8 | #include "LevelObjectPoolUI.generated.h" 9 | 10 | USTRUCT(BlueprintType) 11 | struct FLevelObjectPoolData 12 | { 13 | GENERATED_BODY() 14 | 15 | UPROPERTY(BlueprintReadOnly, Category = "ObjectPoolUI") 16 | FString LevelName; 17 | 18 | UPROPERTY(BlueprintReadOnly, Category = "ObjectPoolUI") 19 | int32 ObjectClassCount; 20 | }; 21 | 22 | USTRUCT(BlueprintType) 23 | struct FSingleClassPoolData 24 | { 25 | GENERATED_BODY() 26 | 27 | UPROPERTY(BlueprintReadOnly, Category = "ObjectPoolUI") 28 | FString ClassName; 29 | 30 | //with ready and not ready object 31 | UPROPERTY(BlueprintReadOnly, Category = "ObjectPoolUI") 32 | int32 TotalObjectCount; 33 | 34 | //InUseCount + InPoolCount 35 | UPROPERTY(BlueprintReadOnly, Category = "ObjectPoolUI") 36 | int32 ReadyObjectCount; 37 | 38 | //is using 39 | UPROPERTY(BlueprintReadOnly, Category = "ObjectPoolUI") 40 | int32 InUseObjectCount; 41 | 42 | //is in pool can be used 43 | UPROPERTY(BlueprintReadOnly, Category = "ObjectPoolUI") 44 | int32 InPoolObjectCount; 45 | }; 46 | 47 | /** 48 | * 49 | */ 50 | UCLASS() 51 | class OBJECTPOOLSYSTEM_API ULevelObjectPoolUI : public UUserWidget 52 | { 53 | GENERATED_BODY() 54 | 55 | public: 56 | UPROPERTY(BlueprintReadOnly, Category = "ObjectPoolUI") 57 | FLevelObjectPoolData LevelObjectPoolData; 58 | 59 | UPROPERTY(BlueprintReadOnly, Category = "ObjectPoolUI") 60 | TArray SingleClassPoolData; 61 | }; 62 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/DataAsset/PoolClassDataAsset.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Engine/DataAsset.h" 7 | 8 | #include "PoolClassDataAsset.generated.h" 9 | 10 | class ULevel; 11 | /** 12 | * 13 | */ 14 | UCLASS() 15 | class OBJECTPOOLSYSTEM_API UPoolClassDataAsset : public UPrimaryDataAsset 16 | { 17 | GENERATED_BODY() 18 | public: 19 | 20 | public: 21 | // The class of the object using in pool 22 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PoolClassDataAsset",meta = (MustImplement = "PoolInterface")) 23 | TSubclassOf Class; 24 | 25 | // The number of objects to preallocate in the pool 26 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PoolClassDataAsset") 27 | int32 PreallocateCount = 0; 28 | 29 | //Todo: soft regist to level, no block level load 30 | 31 | //Todo: Auto Expand Pool 32 | //// If true, the pool will expand if it runs out of objects 33 | //UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PoolClassDataAsset") 34 | //bool bCouldExpand = true; 35 | 36 | ////when reach this percentage of poolsize, will expand, 0~100 37 | //UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PoolClassDataAsset") 38 | //int32 ExpandPercentage = FMath::Clamp(50, 0, 100); 39 | 40 | ///* 41 | // * expand max count pre tick limit 42 | // * if ExpandTimeLimitPerTick is not 0 and expand time used per tick is over it, will not expand more 43 | // */ 44 | //UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PoolClassDataAsset") 45 | //int32 ExpandCountPerTick = 1; 46 | 47 | ///* 48 | // * if expand count automatically decided by frame, will make expand used time per tick smaller than this value 49 | // * ms 50 | // * if 0, will expand ExpandCountPerTick in every tick 51 | // */ 52 | //UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PoolClassDataAsset") 53 | //int32 ExpandTimeLimitPerTick = 0; 54 | 55 | 56 | }; 57 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/ObjectPool/LevelObjectPool.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "UObject/NoExportTypes.h" 7 | #include "Chunk.h" 8 | #include "LevelObjectPoolUI.h" 9 | 10 | #include "LevelObjectPool.generated.h" 11 | 12 | 13 | class ULevelObjectPoolDataAsset; 14 | class USingleClassObjectPool; 15 | /** 16 | * 17 | */ 18 | UCLASS(BlueprintType, Blueprintable) 19 | class ULevelObjectPool : public UObject 20 | { 21 | GENERATED_BODY() 22 | public: 23 | typedef TArray> ChunkPoolArray; 24 | typedef TMap, ChunkPoolArray> ChunkPoolMap; 25 | 26 | public: 27 | bool Init(ULevel* level) { Level = level; return true; }; 28 | 29 | /* 30 | * regist a level object pool data asset 31 | */ 32 | bool RegisterLevelObjectPool(ULevelObjectPoolDataAsset* LevelObjectPoolDataAsset); 33 | 34 | /* 35 | * reverse regist a level object pool data asset, usually called when the level is destroyed 36 | * When Regist failed, should call this function to clear the class already regist 37 | */ 38 | void UnRegisterLevelObjectPool(ULevelObjectPoolDataAsset* LevelObjectPoolDataAsset); 39 | 40 | UFUNCTION(BlueprintCallable, Category = "ObjectPool") 41 | UUserWidget* SetLevelObjectPoolUIByClass(TSubclassOf uiClass); 42 | 43 | UFUNCTION(BlueprintCallable, Category = "ObjectPool") 44 | void SetLevelObjectPoolUI(ULevelObjectPoolUI* ui); 45 | 46 | UObject* AllocateObject(UClass* Class); 47 | 48 | void TickPool(float DeltaTime); 49 | 50 | private: 51 | void UpdateData(); 52 | private: 53 | TWeakObjectPtr Level; 54 | 55 | /* 56 | * every ChunkPoolArray[0] is the preallocated 57 | * one ULevelObjectPoolDataAsset can have only one ChunkPoolArray in this level 58 | */ 59 | ChunkPoolMap Map_ChunkPool; 60 | public: 61 | //all class can allocte in the levelobjectpool 62 | UPROPERTY(BlueprintReadWrite,Category = "ObjectPool") 63 | TMap> Map_ClassPool; 64 | 65 | UPROPERTY(BlueprintReadWrite, Category = "ObjectPool") 66 | TObjectPtr LevelObjectPoolUI; 67 | }; 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # ObjectPoolSubsystem 3 | 4 | An ObjectPool For Unreal Engine 5 5 | 6 | More detail in [this Doc](./Plugins/ObjectPoolSystem/Doc/ActorSpawnDestroy.md) 7 | 8 | ## 1.How to use 9 | 10 | Drag the **BP_LoadObjectPool** actor into the level, it will automatically register the object pool defined by **DA_LevelObjectPool_FistPersonMap**.Then you can use SpawnActor or GetUObject in ObjectPoolSubsystem. 11 | 12 | ## 2.Classes 13 | 14 | ### 2.1 DataAsset 15 | 16 | #### 2.1.1 PoolClassDataAsset 17 | 18 | This class is used to define a pool class, the class in this DataAsset must implement the interface **IPoolInterface**. 19 | 20 | #### 2.1.2 LevelObjectPoolDataAsset 21 | 22 | This is a array of PoolClassDataAsset, it is used to define a pool in a level, you can register multiple pools in a level by spawn and destroy **LevelLoadObjectPoolActor**. 23 | 24 | ### 2.2 Interface 25 | 26 | #### 2.2.1 IPoolInterface 27 | 28 | This is a base interface for the class that you want to use in the object pool, if you want any object to be pooled, you must implement this interface. 29 | 30 | #### 2.2.2 IPoolActorInterface 31 | 32 | This is a subclass of IPoolInterface, it is used to define the actor's behavior when it is spawned or despawned. 33 | 34 | #### 2.2.3 Your Own PoolObjectInterface 35 | 36 | You can define your own interface by inheriting from IPoolInterface, such as **IPoolPawnInterface**, **IPoolCharacterInterface**. But notice that you must really know the lifetime and behavior of the object you want to pool, you can see details in the **IPoolActorInterface**, see what is done in function ***ReleaseToPool, AttachToLevel, OnObjectNew***, and you must do the same thing in your own interface. 37 | 38 | ### 2.3 Actors 39 | 40 | #### 2.3.1 LevelLoadObjectPoolActor 41 | 42 | This actor is a normal actor spawned by ***UWorld::SpawnActor***, used to register and unregister the object pool defined by **LevelObjectPoolDataAsset** when this actor BeginPlay and EndPlay. You can control the lifetime of the object pool by spawn and destroy this actor. 43 | 44 | #### 2.3.2 PoolActor 45 | 46 | This Actor can be spawned by ***UObjectPoolSubsystem::SpawnActor***, do not call this actor's ***Destroy*** function, use ***ReleaseToPool*** instead, if you called ***Destroy***, will crash in ***IPoolActorInterface::AbortActorDestroy***. 47 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Test/CubeSpawner/SpawnTestUI.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Blueprint/UserWidget.h" 7 | 8 | #include "SpawnTestUI.generated.h" 9 | 10 | DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnUIDataChange); 11 | 12 | USTRUCT(BlueprintType) 13 | struct FSpawnTestUIData 14 | { 15 | GENERATED_BODY() 16 | 17 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnTestUI | Data") 18 | int32 SpawnCountPerTick = 10; 19 | 20 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnTestUI | Data") 21 | int32 StayCount = 0; 22 | 23 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnTestUI | Data") 24 | bool bUseObjectPool = false; 25 | 26 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnTestUI | Data") 27 | bool bStartSpawn = false; 28 | 29 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnTestUI | Data") 30 | bool bBlueprintSpawn = false; 31 | 32 | void ResetAll() { 33 | SpawnCountPerTick = 10; 34 | StayCount = 0; 35 | bUseObjectPool = false; 36 | bStartSpawn = false; 37 | bBlueprintSpawn = false; 38 | } 39 | }; 40 | /** 41 | * 42 | */ 43 | UCLASS() 44 | class OBJECTPOOLSYSTEM_API USpawnTestUI : public UUserWidget 45 | { 46 | GENERATED_BODY() 47 | 48 | public: 49 | UFUNCTION(BlueprintCallable, Category = "SpawnTestUI") 50 | void ResetAll(); 51 | 52 | UFUNCTION(BlueprintCallable, Category = "SpawnTestUI") 53 | void SwitchUseObjectPool(); 54 | 55 | UFUNCTION(BlueprintCallable, Category = "SpawnTestUI") 56 | void SwitchStartSpawn(); 57 | 58 | UFUNCTION(BlueprintCallable, Category = "SpawnTestUI") 59 | void SwitchBlueprintSpawn(); 60 | 61 | UFUNCTION(BlueprintCallable, Category = "SpawnTestUI") 62 | void ChangeSpawnCountPerTick(const FText& test); 63 | 64 | UFUNCTION(BlueprintCallable, Category = "SpawnTestUI") 65 | void ChangeStayCount(const FText& test); 66 | 67 | void AddHandleOnUIDataChange(const FScriptDelegate& handle); 68 | 69 | void ClearHandleOnUIDataChange(); 70 | 71 | public: 72 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnTestUI | Data") 73 | FSpawnTestUIData UIData; 74 | 75 | private: 76 | UPROPERTY() 77 | FOnUIDataChange OnDataChange; 78 | }; 79 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Test/EmitSpawner/EmitSpawnerActor.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "EmitSpawnerActor.h" 5 | #include "Private/Macro/ObjectPoolSystemMacro.h" 6 | #include "ObjectPoolSubsystem.h" 7 | 8 | // Sets default values 9 | AEmitSpawnerActor::AEmitSpawnerActor() 10 | { 11 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 12 | PrimaryActorTick.bCanEverTick = true; 13 | 14 | } 15 | 16 | // Called when the game starts or when spawned 17 | void AEmitSpawnerActor::BeginPlay() 18 | { 19 | Super::BeginPlay(); 20 | 21 | } 22 | 23 | // Called every frame 24 | void AEmitSpawnerActor::Tick(float DeltaTime) 25 | { 26 | TRACE_CPUPROFILER_EVENT_SCOPE(AEmitSpawnerActor::Tick); 27 | Super::Tick(DeltaTime); 28 | if (!Emitter) 29 | { 30 | return; 31 | } 32 | 33 | if (Emitter->SpawnInterval > 0) 34 | { 35 | if (SpawnTimer < Emitter->SpawnInterval) 36 | { 37 | SpawnTimer += DeltaTime; 38 | 39 | return; 40 | 41 | } 42 | else 43 | { 44 | SpawnTimer -= Emitter->SpawnInterval; 45 | } 46 | }; 47 | 48 | FTransform transform; 49 | int32 spawnCount = Emitter->GetSpawnCountPerTick(); 50 | 51 | for (int32 i = 0; i < spawnCount; ++i) 52 | { 53 | // Spawn 54 | transform = Emitter->GenerateSpawnTransform() + this->GetTransform(); 55 | if (bUseObjectPool) 56 | { 57 | SpawnObjectPool(transform); 58 | } 59 | else 60 | { 61 | SpawnNormal(transform); 62 | } 63 | } 64 | } 65 | 66 | void AEmitSpawnerActor::SpawnNormal(FTransform& SpawnTransform) 67 | { 68 | TRACE_CPUPROFILER_EVENT_SCOPE(AEmitSpawnerActor::SpawnNormal); 69 | UWorld* world = GetWorld(); 70 | check(world); 71 | world->SpawnActor(NormalSpawnActorClass, SpawnTransform); 72 | } 73 | 74 | void AEmitSpawnerActor::SpawnObjectPool(FTransform& SpawnTransform) 75 | { 76 | TRACE_CPUPROFILER_EVENT_SCOPE(AEmitSpawnerActor::SpawnObjectPool); 77 | UWorld* world = GetWorld(); 78 | check(world); 79 | UObjectPoolSubsystem* poolSubsystem = world->GetSubsystem(); 80 | check(poolSubsystem); 81 | poolSubsystem->SpawnActor(this, ObjectPoolSpawnActorClass, SpawnTransform); 82 | } 83 | 84 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/ObjectPoolSubsystem.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Subsystems/WorldSubsystem.h" 7 | 8 | #include "ObjectPoolSubsystem.generated.h" 9 | 10 | class ULevelObjectPoolDataAsset; 11 | class ULevelObjectPool; 12 | /** 13 | * 14 | */ 15 | UCLASS() 16 | class OBJECTPOOLSYSTEM_API UObjectPoolSubsystem : public UWorldSubsystem 17 | { 18 | GENERATED_BODY() 19 | /* 20 | * There is 2 ways to create a new AActor 21 | * 1. rewrite SpawnActor in UWorld to an AActor only create and init but not add to Scene 22 | * 2. use SpawnActorDeferred to create an AActor, then call FinishSpawningActor to add to Scene 23 | * It is better to use 1, because 1 can have more control 24 | */ 25 | public: 26 | //Todo: Spawn Actor as way 1 27 | UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (WorldContext = "WorldContextObject", DisplayName = "Spawn From Pool", ScriptName = "SpawnActorFromPool")) 28 | AActor* SpawnActor(const UObject* WorldContextObject 29 | , UPARAM(meta = (MustImplement = "PoolActorInterface")) TSubclassOf ActorClass 30 | ,const FTransform& SpawnTransform 31 | , ESpawnActorCollisionHandlingMethod CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::Undefined); 32 | 33 | UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (WorldContext = "WorldContextObject", DisplayName = "Get Object From Pool", ScriptName = "GetUObjectFromPool")) 34 | UObject* GetUObject(const UObject* WorldContextObject, UPARAM(meta = (MustImplement = "PoolInterface")) TSubclassOf ObjectClass); 35 | 36 | 37 | UFUNCTION(BlueprintCallable, Category = "ObjectPool") 38 | ULevelObjectPool* GetLevelObjectPool(ULevel* level); 39 | 40 | public: 41 | // USubsystem implementation Begin 42 | virtual void Initialize(FSubsystemCollectionBase& Collection) override; 43 | virtual void Deinitialize() override; 44 | // USubsystem implementation End 45 | 46 | //Todo: Make this subsystem tickable 47 | 48 | private: 49 | friend class ULevelObjectPoolDataAsset; 50 | bool RegisterLevelObjectPool(ULevelObjectPoolDataAsset* LevelObjectPoolDataAsset, ULevel* level); 51 | 52 | void UnRegisterLevelObjectPool(ULevelObjectPoolDataAsset* LevelObjectPoolDataAsset, ULevel* level); 53 | private: 54 | 55 | UPROPERTY() 56 | TMap> PoolMap; 57 | }; 58 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Common/LevelLoadObjectPoolActor.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "LevelLoadObjectPoolActor.h" 5 | #include "Blueprint/GameViewportSubsystem.h" 6 | #include "Macro/ObjectPoolSystemMacro.h" 7 | #include "DataAsset/LevelObjectPoolDataAsset.h" 8 | #include "ObjectPool/LevelObjectPoolUI.h" 9 | #include "ObjectPool/LevelObjectPool.h" 10 | #include "ObjectPoolSubsystem.h" 11 | 12 | // Sets default values 13 | ALevelLoadObjectPoolActor::ALevelLoadObjectPoolActor() 14 | { 15 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 16 | PrimaryActorTick.bCanEverTick = true; 17 | 18 | } 19 | 20 | // Called when the game starts or when spawned 21 | void ALevelLoadObjectPoolActor::BeginPlay() 22 | { 23 | if (PoolDataAsset) 24 | { 25 | ULevel* level = GetLevel(); 26 | check(level); 27 | bool flag = PoolDataAsset->RegisterToObjectPoolSubsystem(level); 28 | if (flag) 29 | { 30 | UE_LOG(LogObjectPool, Log, TEXT("Regist To Pool Success")); 31 | if (bShowPoolInfoUI) 32 | { 33 | LoadPoolInfoUI(); 34 | } 35 | } 36 | else 37 | { 38 | UE_LOG(LogObjectPool, Error, TEXT("Regist To Pool Failed")); 39 | } 40 | } 41 | else 42 | { 43 | UE_LOG(LogObjectPool, Error, TEXT("PoolDataAsset is null")); 44 | } 45 | 46 | Super::BeginPlay(); 47 | } 48 | 49 | void ALevelLoadObjectPoolActor::EndPlay(const EEndPlayReason::Type EndPlayReason) 50 | { 51 | Super::EndPlay(EndPlayReason); 52 | 53 | if (PoolDataAsset) 54 | { 55 | PoolDataAsset->UnRegisterFromObjectPoolSubsystem(GetLevel()); 56 | } 57 | else 58 | { 59 | UE_LOG(LogObjectPool, Error, TEXT("PoolDataAsset is null")); 60 | } 61 | } 62 | 63 | // Called every frame 64 | void ALevelLoadObjectPoolActor::Tick(float DeltaTime) 65 | { 66 | Super::Tick(DeltaTime); 67 | 68 | } 69 | 70 | void ALevelLoadObjectPoolActor::LoadPoolInfoUI() 71 | { 72 | APlayerController* controller = GetGameInstance()->GetFirstLocalPlayerController(); 73 | if (controller && PoolInfoUIClass) 74 | { 75 | PoolInfoUI = CreateWidget(controller, PoolInfoUIClass); 76 | if (PoolInfoUI) 77 | { 78 | ULevelObjectPool* pool = GetWorld()->GetSubsystem()->GetLevelObjectPool(GetLevel()); 79 | check(pool); 80 | pool->SetLevelObjectPoolUI(PoolInfoUI); 81 | UGameViewportSubsystem* viewport = GetGameInstance()->GetEngine()->GetEngineSubsystem(); 82 | check(viewport); 83 | viewport->AddWidget(PoolInfoUI, FGameViewportWidgetSlot()); 84 | } 85 | else 86 | { 87 | UE_LOG(LogObjectPool, Error, TEXT("Create PoolInfoUI Failed")); 88 | } 89 | } 90 | else 91 | { 92 | UE_LOG(LogObjectPool, Error, TEXT("PoolInfoUIClass is null")); 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Test/CubeSpawner/SpawnTestActor.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | 8 | #include "SpawnTestUI.h" 9 | #include "SpawnTestActor.generated.h" 10 | 11 | /* 12 | * For testing the spawn and despawn in normal spawn way and object pool way 13 | */ 14 | UCLASS() 15 | class OBJECTPOOLSYSTEM_API ASpawnTestActor : public AActor 16 | { 17 | GENERATED_BODY() 18 | 19 | public: 20 | // Sets default values for this actor's properties 21 | ASpawnTestActor(); 22 | 23 | protected: 24 | // Called when the game starts or when spawned 25 | virtual void BeginPlay() override; 26 | 27 | virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; 28 | 29 | public: 30 | // Called every frame 31 | virtual void Tick(float DeltaTime) override; 32 | 33 | UFUNCTION(BlueprintNativeEvent, Category = "SpawnTestActor") 34 | void BlueprintSpawnNormal(); 35 | 36 | virtual void BlueprintSpawnNormal_Implementation(); 37 | 38 | UFUNCTION(BlueprintNativeEvent, Category = "SpawnTestActor") 39 | void BlueprintSpawnObjectPool(); 40 | 41 | virtual void BlueprintSpawnObjectPool_Implementation(); 42 | public: 43 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnTestActor") 44 | TSubclassOf SpawnTestUIClass; 45 | 46 | UPROPERTY(BlueprintReadOnly, Category = "SpawnTestActor") 47 | TObjectPtr SpawnTestUIInstance; 48 | 49 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnTestActor", meta = (MustImplement = "PoolActorInterface")) 50 | TSubclassOf ObjectPoolSpawnClass; 51 | 52 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnTestActor") 53 | TSubclassOf NormalSpawnClass; 54 | 55 | private: 56 | void TickSpawn(); 57 | 58 | void SpawnNormal(); 59 | 60 | void SpawnObjectPool(); 61 | 62 | //will auto destroy actor when actor num over StayCount 63 | void StayCountAutoDestroy(); 64 | 65 | uint32 GetActorCount(); 66 | 67 | void DestroyOneActor(); 68 | 69 | public: 70 | //the interval between two actor spawn location 71 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnTestActor") 72 | FVector SpawnLocationInterval = FVector(100, 100, 100); 73 | 74 | //actor numbers of x,y,z, total actor max number = x*y*z 75 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnTestActor") 76 | FVector SpawnLocationCount = FVector(100, 100, 100); 77 | 78 | UFUNCTION(BlueprintCallable, Category = "SpawnTestActor") 79 | FVector GetSpawnLocation(); 80 | 81 | UFUNCTION(BlueprintCallable, Category = "SpawnTestActor") 82 | void AddToList(AActor* actor); 83 | private: 84 | //Used by spawn actor, insert in tail and remove from head 85 | TDoubleLinkedList> ActorList; 86 | 87 | //current spawn count in SpawnLocationCount, 0,0,0 means the next location to spawn is SpawnLocationCount's 0,0,0 88 | FVector SpawnPosition = FVector(0, 0, 0); 89 | 90 | private: 91 | //Data From SpawnTestUI 92 | FSpawnTestUIData* UIData = nullptr; 93 | }; 94 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/ObjectPool/SingleClassObjectPool.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "UObject/NoExportTypes.h" 7 | #include "Chunk.h" 8 | 9 | #include "SingleClassObjectPool.generated.h" 10 | 11 | class UPoolClassDataAsset; 12 | class UPoolInterface; 13 | struct FSingleClassPoolData; 14 | class ULevelObjectPool; 15 | /** 16 | * 17 | */ 18 | UCLASS(BlueprintType, Blueprintable) 19 | class USingleClassObjectPool : public UObject 20 | { 21 | GENERATED_BODY() 22 | 23 | public: 24 | typedef TDoubleLinkedList ChunkList; 25 | //node of list 26 | typedef ChunkList::TDoubleLinkedListNode ChunkListNode; 27 | public: 28 | 29 | bool Init(UClass* Class, ULevel* level); 30 | 31 | bool Register(UPoolClassDataAsset* PoolClassDataAsset, TSharedPtr ChunkPool, uint32 StartPos, ULevelObjectPool* InLevelObjectPool); 32 | 33 | void UnRegister(UPoolClassDataAsset* PoolClassDataAsset); 34 | 35 | /* 36 | * Get an object from FreeChain to UsedChain 37 | */ 38 | UObject* AllocateObject(); 39 | 40 | /* 41 | * Free an object, only called by IPoolInterface 42 | */ 43 | void FreeObject(ChunkListNode* node); 44 | 45 | //get the used count of this pool 46 | int32 getusedcount() { return UsedChain.Num(); } 47 | //get the free count of this pool 48 | int32 getfreecount() { return FreeChain.Num(); } 49 | //get the all count of this pool 50 | int32 getallcount() { return AllChain.Num(); } 51 | //get the ready count of this pool 52 | int32 getreadycount() { return getusedcount() + getfreecount(); } 53 | 54 | void Tick(float DeltaTime); 55 | private: 56 | UObject* NewThisObject(uint32 PosInAllChain); 57 | 58 | ////allocate a object from this pool 59 | //tweakptr allocateobject(); 60 | ////free a object to this pool 61 | //void freeobject(tweakptr object); 62 | ////free all object in this pool 63 | //void freeallobject(); 64 | ////expand the pool 65 | //void expandpool(uint32 size); 66 | ////shrink the pool 67 | //void shrinkpool(uint32 size); 68 | ////get the class of this pool 69 | //uclass* getclass() { return objectclass.get(); 70 | ////get the used chain of this pool 71 | //tdoublelinkedlist>& getusedchain() { return usedchain; } 72 | ////get the free chain of this pool 73 | //tdoublelinkedlist>& getfreechain() { return freechain; } 74 | ////get the all chain of this pool 75 | //tdoublelinkedlist>& getallchain() { return allchain; } 76 | 77 | 78 | 79 | private: 80 | FSingleClassPoolData* PoolDataForUI; 81 | 82 | bool InitFlag; 83 | 84 | //all the dataasset that used in this pool 85 | TArray> PoolClassDataAssetArray; 86 | 87 | TWeakObjectPtr Level; 88 | TWeakObjectPtr LevelObjectPool; 89 | 90 | TSharedPtr UsingChunkPool; 91 | uint32 StartPosInPool; 92 | 93 | TWeakObjectPtr ObjectClass; 94 | 95 | 96 | 97 | //used chain 98 | ChunkList UsedChain; 99 | 100 | //free chain 101 | ChunkList FreeChain; 102 | 103 | //AllChunk 104 | ChunkList AllChain; 105 | }; 106 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Test/PerformanceTest/ActorSpawnPerformanceTest.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "ActorSpawnPerformanceTest.h" 5 | #include "Macro/ObjectPoolSystemMacro.h" 6 | 7 | // Sets default values 8 | AActorSpawnPerformanceTest::AActorSpawnPerformanceTest() 9 | { 10 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 11 | PrimaryActorTick.bCanEverTick = true; 12 | 13 | } 14 | 15 | // Called when the game starts or when spawned 16 | void AActorSpawnPerformanceTest::BeginPlay() 17 | { 18 | Super::BeginPlay(); 19 | 20 | } 21 | 22 | // Called every frame 23 | void AActorSpawnPerformanceTest::Tick(float DeltaTime) 24 | { 25 | Super::Tick(DeltaTime); 26 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::Tick); 27 | for (int32 i = 0; i < SpawnCountPerTick; ++i) 28 | { 29 | AActor* actor = SpawnActors(); 30 | SpawnedActors.Add(actor); 31 | } 32 | 33 | for (AActor* actor : SpawnedActors) 34 | { 35 | if (actor) 36 | { 37 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::Tick_DestroyActor); 38 | actor->Destroy(); 39 | } 40 | } 41 | 42 | SpawnedActors.Empty(); 43 | 44 | UE_LOG(LogObjectPool, Log, TEXT("Actor Array Size: %d"), GetLevel()->Actors.Num()); 45 | } 46 | 47 | void AActorSpawnPerformanceTest::TestFinishSpawning(AActor* actor) 48 | { 49 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::FinishSpawning); 50 | { 51 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::FinishSpawning_ExecuteConstruction); 52 | FEditorScriptExecutionGuard ScriptGuard; 53 | actor->ExecuteConstruction(FTransform(), nullptr, nullptr, false); 54 | } 55 | 56 | { 57 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::FinishSpawning_PostActorConstruction); 58 | actor->PostActorConstruction(); 59 | } 60 | }; 61 | 62 | AActor* AActorSpawnPerformanceTest::SpawnActors() 63 | { 64 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::SpawnActors); 65 | FName name = FName(*FString::Printf(TEXT("AActorSpawnPerformanceTest_Actors_%d"), SpawnNameUnique++)); 66 | ULevel* level = GetLevel(); 67 | AActor* actor = nullptr; 68 | 69 | { 70 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::SpawnActors_NewObject); 71 | actor = NewObject(level, ActorToSpawn, name, EObjectFlags::RF_Transactional); 72 | } 73 | 74 | if (!actor) 75 | { 76 | return nullptr; 77 | } 78 | 79 | { 80 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::SpawnActors_AddToLevel); 81 | level->Actors.Add(actor); 82 | level->ActorsForGC.Add(actor); 83 | } 84 | 85 | { 86 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::SpawnActors_PostSpawnInitialize); 87 | actor->PostSpawnInitialize(FTransform(), nullptr, nullptr, false, false, true); 88 | } 89 | 90 | { 91 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::SpawnActors_CheckDefaultSubobjects); 92 | actor->CheckDefaultSubobjects(); 93 | } 94 | 95 | { 96 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::SpawnActors_FinishSpawning); 97 | TestFinishSpawning(actor); 98 | //actor->FinishSpawning(FTransform()); 99 | } 100 | 101 | return actor; 102 | } 103 | 104 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/PoolInterface/PoolActorInterface.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "UObject/Interface.h" 7 | #include "PoolInterface.h" 8 | #include "PoolActorInterface.generated.h" 9 | 10 | class ULevel; 11 | class UWorld; 12 | // This class does not need to be modified. 13 | UINTERFACE(MinimalAPI, BlueprintType, NotBlueprintable) 14 | class UPoolActorInterface : public UPoolInterface 15 | { 16 | GENERATED_BODY() 17 | }; 18 | 19 | /** 20 | * An Interface for turning Actorclass to an PoolActorclass so can used in ObjectPoolSubsystem 21 | */ 22 | class OBJECTPOOLSYSTEM_API IPoolActorInterface : public IPoolInterface 23 | { 24 | GENERATED_BODY() 25 | 26 | // Add interface functions to this class. This is the class that will be inherited to implement this interface. 27 | public: 28 | 29 | /** release the actor */ 30 | UFUNCTION(BlueprintCallable, Category = "ObjectPool", meta = (Keywords = "Release", DisplayName = "Release To Pool", ScriptName = "ReleaseToPool")) 31 | virtual void K2_ReleaseToPool(){ ReleaseToPool(); }; 32 | 33 | /* 34 | * called to release this object back to ObjectPool 35 | */ 36 | virtual void ReleaseToPool() override; 37 | 38 | private: 39 | //when new actor, do some init work 40 | virtual void OnNewObject() override; 41 | 42 | //when actor destroyed do some clean work 43 | virtual void OnObjectDestroy() override; 44 | private: 45 | //the follows functions are only used by ObjectPoolSubsystem, so make it private 46 | friend class UObjectPoolSubsystem; 47 | 48 | /************************ interface for actor start ****************************************************/ 49 | 50 | /* 51 | * Attach/Place this actor to level, called by ObjectPoolSubsystem 52 | * will call the BeginPlay for this object and it's components 53 | */ 54 | virtual bool AttachToLevel(ULevel* levelSpawnIn, const FTransform& transform, ESpawnActorCollisionHandlingMethod CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::Undefined); 55 | 56 | /* 57 | * Detach this actor from level 58 | */ 59 | virtual void DetachFromLevel(); 60 | 61 | /************************ interface for actor end ******************************************************/ 62 | 63 | private: 64 | bool bFirstTimeAttach = true; 65 | 66 | private: 67 | /************************ function to make sure not use Actor::Destory() start *************************/ 68 | 69 | //when actor is destroyed, it will call Destroy() function, but we don't want this happen, so we need to abort it 70 | //also you can override the Destroy() function in your actor class to call ReleaseToPool() 71 | 72 | /* 73 | * should not call actor's Destroy() funtion, so regist a delegate to OnActorDestroy to abort 74 | * called when attach to the world 75 | */ 76 | void RegistOnActorDestroyCalledByWorld(UWorld* world); 77 | 78 | /* 79 | * Remove the delegate from OnActorDestroy 80 | * called when detach from the world 81 | */ 82 | void RemoveOnActorDestroyCalledByWorld(UWorld* world); 83 | 84 | /* 85 | * the real function to abort actor's Destroy() funtion 86 | */ 87 | void AbortActorDestroy(AActor* self); 88 | 89 | static FDelegateHandle AbortActorDestroyHandle; 90 | /************************ function to make sure not use Actor::Destory() over **************************/ 91 | 92 | 93 | //check if this is an Actor Object,if not return nullptr 94 | inline AActor* GetActor(); 95 | 96 | }; 97 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/DataAsset/LevelObjectPoolDataAsset.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "LevelObjectPoolDataAsset.h" 5 | #include "ObjectPoolSubsystem.h" 6 | #include "Macro/ObjectPoolSystemMacro.h" 7 | #include "PoolInterface/PoolInterface.h" 8 | 9 | bool ULevelObjectPoolDataAsset::RegisterToObjectPoolSubsystem(ULevel* level) 10 | { 11 | if (!level) 12 | { 13 | UE_LOG(LogObjectPool, Error, TEXT("ULevel is null")); 14 | return false; 15 | } 16 | 17 | //check if PoolClassArray has the same Class in it, if has, drop the later one 18 | TSet ClassSet; 19 | TArray> NewPoolClassArray; 20 | bool bHasSameClass = false; 21 | for (auto& ClassDataAsset : PoolClassArray) 22 | { 23 | if (!ClassDataAsset->Class.Get()) 24 | { 25 | UE_LOG(LogObjectPool, Error, TEXT("ActorClass in PoolClassDataAsset [%s] in LevelObjectPoolDataAsset [%s] is null") 26 | , *ClassDataAsset->GetName(), *GetName()); 27 | return false; 28 | } 29 | if (ClassSet.Contains(ClassDataAsset->Class.Get())) 30 | { 31 | UE_LOG(LogObjectPool, Warning 32 | , TEXT("ActorClass [%s] in PoolClassDataAsset [%s] in LevelObjectPoolDataAsset [%s] is the same as the previous one") 33 | , *ClassDataAsset->GetName(), *GetName()); 34 | bHasSameClass = true; 35 | } 36 | else 37 | { 38 | //check if the ActorClass implements PoolInterface 39 | if (!ClassDataAsset->Class->ImplementsInterface(UPoolInterface::StaticClass())) 40 | { 41 | UE_LOG(LogObjectPool, Error 42 | , TEXT("ActorClass in PoolClassDataAsset [%s] in LevelObjectPoolDataAsset [%s] is not a sub class of PoolInterface") 43 | , *ClassDataAsset->GetName(), *GetName()); 44 | return false; 45 | } 46 | 47 | ClassSet.Add(ClassDataAsset->Class.Get()); 48 | NewPoolClassArray.Add(ClassDataAsset); 49 | } 50 | } 51 | if (bHasSameClass) 52 | { 53 | this->PoolClassArray = NewPoolClassArray; 54 | } 55 | 56 | UWorld* World = level->GetWorld(); 57 | if (nullptr == World) 58 | { 59 | UE_LOG(LogObjectPool, Error, TEXT("UWorld is null, the level is not in a World")); 60 | return false; 61 | } 62 | 63 | UObjectPoolSubsystem* ObjectPoolSubsystem = World->GetSubsystem(); 64 | if (ObjectPoolSubsystem) 65 | { 66 | return ObjectPoolSubsystem->RegisterLevelObjectPool(this, level); 67 | } 68 | else 69 | { 70 | UE_LOG(LogObjectPool, Error, TEXT("ObjectPoolSubsystem is null")); 71 | } 72 | 73 | return false; 74 | } 75 | 76 | bool ULevelObjectPoolDataAsset::K2_RegisterToObjectPoolSubsystem(ULevel* level) 77 | { 78 | return RegisterToObjectPoolSubsystem(level); 79 | } 80 | 81 | void ULevelObjectPoolDataAsset::UnRegisterFromObjectPoolSubsystem(ULevel* level) 82 | { 83 | if (!level) 84 | { 85 | UE_LOG(LogObjectPool, Error, TEXT("ULevel is null")); 86 | return; 87 | } 88 | UWorld* World = level->GetWorld(); 89 | if (nullptr == World) 90 | { 91 | UE_LOG(LogObjectPool, Error, TEXT("UWorld is null, the level is not in a World")); 92 | return; 93 | } 94 | UObjectPoolSubsystem* ObjectPoolSubsystem = World->GetSubsystem(); 95 | if (ObjectPoolSubsystem) 96 | { 97 | ObjectPoolSubsystem->UnRegisterLevelObjectPool(this, level); 98 | } 99 | else 100 | { 101 | UE_LOG(LogObjectPool, Error, TEXT("ObjectPoolSubsystem is null")); 102 | } 103 | } 104 | 105 | void ULevelObjectPoolDataAsset::K2_UnRegisterFromObjectPoolSubsystem(ULevel* level) 106 | { 107 | UnRegisterFromObjectPoolSubsystem(level); 108 | } 109 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/ObjectPoolSubsystem.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "ObjectPoolSubsystem.h" 5 | #include "Macro/ObjectPoolSystemMacro.h" 6 | #include "DataAsset/LevelObjectPoolDataAsset.h" 7 | #include "PoolInterface/PoolActorInterface.h" 8 | #include "ObjectPool/LevelObjectPool.h" 9 | 10 | AActor* UObjectPoolSubsystem::SpawnActor(const UObject* WorldContextObject, TSubclassOf ActorClass, const FTransform& SpawnTransform, ESpawnActorCollisionHandlingMethod CollisionHandlingOverride) 11 | { 12 | if (!WorldContextObject) 13 | { 14 | UE_LOG(LogObjectPool, Error, TEXT("WorldContextObject is null")); 15 | return nullptr; 16 | } 17 | 18 | ULevel* level = WorldContextObject->GetTypedOuter(); 19 | if (!level) //make sure has an level to spawn the new actor in 20 | { 21 | UE_LOG(LogObjectPool, Error, TEXT("WorldContextObject is not in a level")); 22 | return nullptr; 23 | } 24 | 25 | if (!ActorClass.Get()) 26 | { 27 | UE_LOG(LogObjectPool, Error, TEXT("ActorClass is null")); 28 | return nullptr; 29 | } 30 | 31 | if (!PoolMap.Contains(level)) 32 | { 33 | UE_LOG(LogObjectPool, Error, TEXT("level pool is not registed")); 34 | return nullptr; 35 | } 36 | 37 | ULevelObjectPool* pool = PoolMap[level]; 38 | if (!pool) 39 | { 40 | UE_LOG(LogObjectPool, Error, TEXT("level pool is null")); 41 | return nullptr; 42 | } 43 | 44 | UObject* object = pool->AllocateObject(ActorClass); 45 | 46 | if (nullptr == object) 47 | { 48 | //UE_LOG(LogObjectPool, Error, TEXT("AllocateObject return null")); 49 | return nullptr; 50 | } 51 | 52 | AActor* actor = Cast(object); 53 | if (!actor) 54 | { 55 | UE_LOG(LogObjectPool, Error, TEXT("Cast Failed!")); 56 | return nullptr; 57 | } 58 | 59 | IPoolActorInterface* poolactor = Cast(actor); 60 | if (!poolactor) 61 | { 62 | UE_LOG(LogObjectPool, Error, TEXT("AllocateObject return null")); 63 | return nullptr; 64 | } 65 | 66 | bool flag = poolactor->AttachToLevel(level, SpawnTransform, CollisionHandlingOverride); 67 | if (!flag) 68 | { 69 | UE_LOG(LogObjectPool, Error, TEXT("AttachToLevel failed")); 70 | poolactor->ReleaseToPool(); 71 | return nullptr; 72 | } 73 | 74 | return actor; 75 | } 76 | 77 | UObject* UObjectPoolSubsystem::GetUObject(const UObject* WorldContextObject, TSubclassOf ObjectClass) 78 | { 79 | check(WorldContextObject); 80 | 81 | if (!ObjectClass.Get()) 82 | { 83 | UE_LOG(LogObjectPool, Error, TEXT("ObjectClass is null")); 84 | return nullptr; 85 | } 86 | 87 | ULevel* level = WorldContextObject->GetTypedOuter(); 88 | if (!level) //make sure has an level to spawn the new actor in 89 | { 90 | UE_LOG(LogObjectPool, Error, TEXT("WorldContextObject is not in a level")); 91 | return nullptr; 92 | } 93 | 94 | if (!PoolMap.Contains(level)) 95 | { 96 | UE_LOG(LogObjectPool, Error, TEXT("level pool is not registed")); 97 | return nullptr; 98 | } 99 | 100 | ULevelObjectPool* pool = PoolMap[level]; 101 | if (!pool) 102 | { 103 | UE_LOG(LogObjectPool, Error, TEXT("level pool is null")); 104 | return nullptr; 105 | } 106 | 107 | return pool->AllocateObject(ObjectClass); 108 | } 109 | 110 | ULevelObjectPool* UObjectPoolSubsystem::GetLevelObjectPool(ULevel* level) 111 | { 112 | if (!level) 113 | { 114 | UE_LOG(LogObjectPool, Error, TEXT("level is null")); 115 | return nullptr; 116 | } 117 | 118 | if (!PoolMap.Contains(level)) 119 | { 120 | UE_LOG(LogObjectPool, Error, TEXT("this level is not registed, level: [%s]"), *level->GetName()); 121 | return nullptr; 122 | } 123 | 124 | ULevelObjectPool* levelpool = PoolMap[level]; 125 | if (!levelpool) 126 | { 127 | UE_LOG(LogObjectPool, Error, TEXT("levelpool is null")); 128 | return nullptr; 129 | } 130 | 131 | UE_LOG(LogObjectPool, Log, TEXT("GetLevelObjectPool, level [%s], levelobjectpool [%s]") 132 | , *level->GetName(), *levelpool->GetName()); 133 | 134 | return levelpool; 135 | } 136 | 137 | void UObjectPoolSubsystem::Initialize(FSubsystemCollectionBase& Collection) 138 | { 139 | Super::Initialize(Collection); 140 | UE_LOG(LogObjectPool, Log, TEXT("ObjectPoolSubsystem Initialize")); 141 | } 142 | 143 | void UObjectPoolSubsystem::Deinitialize() 144 | { 145 | Super::Deinitialize(); 146 | UE_LOG(LogObjectPool, Log, TEXT("ObjectPoolSubsystem Deinitialize")); 147 | } 148 | 149 | bool UObjectPoolSubsystem::RegisterLevelObjectPool(ULevelObjectPoolDataAsset* LevelObjectPoolDataAsset, ULevel* level) 150 | { 151 | if (!LevelObjectPoolDataAsset) 152 | { 153 | UE_LOG(LogObjectPool, Error, TEXT("LevelObjectPoolDataAsset is null")); 154 | return false; 155 | } 156 | if (!level) 157 | { 158 | UE_LOG(LogObjectPool, Error, TEXT("level is null")); 159 | return false; 160 | } 161 | 162 | TObjectPtr levelpool; 163 | if (!PoolMap.Contains(level)) 164 | { 165 | //new a levelpool 166 | levelpool = NewObject(this); 167 | levelpool->Init(level); 168 | PoolMap.Add(level, levelpool); 169 | } 170 | 171 | bool succeed_state = levelpool->RegisterLevelObjectPool(LevelObjectPoolDataAsset); 172 | if (!succeed_state) 173 | { 174 | levelpool->UnRegisterLevelObjectPool(LevelObjectPoolDataAsset); 175 | UE_LOG(LogObjectPool, Error, TEXT("RegistLevelObjectPool failed")) 176 | } 177 | 178 | UE_LOG(LogObjectPool, Log, TEXT("RegistLevelObjectPool, LevelObjectPoolDataAsset [%s], level [%s], levelpool [%s]"), 179 | *LevelObjectPoolDataAsset->GetName(), *level->GetName(), *levelpool->GetName()); 180 | return succeed_state; 181 | } 182 | 183 | void UObjectPoolSubsystem::UnRegisterLevelObjectPool(ULevelObjectPoolDataAsset* LevelObjectPoolDataAsset, ULevel* level) 184 | { 185 | if (!LevelObjectPoolDataAsset) 186 | { 187 | UE_LOG(LogObjectPool, Error, TEXT("LevelObjectPoolDataAsset is null")); 188 | return; 189 | } 190 | if (!level) 191 | { 192 | UE_LOG(LogObjectPool, Error, TEXT("level is null")); 193 | return; 194 | } 195 | 196 | if (!PoolMap.Contains(level)) 197 | { 198 | UE_LOG(LogObjectPool, Error, TEXT("this level [%s] has not registed any levelpool"), *level->GetName()); 199 | return; 200 | } 201 | 202 | ULevelObjectPool* levelpool = PoolMap[level]; 203 | if (!levelpool) 204 | { 205 | UE_LOG(LogObjectPool, Error, TEXT("levelpool is null")); 206 | return; 207 | } 208 | 209 | levelpool->UnRegisterLevelObjectPool(LevelObjectPoolDataAsset); 210 | 211 | UE_LOG(LogObjectPool, Log, TEXT("UnRegistLevelObjectPool, LevelObjectPoolDataAsset [%s], level [%s], levelpool [%s]"), 212 | *LevelObjectPoolDataAsset->GetName(), *level->GetName(), *levelpool->GetName()); 213 | return; 214 | } 215 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/ObjectPool/SingleClassObjectPool.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "SingleClassObjectPool.h" 5 | #include "Macro/ObjectPoolSystemMacro.h" 6 | #include "DataAsset/PoolClassDataAsset.h" 7 | #include "PoolInterface/PoolInterface.h" 8 | #include "LevelObjectPool.h" 9 | 10 | bool USingleClassObjectPool::Init(UClass* Class, ULevel* level) 11 | { 12 | if (nullptr == Class) 13 | { 14 | UE_LOG(LogObjectPool, Error, TEXT("Class is null")); 15 | return false; 16 | } 17 | ObjectClass = Class; 18 | Level = level; 19 | InitFlag = true; 20 | 21 | return true; 22 | } 23 | 24 | bool USingleClassObjectPool::Register(UPoolClassDataAsset* PoolClassDataAsset, TSharedPtr ChunkPool, uint32 StartPos, ULevelObjectPool* InLevelObjectPool) 25 | { 26 | LevelObjectPool = InLevelObjectPool; 27 | if (nullptr == PoolClassDataAsset) 28 | { 29 | UE_LOG(LogObjectPool, Error, TEXT("PoolClassDataAsset is null")); 30 | return false; 31 | } 32 | 33 | //do not register twice 34 | if (PoolClassDataAssetArray.Contains(PoolClassDataAsset)) 35 | { 36 | UE_LOG(LogObjectPool, Warning, TEXT("PoolClassDataAsset is already registered, no need to registed twice")); 37 | return true; 38 | } 39 | 40 | this->UsingChunkPool = ChunkPool; 41 | this->StartPosInPool = StartPos; 42 | 43 | //put these chunk to allchain and freechain 44 | for (int32 i = 0; i < PoolClassDataAsset->PreallocateCount; ++i) 45 | { 46 | //AllChain.Add(UsingChunkPool->GetChunk(StartPosInPool + i)); 47 | AllChain.AddTail(&UsingChunkPool->ChunkArray[StartPosInPool + i]); 48 | FreeChain.AddTail(&UsingChunkPool->ChunkArray[StartPosInPool + i]); 49 | } 50 | 51 | //init these object in allchain 52 | uint32 PosInAllChain = 0; 53 | //Traversal all the node in allchain 54 | for (ChunkListNode* Node = FreeChain.GetHead(); Node; Node = Node->GetNextNode()) 55 | { 56 | FObjectChunk* Chunk = Node->GetValue(); 57 | 58 | if (nullptr != Chunk->Object) 59 | { 60 | UE_LOG(LogObjectPool, Warning, TEXT("Chunk->Object is valid before init")); 61 | continue; 62 | } 63 | 64 | //real new object 65 | Chunk->Object = NewThisObject(PosInAllChain++); 66 | 67 | if (nullptr != Chunk->Object) 68 | { 69 | //this object should not be garbage collected,until it is released to pool and unregistered 70 | //Log info 71 | //UE_LOG(LogObjectPool, Log, TEXT("New Object succeed, ClassName: [%s], ObjectName: [%s]"), *ObjectClass->GetName(), *Chunk->Object->GetName()); 72 | IPoolInterface* PoolInterface = CastChecked(Chunk->Object); 73 | PoolInterface->OnNewObject(); 74 | PoolInterface->pool = this; 75 | PoolInterface->node = Node; 76 | 77 | Chunk->Object->AddToRoot(); 78 | } 79 | else 80 | { 81 | UE_LOG(LogObjectPool, Error, TEXT("Chunk->Object is invalid after init, PosInAllChain:[%d]"), PosInAllChain); 82 | } 83 | } 84 | 85 | return true; 86 | } 87 | 88 | void USingleClassObjectPool::UnRegister(UPoolClassDataAsset* PoolClassDataAsset) 89 | { 90 | if (nullptr == PoolClassDataAsset) 91 | { 92 | UE_LOG(LogObjectPool, Error, TEXT("PoolClassDataAsset is null")); 93 | return; 94 | } 95 | //Log Info 96 | UE_LOG(LogObjectPool, Log, TEXT("Start UnRegister SingleClassObjectPool, ClassName: [%s]"), *ObjectClass->GetName()); 97 | 98 | //Traversal all the object in usedchain 99 | for (FObjectChunk* Chunk : UsedChain) 100 | { 101 | //if the object in usedchain is valid, release it to pool 102 | if (nullptr != Chunk->Object) 103 | { 104 | IPoolInterface* PoolInterface = CastChecked(Chunk->Object); 105 | PoolInterface->ReleaseToPool(); 106 | } 107 | } 108 | UsedChain.Empty(); 109 | 110 | //Traversal all the object in allchain 111 | for (FObjectChunk* Chunk : AllChain) 112 | { 113 | //if the object in allchain is valid, release it to pool 114 | if (nullptr != Chunk->Object) 115 | { 116 | //Log info 117 | //UE_LOG(LogObjectPool, Log, TEXT("Release Object, ClassName: [%s], ObjectName: [%s]", *ObjectClass->GetName(), *Chunk->Object->GetName()); 118 | //let the object can be GC 119 | Chunk->Object->RemoveFromRoot(); 120 | Chunk->Object->MarkAsGarbage(); 121 | IPoolInterface* PoolInterface = CastChecked(Chunk->Object); 122 | PoolInterface->OnObjectDestroy(); 123 | Chunk->ReSet(); 124 | } 125 | } 126 | //clear allchain 127 | AllChain.Empty(); 128 | 129 | //Reset this 130 | this->UsingChunkPool.Reset(); 131 | this->StartPosInPool = 0; 132 | 133 | //Log Info 134 | UE_LOG(LogObjectPool, Log, TEXT("End UnRegister SingleClassObjectPool, ClassName: [%s]"), *ObjectClass->GetName()); 135 | } 136 | 137 | UObject* USingleClassObjectPool::AllocateObject() 138 | { 139 | //Log Info 140 | //UE_LOG(LogObjectPool, Log, TEXT("AllocateObject An Object in SingleClassObjectPool, ClassName: [%s]"), *ObjectClass->GetName()); 141 | 142 | //Get FreeChain Head 143 | ChunkListNode* FreeChainHead = FreeChain.GetHead(); 144 | if (nullptr == FreeChainHead) 145 | { 146 | //UE_LOG(LogObjectPool, Error, TEXT("FreeChainHead is null")); 147 | return nullptr; 148 | } 149 | 150 | //Remove FreeChain Head 151 | FreeChain.RemoveNode(FreeChainHead, false); 152 | ChunkListNode* TargetNode = FreeChainHead; 153 | 154 | //Add FreeChain Head to UsedChain 155 | UsedChain.AddTail(TargetNode); 156 | 157 | //Get the object in FreeChain Head 158 | UObject* object = TargetNode->GetValue()? FreeChainHead->GetValue()->Object : nullptr; 159 | if (nullptr == object) 160 | { 161 | UE_LOG(LogObjectPool, Error, TEXT("object is null")); 162 | return nullptr; 163 | } 164 | 165 | TargetNode->GetValue()->bIsAllocated = true; 166 | 167 | return object; 168 | } 169 | 170 | void USingleClassObjectPool::FreeObject(ChunkListNode* node) 171 | { 172 | if (nullptr == node) 173 | { 174 | UE_LOG(LogObjectPool, Error, TEXT("node is null when free an Object")); 175 | return; 176 | } 177 | 178 | if (!node->GetValue()->bIsAllocated) 179 | { 180 | //in case of double free 181 | return; 182 | } 183 | 184 | //Log Info 185 | //UE_LOG(LogObjectPool, Log, TEXT("FreeObject An Object to SingleClassObjectPool, ClassName: [%s]"), *ObjectClass->GetName()); 186 | 187 | //Remove UsedChain Node 188 | UsedChain.RemoveNode(node, false); 189 | //Add UsedChain Node to FreeChain 190 | FreeChain.AddTail(node); 191 | //Get the object in UsedChain Node 192 | UObject* object = node->GetValue() ? node->GetValue()->Object : nullptr; 193 | if (nullptr == object) 194 | { 195 | UE_LOG(LogObjectPool, Error, TEXT("object is null")); 196 | return; 197 | } 198 | node->GetValue()->bIsAllocated = false; 199 | } 200 | 201 | void USingleClassObjectPool::Tick(float DeltaTime) 202 | { 203 | //Todo: tick pool to expand or shrink, or soft load 204 | } 205 | 206 | UObject* USingleClassObjectPool::NewThisObject(uint32 PosInAllChain) 207 | { 208 | //Log info 209 | //UE_LOG(LogObjectPool, Log, TEXT("NewThisObject in SingleClassObjectPool, ClassName: [%s]"), *ObjectClass->GetName()); 210 | 211 | FStaticConstructObjectParameters Params(ObjectClass.Get()); 212 | Params.Outer = Level.Get(); 213 | //PosInAllChain to string 214 | Params.Name = FName(ObjectClass->GetName() + "_" + this->GetName() + "_" + FString::FromInt(PosInAllChain)); 215 | Params.SetFlags = EObjectFlags::RF_Transactional; 216 | Params.Template = nullptr; 217 | Params.bCopyTransientsFromClassDefaults = false; 218 | Params.InstanceGraph = nullptr; 219 | Params.ExternalPackage = nullptr; 220 | return StaticConstructObject_Internal(Params); 221 | } 222 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/ObjectPool/LevelObjectPool.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "LevelObjectPool.h" 5 | #include "Macro/ObjectPoolSystemMacro.h" 6 | #include "DataAsset/LevelObjectPoolDataAsset.h" 7 | #include "SingleClassObjectPool.h" 8 | 9 | bool ULevelObjectPool::RegisterLevelObjectPool(ULevelObjectPoolDataAsset* LevelObjectPoolDataAsset) 10 | { 11 | if (!LevelObjectPoolDataAsset) 12 | { 13 | UE_LOG(LogObjectPool, Error, TEXT("LevelObjectPoolDataAsset is null")); 14 | return false; 15 | } 16 | 17 | //Log info 18 | UE_LOG(LogObjectPool, Log, TEXT("Regist LevelObjectPoolDataAsset [%s] in Level [%s], levelptr: [%d]") 19 | , *LevelObjectPoolDataAsset->GetName(), *Level->GetName(), Level.Get()); 20 | 21 | //caculate the total number of objects in the LevelObjectPoolDataAsset 22 | uint32 TotalNum = 0; 23 | for (auto& ClassData : LevelObjectPoolDataAsset->PoolClassArray) 24 | { 25 | //Check if the ActorClass is null in PoolClassDataAsset 26 | if (!ClassData->Class.Get()) 27 | { 28 | UE_LOG(LogObjectPool, Error, TEXT("ActorClass in PoolClassDataAsset [%s] in LevelObjectPoolDataAsset [%s] is null") 29 | , *ClassData->GetName(), *LevelObjectPoolDataAsset->GetName()); 30 | return false; 31 | } 32 | TotalNum += ClassData->PreallocateCount; 33 | } 34 | 35 | //add a new chunkpool for this LevelObjectPoolDataAsset 36 | ChunkPoolArray& ChunkPool = Map_ChunkPool.FindOrAdd(LevelObjectPoolDataAsset); 37 | TSharedPtr pool = MakeShareable(new FChunkPool(TotalNum)); 38 | ChunkPool.Add(pool); 39 | 40 | 41 | uint32 PosInChunkPool = 0; 42 | //Traversal all the class in the LevelObjectPoolDataAsset 43 | for (auto& ClassData : LevelObjectPoolDataAsset->PoolClassArray) 44 | { 45 | //Check if the pool of this class has been created 46 | TObjectPtr classinpool = nullptr; 47 | if (!Map_ClassPool.Contains(ClassData->Class)) 48 | { 49 | classinpool = NewObject(this); 50 | classinpool->Init(ClassData->Class,Level.Get()); 51 | Map_ClassPool.Add(ClassData->Class, classinpool); 52 | } 53 | else 54 | { 55 | classinpool = Map_ClassPool[ClassData->Class]; 56 | } 57 | 58 | //SingleClassPool register 59 | bool flag = classinpool->Register(ClassData, pool, PosInChunkPool, this); 60 | if (!flag) 61 | { 62 | UE_LOG(LogObjectPool, Error, TEXT("Regist PoolClassData [%s] LevelObjectPoolDataAsset [%s] in Level [%s] failed") 63 | , *ClassData->GetName(), *LevelObjectPoolDataAsset->GetName(), *Level->GetName()); 64 | return false; 65 | } 66 | PosInChunkPool += ClassData->PreallocateCount; 67 | } 68 | 69 | UE_LOG(LogObjectPool, Warning, TEXT("Regist LevelObjectPoolDataAsset [%s] in Level [%s] success") 70 | , *LevelObjectPoolDataAsset->GetName(), *Level->GetName()); 71 | return true; 72 | } 73 | 74 | void ULevelObjectPool::UnRegisterLevelObjectPool(ULevelObjectPoolDataAsset* LevelObjectPoolDataAsset) 75 | { 76 | if (nullptr == LevelObjectPoolDataAsset) 77 | { 78 | UE_LOG(LogObjectPool, Error, TEXT("LevelObjectPoolDataAsset is null")); 79 | return; 80 | } 81 | 82 | //make sure the LevelObjectPoolDataAsset has been registered 83 | if (!Map_ChunkPool.Contains(LevelObjectPoolDataAsset)) 84 | { 85 | UE_LOG(LogObjectPool, Error, TEXT("Try to unregister LevelObjectPoolDataAsset [%s] in Level [%s] failed, because the LevelObjectPoolDataAsset has not been registered") 86 | , *LevelObjectPoolDataAsset->GetName(), *Level->GetName()); 87 | return; 88 | } 89 | 90 | //Log info 91 | UE_LOG(LogObjectPool, Log, TEXT("UnRegist LevelObjectPoolDataAsset [%s] in Level [%s]") 92 | , *LevelObjectPoolDataAsset->GetName(), *Level->GetName()); 93 | 94 | //clear all singleclasspool registed by LevelObjectPoolDataAsset in this level 95 | for (auto& ClassData : LevelObjectPoolDataAsset->PoolClassArray) 96 | { 97 | //Check if the ActorClass is null in PoolClassDataAsset 98 | if (!ClassData->Class.Get()) 99 | { 100 | UE_LOG(LogObjectPool, Error, TEXT("ActorClass in PoolClassDataAsset [%s] in LevelObjectPoolDataAsset [%s] is null") 101 | , *ClassData->GetName(), *LevelObjectPoolDataAsset->GetName()); 102 | } 103 | 104 | //Check if the pool of this class has been created 105 | if (!Map_ClassPool.Contains(ClassData->Class)) 106 | { 107 | UE_LOG(LogObjectPool, Error, TEXT("Try to unregister PoolClassData [%s] LevelObjectPoolDataAsset [%s] in Level [%s] failed, because the pool of this class has not been created") 108 | , *ClassData->GetName(), *LevelObjectPoolDataAsset->GetName(), *Level->GetName()); 109 | continue; 110 | } 111 | 112 | TObjectPtr classinpool = Map_ClassPool[ClassData->Class]; 113 | 114 | //SingleClassPool unregister 115 | classinpool->UnRegister(ClassData); 116 | //Log info 117 | UE_LOG(LogObjectPool, Log, TEXT("UnRegist PoolClassData [%s] LevelObjectPoolDataAsset [%s] in Level [%s] success") 118 | , *ClassData->GetName(), *LevelObjectPoolDataAsset->GetName(), *Level->GetName()); 119 | 120 | //check if this classinpool need to remove 121 | //classinpool->MarkAsGarbage(); 122 | } 123 | 124 | //release the chunkpool for this LevelObjectPoolDataAsset 125 | } 126 | 127 | UUserWidget* ULevelObjectPool::SetLevelObjectPoolUIByClass(TSubclassOf uiClass) 128 | { 129 | APlayerController* control = this->GetWorld()->GetGameInstance()->GetFirstLocalPlayerController(); 130 | if (control) 131 | { 132 | LevelObjectPoolUI = CreateWidget(control, uiClass); 133 | } 134 | else 135 | { 136 | UE_LOG(LogObjectPool, Error, TEXT("SpawnTestActor::BeginPlay: control is null")); 137 | } 138 | 139 | if (!LevelObjectPoolUI) 140 | { 141 | UE_LOG(LogObjectPool, Error, TEXT("SpawnTestActor::BeginPlay: LevelObjectPoolUI is null")); 142 | return nullptr; 143 | } 144 | 145 | SetLevelObjectPoolUI(LevelObjectPoolUI); 146 | 147 | return LevelObjectPoolUI; 148 | } 149 | 150 | void ULevelObjectPool::SetLevelObjectPoolUI(ULevelObjectPoolUI* ui) 151 | { 152 | if (!ui) 153 | { 154 | UE_LOG(LogObjectPool, Warning, TEXT("SetLevelObjectPoolUI: ui is null")); 155 | return; 156 | } 157 | LevelObjectPoolUI = ui; 158 | LevelObjectPoolUI->LevelObjectPoolData.LevelName = Level->GetName(); 159 | LevelObjectPoolUI->LevelObjectPoolData.ObjectClassCount = Map_ClassPool.Num(); 160 | 161 | UpdateData(); 162 | } 163 | 164 | UObject* ULevelObjectPool::AllocateObject(UClass* Class) 165 | { 166 | if (!Map_ClassPool.Contains(Class)) 167 | { 168 | UE_LOG(LogObjectPool, Error, TEXT("Try to allocate object of class [%s] in Level [%s] failed, because the pool of this class has not been created") 169 | , *Class->GetName(), *Level->GetName()); 170 | return nullptr; 171 | } 172 | 173 | //a counter to call UpdateData every 100 times 174 | static int32 Counter = 0; 175 | if (Counter++ % 100 == 0) 176 | { 177 | UpdateData(); 178 | Counter = 0; 179 | } 180 | return Map_ClassPool[Class]->AllocateObject(); 181 | } 182 | 183 | void ULevelObjectPool::TickPool(float DeltaTime) 184 | { 185 | for (auto& ClassPool : Map_ClassPool) 186 | { 187 | ClassPool.Value->Tick(DeltaTime); 188 | } 189 | } 190 | 191 | void ULevelObjectPool::UpdateData() 192 | { 193 | if (!LevelObjectPoolUI) 194 | { 195 | return; 196 | } 197 | 198 | LevelObjectPoolUI->SingleClassPoolData.Empty(); 199 | for (auto& ClassPool : Map_ClassPool) 200 | { 201 | FSingleClassPoolData data; 202 | data.ClassName = ClassPool.Key->GetName(); 203 | data.TotalObjectCount = ClassPool.Value->getallcount(); 204 | data.ReadyObjectCount = ClassPool.Value->getreadycount(); 205 | data.InUseObjectCount = ClassPool.Value->getusedcount(); 206 | data.InPoolObjectCount = ClassPool.Value->getfreecount(); 207 | LevelObjectPoolUI->SingleClassPoolData.Add(data); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/Test/CubeSpawner/SpawnTestActor.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "SpawnTestActor.h" 5 | #include "Private/Macro/ObjectPoolSystemMacro.h" 6 | #include "Blueprint/GameViewportSubsystem.h" 7 | #include "PoolInterface/PoolInterface.h" 8 | #include "ObjectPoolSubsystem.h" 9 | 10 | // Sets default values 11 | ASpawnTestActor::ASpawnTestActor() 12 | { 13 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 14 | PrimaryActorTick.bCanEverTick = true; 15 | } 16 | 17 | // Called when the game starts or when spawned 18 | void ASpawnTestActor::BeginPlay() 19 | { 20 | if (!SpawnTestUIInstance) 21 | { 22 | APlayerController* control = this->GetGameInstance()->GetFirstLocalPlayerController(); 23 | if (control) 24 | { 25 | SpawnTestUIInstance = CreateWidget(control, SpawnTestUIClass); 26 | } 27 | else 28 | { 29 | UE_LOG(LogObjectPool, Error, TEXT("SpawnTestActor::BeginPlay: control is null")); 30 | } 31 | } 32 | 33 | Super::BeginPlay(); 34 | 35 | if (SpawnTestUIInstance) 36 | { 37 | UIData = &SpawnTestUIInstance->UIData; 38 | auto viewsubsystem = GetGameInstance()->GetEngine()->GetEngineSubsystem(); 39 | if (viewsubsystem) 40 | { 41 | viewsubsystem->AddWidget(SpawnTestUIInstance, FGameViewportWidgetSlot()); 42 | UE_LOG(LogObjectPool, Log, TEXT("SpawnTestActor::BeginPlay: AddWidget")); 43 | } 44 | else 45 | { 46 | UE_LOG(LogObjectPool, Error, TEXT("SpawnTestActor::BeginPlay: viewsubsystem is null")); 47 | } 48 | FOnUIDataChange::FDelegate handle; 49 | handle.BindUFunction(this, TEXT("UpdateData")); 50 | SpawnTestUIInstance->AddHandleOnUIDataChange(handle); 51 | 52 | } 53 | else 54 | { 55 | UE_LOG(LogObjectPool, Error, TEXT("SpawnTestActor::BeginPlay: SpawnTestUI is null")); 56 | } 57 | } 58 | 59 | void ASpawnTestActor::EndPlay(const EEndPlayReason::Type EndPlayReason) 60 | { 61 | if (SpawnTestUIInstance) 62 | { 63 | SpawnTestUIInstance->ClearHandleOnUIDataChange(); 64 | } 65 | 66 | Super::EndPlay(EndPlayReason); 67 | } 68 | 69 | // Called every frame 70 | void ASpawnTestActor::Tick(float DeltaTime) 71 | { 72 | TRACE_CPUPROFILER_EVENT_SCOPE(ASpawnTestActor::Tick); 73 | Super::Tick(DeltaTime); 74 | TickSpawn(); 75 | StayCountAutoDestroy(); 76 | } 77 | 78 | void ASpawnTestActor::BlueprintSpawnNormal_Implementation() 79 | { 80 | } 81 | 82 | void ASpawnTestActor::BlueprintSpawnObjectPool_Implementation() 83 | { 84 | } 85 | 86 | void ASpawnTestActor::TickSpawn() 87 | { 88 | TRACE_CPUPROFILER_EVENT_SCOPE(ASpawnTestActor::TickSpawn); 89 | if (!UIData) 90 | { 91 | UE_LOG(LogObjectPool, Error, TEXT("SpawnTestActor::TickSpawn: UIData is null")); 92 | return; 93 | } 94 | if (UIData->bStartSpawn) 95 | { 96 | if (UIData->bBlueprintSpawn) 97 | { 98 | if (UIData->bUseObjectPool) 99 | { 100 | for (int32 i = 0; i < UIData->SpawnCountPerTick; ++i) 101 | { 102 | BlueprintSpawnObjectPool(); 103 | } 104 | } 105 | else 106 | { 107 | for (int32 i = 0; i < UIData->SpawnCountPerTick; ++i) 108 | { 109 | BlueprintSpawnNormal(); 110 | } 111 | } 112 | } 113 | else 114 | { 115 | if (UIData->bUseObjectPool) 116 | { 117 | for (int32 i = 0; i < UIData->SpawnCountPerTick; ++i) 118 | { 119 | SpawnObjectPool(); 120 | } 121 | } 122 | else 123 | { 124 | for (int32 i = 0; i < UIData->SpawnCountPerTick; ++i) 125 | { 126 | SpawnNormal(); 127 | } 128 | } 129 | } 130 | } 131 | 132 | } 133 | 134 | void ASpawnTestActor::SpawnNormal() 135 | { 136 | TRACE_CPUPROFILER_EVENT_SCOPE(ASpawnTestActor::SpawnNormal); 137 | UWorld* world = GetWorld(); 138 | FVector location = GetSpawnLocation(); 139 | AActor* actor = world->SpawnActor(NormalSpawnClass, location, FRotator()); 140 | if (actor) 141 | { 142 | ActorList.AddTail(actor); 143 | } 144 | else 145 | { 146 | UE_LOG(LogObjectPool, Error, TEXT("SpawnTestActor::SpawnNormal: actor is null")); 147 | } 148 | } 149 | 150 | void ASpawnTestActor::SpawnObjectPool() 151 | { 152 | TRACE_CPUPROFILER_EVENT_SCOPE(ASpawnTestActor::SpawnObjectPool); 153 | UObjectPoolSubsystem* poolsubsystem = GetWorld()->GetSubsystem(); 154 | if (poolsubsystem) 155 | { 156 | FVector location = GetSpawnLocation(); 157 | FTransform transform; 158 | transform.SetLocation(location); 159 | AActor* actor = nullptr; 160 | { 161 | TRACE_CPUPROFILER_EVENT_SCOPE(ASpawnTestActor::SpawnObjectPool_SpawnActor); 162 | actor = poolsubsystem->SpawnActor(this, ObjectPoolSpawnClass, transform); 163 | } 164 | 165 | if (actor) 166 | { 167 | ActorList.AddTail(actor); 168 | } 169 | else 170 | { 171 | UE_LOG(LogObjectPool, Error, TEXT("SpawnTestActor::SpawnObjectPool: actor is null")); 172 | } 173 | } 174 | else 175 | { 176 | UE_LOG(LogObjectPool, Error, TEXT("SpawnTestActor::SpawnObjectPool: poolsubsystem is null")); 177 | } 178 | } 179 | 180 | void ASpawnTestActor::StayCountAutoDestroy() 181 | { 182 | TRACE_CPUPROFILER_EVENT_SCOPE(ASpawnTestActor::StayCountAutoDestroy); 183 | if (UIData && UIData->StayCount > 0) 184 | { 185 | for (int32 count = GetActorCount(); count > UIData->StayCount; count = GetActorCount()) 186 | { 187 | DestroyOneActor(); 188 | } 189 | } 190 | } 191 | 192 | uint32 ASpawnTestActor::GetActorCount() 193 | { 194 | return ActorList.Num(); 195 | } 196 | 197 | void ASpawnTestActor::DestroyOneActor() 198 | { 199 | TRACE_CPUPROFILER_EVENT_SCOPE(ASpawnTestActor::DestroyOneActor); 200 | if (ActorList.Num() > 0) 201 | { 202 | auto node = ActorList.GetHead(); 203 | if (node) 204 | { 205 | AActor* actor = node->GetValue(); 206 | if (actor) 207 | { 208 | if (actor->Implements()) 209 | { 210 | IPoolInterface* poolactor = Cast(actor); 211 | if (poolactor) 212 | { 213 | TRACE_CPUPROFILER_EVENT_SCOPE(UPoolInterface::ReleaseToPool); 214 | poolactor->ReleaseToPool(); 215 | } 216 | else 217 | { 218 | UE_LOG(LogObjectPool, Error, TEXT("SpawnTestActor::DestroyOneActor: poolactor is null")); 219 | } 220 | } 221 | else 222 | { 223 | TRACE_CPUPROFILER_EVENT_SCOPE(AActor::Destroy); 224 | actor->Destroy(); 225 | } 226 | } 227 | else 228 | { 229 | UE_LOG(LogObjectPool, Error, TEXT("SpawnTestActor::DestroyOneActor: actor is null")); 230 | } 231 | ActorList.RemoveNode(node); 232 | } 233 | } 234 | } 235 | 236 | FVector ASpawnTestActor::GetSpawnLocation() 237 | { 238 | FVector re = SpawnLocationInterval * SpawnPosition; 239 | USceneComponent* scene = GetRootComponent(); 240 | if (scene) 241 | { 242 | re += scene->GetComponentLocation(); 243 | } 244 | //move SpawnPosition to next point, order as x,y,z, limited by SpawnLocationCount 245 | SpawnPosition.X++; 246 | if (SpawnPosition.X >= SpawnLocationCount.X) 247 | { 248 | SpawnPosition.X = 0; 249 | SpawnPosition.Y++; 250 | if (SpawnPosition.Y >= SpawnLocationCount.Y) 251 | { 252 | SpawnPosition.Y = 0; 253 | SpawnPosition.Z++; 254 | if (SpawnPosition.Z >= SpawnLocationCount.Z) 255 | { 256 | SpawnPosition.Z = 0; 257 | } 258 | } 259 | } 260 | return re; 261 | } 262 | 263 | void ASpawnTestActor::AddToList(AActor* actor) 264 | { 265 | if (actor) 266 | { 267 | ActorList.AddTail(actor); 268 | } 269 | else 270 | { 271 | UE_LOG(LogObjectPool, Error, TEXT("SpawnTestActor::AddToList: actor is null")); 272 | } 273 | } 274 | 275 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Source/PoolInterface/PoolActorInterface.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "PoolActorInterface.h" 5 | #include "GameFrameWork/Actor.h" 6 | #include "Private/Macro/ObjectPoolSystemMacro.h" 7 | 8 | void IPoolActorInterface::ReleaseToPool() 9 | { 10 | //UE_LOG(LogObjectPool, Log, TEXT("Start ReleaseToPool")); 11 | 12 | //Get self as AActor 13 | AActor* self = GetActor(); 14 | if (!self) 15 | { 16 | UE_LOG(LogObjectPool, Error, TEXT("Pool Actor Should release to pool but self is null, it is not an AActor's childclass")); 17 | //Todo: maybe should abort here, but not sure, so just return now 18 | return; 19 | } 20 | 21 | { 22 | TRACE_CPUPROFILER_EVENT_SCOPE(IPoolActorInterface::ReleaseToPool_DetachFromLevel); 23 | DetachFromLevel(); 24 | } 25 | 26 | { 27 | TRACE_CPUPROFILER_EVENT_SCOPE(IPoolActorInterface::ReleaseToPool_OnReleaseToPool); 28 | IPoolInterface::ReleaseToPool(); 29 | } 30 | } 31 | 32 | void IPoolActorInterface::OnNewObject() 33 | { 34 | //do something like init the object,but not regist tick and component 35 | //Get self as AActor 36 | AActor* self = GetActor(); 37 | if (!self) 38 | { 39 | UE_LOG(LogObjectPool, Error, TEXT("Pool Actor Should attach to level but self is null, it is not an AActor's childclass")); 40 | return; 41 | } 42 | 43 | self->SpawnCollisionHandlingMethod = ESpawnActorCollisionHandlingMethod::Undefined; 44 | 45 | { 46 | TRACE_CPUPROFILER_EVENT_SCOPE(IPoolActorInterface::AttachToLevel_PostSpawnInitialize); 47 | self->PostSpawnInitialize(FTransform(), nullptr, nullptr, false, false, true); 48 | } 49 | 50 | self->CheckDefaultSubobjects(); 51 | 52 | //up there will add this actor in level, so here to detach it 53 | //this->ReleaseToPool(); 54 | } 55 | 56 | void IPoolActorInterface::OnObjectDestroy() 57 | { 58 | AActor* self = GetActor(); 59 | if (!self) 60 | { 61 | UE_LOG(LogObjectPool, Error, TEXT("Pool Actor Should attach to level but self is null, it is not an AActor's childclass")); 62 | return; 63 | } 64 | 65 | self->MarkComponentsAsGarbage(); 66 | } 67 | 68 | bool IPoolActorInterface::AttachToLevel(ULevel* levelSpawnIn, const FTransform& transform, ESpawnActorCollisionHandlingMethod CollisionHandlingOverride) 69 | { 70 | if (!levelSpawnIn) 71 | { 72 | UE_LOG(LogObjectPool, Error, TEXT("Pool Actor Should attach to level but level is null")); 73 | return false; 74 | } 75 | 76 | //this is a new actor with managed by ObjectPoolSubsystem, so should not call actor's Destroy() funtion 77 | RegistOnActorDestroyCalledByWorld(levelSpawnIn->OwningWorld); 78 | 79 | //Get self as AActor 80 | AActor* self = GetActor(); 81 | if (!self) 82 | { 83 | UE_LOG(LogObjectPool, Error, TEXT("Pool Actor Should attach to level but self is null, it is not an AActor's childclass")); 84 | return false; 85 | } 86 | 87 | self->SpawnCollisionHandlingMethod = CollisionHandlingOverride; 88 | 89 | if (bFirstTimeAttach) 90 | { 91 | bFirstTimeAttach = false; 92 | self->FinishSpawning(transform); 93 | return true; 94 | } 95 | 96 | self->GetRootComponent()->SetWorldTransform(transform); 97 | 98 | self->RegisterAllComponents(); 99 | 100 | { 101 | TRACE_CPUPROFILER_EVENT_SCOPE(IPoolActorInterface::DetachFromLevel_UnregisterAllActorTickFunctions); 102 | //register the actor's tick function 103 | const bool bRegisterTickFunctions = true; 104 | const bool bIncludeComponents = true; 105 | self->RegisterAllActorTickFunctions(bRegisterTickFunctions, bIncludeComponents); 106 | } 107 | 108 | self->PostActorConstruction(); 109 | 110 | /* 111 | self->DispatchBeginPlay();*/ 112 | 113 | 114 | //UE_LOG(LogObjectPool, Log, TEXT("AttachToLevel %s, transform: %s"), *self->GetName(), *transform.ToString 115 | return true; 116 | } 117 | 118 | void IPoolActorInterface::DetachFromLevel() 119 | { 120 | //Get self as AActor 121 | AActor* self = GetActor(); 122 | if (!self || !self->GetWorld()) 123 | { 124 | UE_LOG(LogObjectPool, Error, TEXT("Pool Actor Should detach from level but self is null, it is not an AActor's childclass")); 125 | return; 126 | } 127 | 128 | { 129 | TRACE_CPUPROFILER_EVENT_SCOPE(IPoolActorInterface::ReleaseToPool_RouteEndPlay); 130 | self->RouteEndPlay(EEndPlayReason::RemovedFromWorld); 131 | } 132 | 133 | // Detach this actor's children 134 | { 135 | TRACE_CPUPROFILER_EVENT_SCOPE(IPoolActorInterface::DetachFromLevel_DetachChildren); 136 | TArray AttachedActors; 137 | self->GetAttachedActors(AttachedActors); 138 | if (AttachedActors.Num() > 0) 139 | { 140 | TInlineComponentArray SceneComponents; 141 | self->GetComponents(SceneComponents); 142 | 143 | for (TArray< AActor* >::TConstIterator AttachedActorIt(AttachedActors); AttachedActorIt; ++AttachedActorIt) 144 | { 145 | AActor* ChildActor = *AttachedActorIt; 146 | if (ChildActor != NULL) 147 | { 148 | for (USceneComponent* SceneComponent : SceneComponents) 149 | { 150 | ChildActor->DetachAllSceneComponents(SceneComponent, FDetachmentTransformRules::KeepWorldTransform); 151 | } 152 | #if WITH_EDITOR 153 | if (GIsEditor) 154 | { 155 | GEngine->BroadcastLevelActorDetached(ChildActor, self); 156 | } 157 | #endif 158 | } 159 | } 160 | } 161 | } 162 | 163 | { 164 | TRACE_CPUPROFILER_EVENT_SCOPE(IPoolActorInterface::DetachFromLevel_DetachFromActor); 165 | // Detach from anything we were attached to 166 | USceneComponent* RootComp = self->GetRootComponent(); 167 | if (RootComp != nullptr && RootComp->GetAttachParent() != nullptr) 168 | { 169 | AActor* OldParentActor = RootComp->GetAttachParent()->GetOwner(); 170 | if (OldParentActor) 171 | { 172 | // Attachment is persisted on the child so modify both actors for Undo/Redo but do not mark the Parent package dirty 173 | OldParentActor->Modify(/*bAlwaysMarkDirty=*/false); 174 | } 175 | 176 | self->DetachFromActor(FDetachmentTransformRules::KeepRelativeTransform); 177 | 178 | #if WITH_EDITOR 179 | if (GIsEditor) 180 | { 181 | GEngine->BroadcastLevelActorDetached(self, OldParentActor); 182 | } 183 | #endif 184 | } 185 | } 186 | 187 | // If this actor has an owner, notify it that it has lost a child. 188 | if (self->GetOwner()) 189 | { 190 | self->SetOwner(NULL); 191 | } 192 | 193 | { 194 | TRACE_CPUPROFILER_EVENT_SCOPE(IPoolActorInterface::DetachFromLevel_UnregisterAllComponents); 195 | // Clean up the actor's components. 196 | self->UnregisterAllComponents(); 197 | self->UninitializeComponents(); 198 | } 199 | 200 | { 201 | TRACE_CPUPROFILER_EVENT_SCOPE(IPoolActorInterface::DetachFromLevel_UnregisterAllActorTickFunctions); 202 | // Unregister the actor's tick function 203 | const bool bRegisterTickFunctions = false; 204 | const bool bIncludeComponents = false; 205 | self->RegisterAllActorTickFunctions(bRegisterTickFunctions, bIncludeComponents); 206 | } 207 | 208 | } 209 | 210 | FDelegateHandle IPoolActorInterface::AbortActorDestroyHandle; 211 | 212 | void IPoolActorInterface::RegistOnActorDestroyCalledByWorld(UWorld* world) 213 | { 214 | check(world); 215 | static bool bIsRegist = false; 216 | if (bIsRegist) 217 | { 218 | //Just regist once 219 | return; 220 | } 221 | AbortActorDestroyHandle = world->AddOnActorDestroyedHandler( 222 | FOnActorDestroyed::FDelegate::CreateRaw(this, &IPoolActorInterface::AbortActorDestroy) 223 | ); 224 | bIsRegist = true; 225 | } 226 | 227 | //No need to move actually 228 | void IPoolActorInterface::RemoveOnActorDestroyCalledByWorld(UWorld* world) 229 | { 230 | check(world); 231 | world->RemoveOnActorDestroyededHandler(AbortActorDestroyHandle); 232 | } 233 | 234 | void IPoolActorInterface::AbortActorDestroy(AActor* self) 235 | { 236 | if (!self->Implements()) 237 | { 238 | return; 239 | } 240 | UE_LOG(LogObjectPool, Fatal, TEXT("Pool Actor Should release to pool but destroy by world, abort!!!")); 241 | } 242 | 243 | AActor* IPoolActorInterface::GetActor() 244 | { 245 | return CastChecked(this);; 246 | } 247 | 248 | -------------------------------------------------------------------------------- /Plugins/ObjectPoolSystem/Doc/ActorSpawnDestroy.md: -------------------------------------------------------------------------------- 1 | # Actor 生成与销毁性能 ,以及 ActorPool 的必要性 2 | 3 | 说明:以下示例代码来自于 Unreal Engine 5.1.1,ObjectPool代码以及测试蓝图来自 ***[对象池子系统插件](https://github.com/HankTassadar/ObjectPoolSubsystem "对象池子系统插件")*** ,以下关于性能的讨论只针对GameThread。 4 | 5 | ## 0. 概述 6 | 7 | 试想这样一个场景,我们有一个游戏,需要在游戏中生成大量的 Actor,比如子弹,怪物,道具等等,这些 Actor 都是需要在游戏中动态生成的,而且这些 Actor 的数量是非常大的,比如同一时刻存在的子弹的数量可能会达到几千个,每Tick都会生成数百个的生成与销毁。这时进行性能优化的时候,我们进需要从以下几个方面考虑:Actor的生成,Actor的Tick,Actor的销毁。 8 | 9 | 本文主要分析一些 Actor 在生成与销毁时的性能消耗,进而讨论一些关于 ActorPool 必要性的问题。 10 | 11 | ## 1. Actor 的生成与销毁 12 | 13 | ### 1.1 Actor 的生命周期 14 | 15 | 在 UE 中,Actor 的生命周期主要分为以下几个阶段: 16 | 17 | (1) New 出一个 Actor 18 | (2) Actor 初始化 19 | (3) BeginPlay 20 | (4) Tick,响应输入与事件 21 | (5) EndPlay 22 | (6) 销毁 Actor,等待 GC 23 | (7) Actor 被 GC 回收 24 | 25 | 上述过程中, Actor 的 New ,初始化与 BeginPlay 通常是一起完成的,当延迟生成时会分开完成,EndPlay 与销毁 Actor 通常是一起完成的。 26 | 27 | ### 1.2 Actor 的生成 28 | 29 | 我们可以通过调用 UWorld::SpawnActor 来生成一个 Actor,这个函数的声明如下: 30 | 31 | ```cpp 32 | AActor* SpawnActor( UClass* Class, FTransform const* Transform, const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters()); 33 | ``` 34 | 35 | 想要生成一个 Actor ,我们只需要知道它的类型,以及生成时的坐标变换信息即可。 36 | 37 | 在这个函数当中,我们省略一些非重点代码,只关注Actor的流程: 38 | 39 | ```cpp 40 | AActor* UWorld::SpawnActor( UClass* Class, FTransform const* UserTransformPtr, const FActorSpawnParameters& SpawnParameters) 41 | { 42 | ... 43 | //一串if判断,判断是否可以生成Actor 44 | ... 45 | //编辑器相关,Actor名称等 46 | ... 47 | //碰撞相关,是否允许发生碰撞生成 48 | ... 49 | // actually make the actor object 50 | AActor* const Actor = NewObject(LevelToSpawnIn, Class, NewActorName, ActorFlags, Template, false/*bCopyTransientsFromClassDefaults*/, nullptr/*InInstanceGraph*/, ExternalPackage); 51 | ... 52 | if ( GUndo ) 53 | { 54 | ModifyLevel( LevelToSpawnIn ); 55 | } 56 | LevelToSpawnIn->Actors.Add( Actor ); 57 | LevelToSpawnIn->ActorsForGC.Add(Actor); 58 | ... 59 | // Broadcast delegate before the actor and its contained components are initialized 60 | OnActorPreSpawnInitialization.Broadcast(Actor); 61 | 62 | Actor->PostSpawnInitialize(UserTransform, SpawnParameters.Owner, SpawnParameters.Instigator, SpawnParameters.IsRemoteOwned(), SpawnParameters.bNoFail, SpawnParameters.bDeferConstruction); 63 | ... 64 | Actor->CheckDefaultSubobjects(); 65 | 66 | // Broadcast notification of spawn 67 | OnActorSpawned.Broadcast(Actor); 68 | ... 69 | } 70 | ``` 71 | 72 | 生成一个Actor的流程主要是 NewObject,然后添加到 Level 的 Actor 数组当中,然后调用 Actor 的 PostSpawnInitialize 函数,最后调用 Actor 的 CheckDefaultSubobjects 函数。 73 | 74 | 注意 PostSpawnInitialize 函数的调用时的最后一个参数 SpawnParameters.bDeferConstruction,这个参数的作用是是否延迟 Actor 生成到 Level 中,如果为 true ,则 Actor 不会被生成到 Level 中直到主动调用了 Actor 的 FinishSpawning 函数。 75 | 76 | 于是我们可以通过这里入手预先构造出 Actor,然后在需要的时候再调用 FinishSpawning 函数,这样就可以避免在游戏中频繁的生成 Actor 了。 77 | 78 | ### 1.3 Actor 生成的性能消耗 79 | 80 | 接下来我们使用 ***[对象池子系统插件](https://github.com/HankTassadar/ObjectPoolSubsystem "对象池子系统插件")*** 中的 BP_PerformanceTest 来测试一下生成过程中各个函数的性能消耗,测试中生成的是一个只有一个 StaticMeshComponent 的 Actor, 每 Tick 生成 100 个该对象。简化 SpawnActor 的流程到 ActorSpawnPerformanceTest::SpawnActors 中,然后每 Tick 生成固定数量的 Actor ,然后销毁掉。使用 TRACE_CPUPROFILER_EVENT_SCOPE 宏来测量各个过程的消耗。 81 | 82 | 使用 Unreal Insights 查看性能消耗,截取其中一次 Tick 如下: 83 | ![image](./Image/Tick.PNG) 84 | 85 | 可以看到,多次 SpawnActor 然后 Destroy 时, 生成时的性能消耗占比较大(不一定,会有例外,之后会讨论销毁性能)。 86 | 87 | 然后我们再来看一下具体的单次 SpawnActor 消耗: 88 |
89 | AActorSpawnPerformanceTest::SpawnActors 90 | 91 | ```cpp 92 | 93 | AActor* AActorSpawnPerformanceTest::SpawnActors() 94 | { 95 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::SpawnActors); 96 | FName name = FName(*FString::Printf(TEXT("AActorSpawnPerformanceTest_Actors_%d"), SpawnNameUnique++)); 97 | ULevel* level = GetLevel(); 98 | AActor* actor = nullptr; 99 | 100 | { 101 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::SpawnActors_NewObject); 102 | actor = NewObject(level, ActorToSpawn, name, EObjectFlags::RF_Transactional); 103 | } 104 | 105 | if (!actor) 106 | { 107 | return nullptr; 108 | } 109 | 110 | { 111 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::SpawnActors_AddToLevel); 112 | level->Actors.Add(actor); 113 | level->ActorsForGC.Add(actor); 114 | } 115 | 116 | { 117 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::SpawnActors_PostSpawnInitialize); 118 | actor->PostSpawnInitialize(FTransform(), nullptr, nullptr, false, false, true); 119 | } 120 | 121 | { 122 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::SpawnActors_CheckDefaultSubobjects); 123 | actor->CheckDefaultSubobjects(); 124 | } 125 | 126 | { 127 | TRACE_CPUPROFILER_EVENT_SCOPE(AActorSpawnPerformanceTest::SpawnActors_FinishSpawning); 128 | actor->FinishSpawning(FTransform()); 129 | } 130 | 131 | return actor; 132 | } 133 | ``` 134 | 135 |
136 | 137 | ![image](./Image/SpawnActors.PNG) 138 | 139 | 可以看到大部分的消耗是在最后的 FinishSpawning 上。我们详细看一下这个函数。 140 | 141 | ```cpp 142 | void AActor::FinishSpawning(const FTransform& UserTransform, bool bIsDefaultTransform, const FComponentInstanceDataCache* InstanceDataCache) 143 | { 144 | ... 145 | //一些非重点代码 146 | ... 147 | { 148 | FEditorScriptExecutionGuard ScriptGuard; 149 | ExecuteConstruction(FinalRootComponentTransform, nullptr, InstanceDataCache,bIsDefaultTransform); 150 | } 151 | 152 | { 153 | SCOPE_CYCLE_COUNTER(STAT_PostActorConstruction); 154 | PostActorConstruction(); 155 | } 156 | } 157 | ``` 158 | 159 | ExecuteConstruction 中对这个 Actor 拥有的 Component 进行了生成,包括一些蓝图中的组件以及调用蓝图的 ConstructionScript。 160 | PostActorConstruction 主要进行一些初始化操作,并调用了 BeginPlay。 161 | 162 | 为具体查看这两个过程的消耗,我们使用 AActorSpawnPerformanceTest::TestFinishSpawning 来替换这个 AActor::FinishSpawning ,结果如下: 163 | 164 | ![image](./Image/TestFinishSpawning.PNG) 165 | 166 | 可以看到, ExecuteConstruction 占用了 FinishSpawning 95% 的消耗,而 PostActorConstruction 只占用了 5% 的消耗。 167 | 168 | 如果生成的 Actor 比较复杂,拥有的组件很多,那么在 ExecuteConstruction 时性能消耗会更大。 169 | 170 | ### 1.4 Actor 的销毁 171 | 172 | 销毁 Acotr 时我们需要调用 AActor::Destroy ,但实际上的销毁逻辑在 UWorld::DestroyActor ,这个函数的声明如下: 173 | 174 | ```cpp 175 | bool DestroyActor( AActor* Actor, bool bNetForce=false, bool bShouldModifyLevel=true ); 176 | ``` 177 | 178 | 省略一些非重点内容,我们看一下销毁流程: 179 | 180 | ```cpp 181 | bool UWorld::DestroyActor( AActor* ThisActor, bool bNetForce, bool bShouldModifyLevel ) 182 | { 183 | ... 184 | //一些是否可销毁的判断 185 | ... 186 | OnActorDestroyed.Broadcast(ThisActor); 187 | 188 | // Tell this actor it's about to be destroyed. 189 | ThisActor->Destroyed(); 190 | ... 191 | // Detach this actor's children, 释放所有的子 Actor 192 | ... 193 | // Detach from anything we were attached to,从父 Actor 中释放 194 | ... 195 | ThisActor->ClearComponentOverlaps(); 196 | ... 197 | //一下NetDriver相关的内容 198 | ... 199 | // Remove the actor from the actor list. 200 | RemoveActor( ThisActor, bShouldModifyLevel ); 201 | ... 202 | // Clean up the actor's components. 203 | ThisActor->UnregisterAllComponents(); 204 | 205 | // Mark the actor and its direct components as pending kill. 206 | ThisActor->MarkAsGarbage(); 207 | ThisActor->MarkPackageDirty(); 208 | ThisActor->MarkComponentsAsPendingKill(); 209 | 210 | // Unregister the actor's tick function 211 | const bool bRegisterTickFunctions = false; 212 | const bool bIncludeComponents = true; 213 | ThisActor->RegisterAllActorTickFunctions(bRegisterTickFunctions, bIncludeComponents); 214 | 215 | // Return success. 216 | return true; 217 | } 218 | ``` 219 | 220 | 主要流程为 Actor 调用 Destroyed 来调用 EndPlay ,然后清理组件重叠,从关卡的 ActorList 中移除,取消注册组件,标记为垃圾,取消注册 Tick。 221 | 222 | ### 1.5 Actor 销毁的性能消耗 223 | 224 | 我们来对比一下测试刚开始时的 Tick 和测试快结束时的 Tick。 225 | 刚开始时: 226 | 227 | ![image](./Image/DestroyBegin.PNG) 228 | 229 | 快结束时: 230 | 231 | ![image](./Image/DestroyEnd.PNG) 232 | 233 | 可以看出销毁相同数量的 Actor ,所用的时间越来越长。 234 | 我们导出销毁的数据,将其绘制成图表如下: 235 | 236 | ![image](./Image/DestroyTime.png) 237 | 238 | 销毁单个 Actor 的时间消耗从开始的 10us 左右,到最后的 175us 左右。并且 60s 时销毁时间突降至刚开始的水平。 239 | 240 | 从图中可以看出单次销毁时间呈线性增长,但单次生成时间没有太多变化。这是因为生成时将 Actor 添加到了 Level 的 ActorList(ULevel::Actors, ULevel::ActorsForGC) 中,而销毁时需要从 ActorList 中移除,ActorList 是一个数组,销毁时从数组中查找占用了大量时间, 随着 ActorList 的增长,查找的时间也会增长。 241 | 242 | 我们仔细看一下销毁时从 ActorList 中移除的函数 RemoveActor : 243 | 244 | ```cpp 245 | void UWorld::RemoveActor(AActor* Actor, bool bShouldModifyLevel) const 246 | { 247 | if (ULevel* CheckLevel = Actor->GetLevel()) 248 | { 249 | const int32 ActorListIndex = CheckLevel->Actors.Find(Actor); 250 | // Search the entire list. 251 | if (ActorListIndex != INDEX_NONE) 252 | { 253 | if (bShouldModifyLevel && GUndo) 254 | { 255 | ModifyLevel(CheckLevel); 256 | } 257 | 258 | if (!IsGameWorld()) 259 | { 260 | CheckLevel->Actors[ActorListIndex]->Modify(); 261 | } 262 | 263 | CheckLevel->Actors[ActorListIndex] = nullptr; 264 | 265 | CheckLevel->ActorsForGC.RemoveSwap(Actor); 266 | } 267 | } 268 | 269 | // Remove actor from network list 270 | RemoveNetworkActor( Actor ); 271 | } 272 | ``` 273 | 274 | 从代码中可以看到从 ULevel::Actors 中查找到 Actor 的索引,只是将这个索引对应的元素置空,并没有将这个元素从数组中移除,可以想象,随着我们不断的生成与销毁 Actor ,这个数组会越来越大,而且数组中会有很多空元素,这样在查找时就会消耗更多的时间。 275 | 276 | 在测试的 Tick 中,我们每一次 Tick 使用日志记录了 ActorList 的长度,将其绘制成图表并与上图进行对比: 277 | 278 | ![image](./Image/DestroyTimeAndActorsCount.png) 279 | 280 | 对比上面两张图,可以判断出销毁时间与 ActorList 的长度成正相关。性能的消耗确实在这个数组的查找上。 281 | 282 | 在 60s 时销毁时间降至刚开始的水平,这是因为在 60s 时触发了垃圾收集,在收集垃圾后对ActorList 进行了收缩,移除了所有的空元素。 283 | 284 | 接下来我们看垃圾收集的情况,整个测试过程中 只有 60s 时触发了垃圾收集。 285 | 286 | ![image](./Image/GarbageCollect.PNG) 287 | 288 | 这次垃圾收集耗时极长,因为这一次收集了2-3万个 Actor,导致了这一帧一半以上的时间都用于 GC ,如果实际游戏中出现这么长的 GC ,会极大影响游戏体验。 289 | 290 | 收集完成后进行垃圾处理,垃圾处理函数 IncrementalPurgeGarbage 的耗时的散点图如下: 291 | 292 | ![image](./Image/GarbageTime.png) 293 | 294 | 可以看到 60s 触发垃圾收集后,就一直以每 Tick 使用 2ms 销毁垃圾的速度进行,并且一直在处理没有间断直到测试终止。并且测试终止时出现了一次长达 5s 的垃圾销毁。 295 | 296 | 这也能解释为什么只有 60s 的时候进行了 GC ,因为这一次的 GC 处理的对象太多了,之后一直到测试终止前都没有处理完第一次 GC 的对象。所以上面的 Actors Size 图表中看到 60s 时缩减了数组的长度,而之后一直在增加。 297 | 298 | 如果一直以这个速率持续的生成销毁 Actor ,在这次 GC 彻底处理完成之前,不断的生成销毁会导致下一次 GC 需要处理的对象更多,然后恶性循环,始终有大量的对象等待 GC 处理,这样就会导致游戏的性能越来越差,直到崩溃。 299 | 300 | ### 1.6 总结 301 | 302 | 生成的过程中,很多步骤并不是一定要在使用这个对象的时候才进行,对于频繁使用的对象,完全可以在加载时就生成好。销毁时也一样,并非一定要把工作全都交给 GC ,多次复用同一个对象,可以减少 GC 的压力。 303 | 304 | ## 2. ActorPool的必要性 305 | 306 | 307 | 308 | ## 3. ***[对象池子系统插件](https://github.com/HankTassadar/ObjectPoolSubsystem "对象池子系统插件")*** 的设计与实现 309 | --------------------------------------------------------------------------------