【UE5】通用对象池系统设计和实现(一)

对象池的好处

众所周知,对象池是游戏开发中很常见的一种设计模式,它的好处颇多,比如内存复用、减少内存碎片等等。在处理大量可重复利用的游戏对象时,可以极大的提高游戏性能。最常见的比如子弹之类的物体。在UE中,如Niagara等都带有专门的对象池系统来优化其性能。但其余的如生成、销毁Actor时,却没有一个通用的对象池系统来进行管理。这次我们就来实现一个对蓝图友好、使用方便且易拓展的UE通用对象池系统。

最终实现功能

我们实现的对象池使用起来非常方便,首先第一步,在想要的位置创建一个对象池数据文件:

创建对象池数据文件

创建完,打开对象池数据文件,可以看见以下配置项:

对象池数据文件配置项

  • PoolName:该对象池的唯一辨识符,类型为一个FName,该变量会自动命名为创建时的文件名,一般情况下建议做好文件命名规范,而不去改动这个参数。
  • ObjectType:对象池的对象类型,可选择为Actor对象池或UObject对象池,可自行拓展更多类型,不过一般为Actor对象池居多。
  • Pool Actor Classes:要池化的Actor,可直接把Actor传入此Array,即可将此对象池化。当ObjectType不同时,该选项会变成对应Type类型的Array。
  • PoolMode:对象池的运作模式,可设置为栈模式或者队列模式,队列模式可以使池对象取用更加均匀,不同模式按需选用,且可轻松自行拓展更多运作模式。
  • Collction Check:回收对象时是否进行安全检查,检查待回收对象是否已在池中。默认为开启状态,关闭可以提升些许性能,按实际情况更改,推荐保持默认即可。
  • Default Capacity:设置池的初始大小,根据对象的使用频率适当调整。
  • Max Size:设置对象池的最大容量,同样根据对象的使用频率适当调整。

配置完池的基本参数后,将需要池化的对象传入Array中

如图:
配置池化Actor
池化的Actor如果需要接收从池中取出和回收时的事件,还可以直接添加一个Pooltem组件,以此可监听对应事件:
池行为事件

配置好一个池数据文件后,把它直接添加进项目设置->ObjectPool->ObjectPoolSystem中的Object Pool Data Array即可

添加进项目设置

使用起来也很简单,下面是在蓝图中使用对象池系统的例子

获取对象:
获取对象
回收对象:
回收对象
从上面的简单例子可以看出,我们的对象池系统使用起来非常方便,获取池化对象基本上分为以下三步:

  1. 获取ObjectPoolSubsystem;
  2. 从ObjectPoolSubsystem获取Pool对象,可通过PoolName或者资产引用进行获取,推荐使用PoolName,性能更好;
  3. 从Pool对象中取出指定类型的对象,使用该对象的类引用来进行查找,该方法返回从池中取出的实例化对象。

而回收对象同样很简单,同样分为三步:

  1. 获取ObjectPoolSubsystem;
  2. 从ObjectPoolSubsystem获取Pool对象;
  3. 传入待回收对象的示例和其对应的类引用,即可完成回收。

蓝图中所开放的函数,在C++中同样可用,所以你依旧可以通过在C++中编写代码来使用对象池系统。

对象池系统框架设计

简单讲完实现的效果,我们回过头来看看如何从头构筑这样一套系统。首先,我们需要思考一个问题,一个通用对象池系统,他肯定需要一个全局管理的单例类,而这个类的的实例,应该放在引擎的哪一个位置好?换在Unity,我们也许会写一个单例的Manager类,然后把他设置为DontDestroyOnLoad,从而让他在整个游戏的生命周期中以单例对象的形式进行管理。而在UE中,由于UE有一套完备的Gameplay框架,我们不得不考虑,我们的单例类应该运行在框架中的哪一个层级,才能完美的解决我们的需求?我们所需要的,是在整个游戏的生命周期中以一个单例来管理我们的对象池。而在UE的Gameplay架构里,最符合这一特征的无疑是我们的GameInstance(对UE的Gameplay架构不太熟悉的可以看看大钊的InsideUE系列,很有帮助)。确定是在GameInstance后,如果是以前,接下来头疼的就是如何管理我们单例类的生命周期。但现在UE有了Subsystem框架(还不了解的同样可以看看大钊的博客),这让我们可以轻松实现我们自己的单例类,而不用去头疼单例类的自动实例化和释放等问题。

而有了用于管理所有对象池的ObjectPoolSubsystem,接下来的思路就简单多了,先简单上个图:

对象池系统框架图
结合上图,我们从底往上走,最底的是我们想要池化的普通Actor,对于每个池化Actor,它都有对应的UObjectPool对象,负责池化和管理对应Actor。再往上走,管理我们UObjectPool的则是UPool对象,每一个ObjectPoolData都对应了一个UPool对象,而每个UPool都会读取对应ObjectPoolData中的数据,并为Data中指定的每一个待池化的Actor创建出对应的UObjectPool。继续往上走,就来到了我们的UObjectPoolSubsystem,我们在之前也说过,它需要管理所有的对象池,所以理所当然的,UObjectPoolSubsystem将持有我们所有UPool对象的引用。这样一来,只要我们能够获取到UObjectPoolSubsystem,我们就可以获取到所有的UObjectPool。

结尾

本节我们讨论了对象池系统框架的基础部分。下一节,除了讲框架中PoolAgent的设计,我们还将讨论对象池的核心类——UObjectPool的设计与实现。

在Unreal Engine 5 (UE5)中实现合成系统可以通过以下步骤完成: 1. 创建一个Actor类,作为合成系统的基础。可以使用UE5自带的Actor类或者自定义一个Actor类。 2. 在Actor类中添加必要的变量和函数,例如合成的输入和输出物品、合成的材料、合成的效果等。 3. 创建一个UI界面,用于显示和控制合成系统。可以使用UE5自带的UMG或者其他UI框架来创建。 4. 在UI界面中添加按钮和其他控件,用于控制合成系统。例如,添加一个合成按钮,当用户点击该按钮时,合成系统将进行合成操作。 5. 在合成系统的函数中实现合成的逻辑。根据输入的材料和合成的效果,计算出合成的结果,并将结果输出到指定的位置。 6. 在UE5中创建一些测试场景,用于测试合成系统的功能和性能。 下面是一个简单的例子,演示如何使用UE5实现一个合成系统: 1. 创建一个Actor类,名为CraftingSystem。 2. 在CraftingSystem中添加以下变量和函数: ```c++ UPROPERTY(EditAnywhere) UStaticMeshComponent* OutputItemMesh; // 合成的输出物品 UPROPERTY(EditAnywhere) TArray<TSubclassOf<AActor>> InputItemClasses; // 合成的输入物品 UPROPERTY(EditAnywhere) TArray<int32> InputItemCounts; // 合成的输入物品数量 UPROPERTY(EditAnywhere) TSubclassOf<AActor> CraftEffectClass; // 合成的效果 UFUNCTION(BlueprintCallable) void Craft(); // 合成的函数 ``` 3. 创建一个UI界面,名为CraftingUI。 4. 在CraftingUI中添加一个合成按钮。 5. 在CraftingUI中为合成按钮添加点击事件,调用CraftingSystem的Craft函数。 6. 在CraftingSystem的Craft函数中实现合成的逻辑。计算出需要的输入物品和数量,如果输入物品足够,将其减少并生成一个输出物品和一个合成效果。 ```c++ void ACraftingSystem::Craft() { TArray<AActor*> inputItems; for (int32 i = 0; i < InputItemClasses.Num(); i++) { UClass* inputClass = InputItemClasses[i]; int32 inputCount = InputItemCounts[i]; TArray<AActor*> foundActors; UGameplayStatics::GetAllActorsOfClass(GetWorld(), inputClass, foundActors); int32 count = 0; for (AActor* actor : foundActors) { if (count >= inputCount) { break; } if (inputItems.Contains(actor)) { continue; } inputItems.Add(actor); count++; } if (count < inputCount) { UE_LOG(LogTemp, Error, TEXT("Not enough input items.")); return; } } for (AActor* actor : inputItems) { actor->Destroy(); } AActor* outputItem = GetWorld()->SpawnActor<AActor>(OutputItemClass
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

◎Bigotry◎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值