UE5 对象池 一个umg和actor的对象池

该文章展示了在UnrealEngine中实现的一个名为UObjectPoolSubsystem的系统,用于管理游戏中的对象池,如Actor和UserWidget。它包含FActorPool结构,用于存储和检索Actor实例,支持对象的创建、释放和重用,优化游戏性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

.h文件

// Copyright Access! GameStudio. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidgetPool.h"
#include "Subsystems/WorldSubsystem.h"
#include "ObjectPoolSubsystem.generated.h"

/**
 * 
 */

USTRUCT(BlueprintType)
struct FActorPool
{
	GENERATED_BODY()

public:

	FActorPool(){};
	void SetWorld(UWorld* World)
	{
		if(World)
		{
			OwningWorld = World;
		}
	}
	bool IsInitialized() const { return OwningWorld.IsValid(); }

	AActor* GetActor(TSubclassOf<AActor> ActorClass);

	//创建actor
	template <typename ActorT = AActor>
	AActor* GetOrCreateActor(TSubclassOf<ActorT> ActorClass)
	{
		if (!ActorClass || !ensure(IsInitialized()))
		{
			return nullptr;
		}

		AActor* Actor = nullptr;
		for (AActor* InactiveActor : InactiveActors)
		{
			if (InactiveActor->StaticClass() == ActorClass)
			{
				Actor = InactiveActor;
				InactiveActors.RemoveSingleSwap(InactiveActor);
				break;
			}
		}
		if(Actor)
		{
			SetActorEnable(Actor,true);
		}
		if (!Actor)
		{
			Actor = OwningWorld.Get()->SpawnActor(ActorClass);
			if (!Actor)
			{
				return nullptr;
			}
		}
		ActiveActors.Add(Actor);

		return Cast<ActorT>(Actor);
	}

	//将actor释放回对象池
	void ReleaseActor(AActor* Actor);

	void ReleaseActor(TArray<AActor*> Actors);

	void ReleaseAll();

	//重置对象池
	void ResetPool();

	//设置Actor的开启与关闭(碰撞、显示、Tick和所有子Actor)
	static void SetActorEnable(AActor* Actor, bool Enable);;

private:
	UPROPERTY(Transient)
	TArray<TObjectPtr<AActor>> ActiveActors;
	UPROPERTY(Transient)
	TArray<TObjectPtr<AActor>> InactiveActors;

	TWeakObjectPtr<UWorld> OwningWorld;
};


UCLASS()
class XXXXXX_API UObjectPoolSubsystem : public UWorldSubsystem
{
	GENERATED_BODY()
public:
	//创建Widget
	UFUNCTION(BlueprintCallable)
	UUserWidget* CreateOrGetWidget(TSubclassOf<UUserWidget> WidgetClass);
	//释放Widget到对象池
	UFUNCTION(BlueprintCallable)
	void ReleaseWidget(UUserWidget* Widget);
	UFUNCTION(BlueprintCallable)
	void ReleaseWidgets(TArray<UUserWidget*> Widgets);

	//创建Actor
	UFUNCTION(BlueprintCallable)
	AActor* CreateOrGetActor(TSubclassOf<AActor> ActorClass);
	//释放Actor到对象池
	UFUNCTION(BlueprintCallable)
	void ReleaseActor(AActor* Actor);
	UFUNCTION(BlueprintCallable)
	void ReleaseActors(TArray<AActor*> Actors);
	UFUNCTION(BlueprintCallable)
	void ReleaseActorByClass(TSubclassOf<AActor> ActorClass);
	UFUNCTION(BlueprintCallable)
	void ResetPool(TSubclassOf<AActor> ActorClass);
	
protected:
	virtual void PostInitialize() override;

private:
	UPROPERTY()
	FUserWidgetPool WidgetPool;
	UPROPERTY()
	TMap<TSubclassOf<AActor>,FActorPool> ActorPools;
};

.cpp文件

// Copyright Access! GameStudio. All Rights Reserved.


#include "Subsystem/ObjectPoolSubsystem.h"

AActor* FActorPool::GetActor(TSubclassOf<AActor> ActorClass)
{
	return GetOrCreateActor(ActorClass);
}

void FActorPool::ReleaseActor(AActor* Actor)
{
	if (!Actor)
	{
		return;
	}
		
	if (const auto Index = ActiveActors.Find(Actor) != INDEX_NONE)
	{
		ActiveActors.RemoveAt(Index);
		InactiveActors.Push(Actor);
		SetActorEnable(Actor,false);
	}
}

void FActorPool::ReleaseActor(TArray<AActor*> Actors)
{
	for (AActor* Actor : Actors)
	{
		ReleaseActor(Actor);
	}
}

void FActorPool::ReleaseAll()
{
	for(auto Actor:ActiveActors)
	{
		SetActorEnable(Actor,false);
	}
	InactiveActors.Append(ActiveActors);
	ActiveActors.Empty();
}

void FActorPool::ResetPool()
{
	InactiveActors.Reset();
	ActiveActors.Reset();
}

void FActorPool::SetActorEnable(AActor* Actor, bool Enable)
{
	if(!Actor)
		return;

	Actor->SetActorHiddenInGame(!Enable);
	Actor->SetActorEnableCollision(Enable);
	Actor->SetActorTickEnabled(Enable);

	TArray<UActorComponent*> Components;
	Actor->GetComponents(Components);
	for (UActorComponent* ActorComponent : Components)
	{
		ActorComponent->SetActive(Enable);
		ActorComponent->SetComponentTickEnabled(Enable);
	}
}

void UObjectPoolSubsystem::PostInitialize()
{
	Super::PostInitialize();
	WidgetPool.SetWorld(GetWorld());
	//ActorPool.SetWorld(GetWorld());
}

UUserWidget* UObjectPoolSubsystem::CreateOrGetWidget(const TSubclassOf<UUserWidget> WidgetClass)
{
	return WidgetPool.GetOrCreateInstance(WidgetClass);
}

void UObjectPoolSubsystem::ReleaseWidget(UUserWidget* Widget)
{
	if(!Widget) return;
	Widget->SetVisibility(ESlateVisibility::Collapsed);
	WidgetPool.Release(Widget,false);
}

void UObjectPoolSubsystem::ReleaseWidgets(TArray<UUserWidget*> Widgets)
{
	for (const auto Widget : Widgets)
	{
		ReleaseWidget(Widget);
	}
}

AActor* UObjectPoolSubsystem::CreateOrGetActor(TSubclassOf<AActor> ActorClass)
{
	if(!ActorClass) return nullptr;
	
	if(const auto Pool = ActorPools.Find(ActorClass))
	{
		return Pool->GetOrCreateActor(ActorClass);
	}
	else
	{
		FActorPool NewPool;
		NewPool.SetWorld(GetWorld());
		ActorPools.Add(ActorClass, NewPool);

		return NewPool.GetOrCreateActor(ActorClass);
	}
}

void UObjectPoolSubsystem::ReleaseActor(AActor* Actor)
{
	if(!Actor) return;
	if(const auto Pool = ActorPools.Find(Actor->StaticClass()))
	{
		Pool->ReleaseActor(Actor);
	}
}

void UObjectPoolSubsystem::ReleaseActors(TArray<AActor*> Actors)
{
	if(!Actors.IsEmpty())
	{
		if(const auto Pool = ActorPools.Find(Actors.Last()->StaticClass()))
		{
			Pool->ReleaseActor(Actors);
		}
	}
}

void UObjectPoolSubsystem::ReleaseActorByClass(TSubclassOf<AActor> ActorClass)
{
	if(const auto Pool = ActorPools.Find(ActorClass))
	{
		Pool->ReleaseAll();
	}
}

void UObjectPoolSubsystem::ResetPool(TSubclassOf<AActor> ActorClass)
{
	if(const auto Pool = ActorPools.Find(ActorClass))
	{
		Pool->ResetPool();
	}
}

<think>我们面对的问题是如何在Unreal Engine 5中将UMG用户界面(UI)附加到3D模型表面。这通常用于在3D物体上显示交互信息,例如在游戏中的道具上显示名称或状态条。 在Unreal Engine中,UMG(Unreal Motion Graphics)是用于创建UI的视觉UI设计器。通常,UMG创建的UI是2D的,并显示在屏幕上(即屏幕空间)。但是,我们可以通过将UMG渲染到纹理(Render Target),然后将该纹理应用为3D模型的材质,从而实现将UMG界面附加到3D模型表面。 步骤概述: 1. 创建一个UMG用户界面。 2. 创建一个渲染目标(Render Target)资源。 3. 使用“场景捕获组件2D”(Scene Capture Component 2D)或“Widget组件”(Widget Component)来将UMG渲染到渲染目标。 4. 创建一个材质,将渲染目标作为纹理使用。 5. 将该材质应用到3D模型上。 然而,在UE5中,更推荐使用“Widget组件”(Widget Component)来直接将UMG附加到3D空间中。Widget组件可以附加到Actor上,并显示在3D空间中,但默认情况下它总是面向摄像机(类似于广告牌)。如果我们希望它固定在模型表面,我们可以将Widget组件附加到模型的一个场景组件(Scene Component)上,并调整其位置旋转。 但是,如果要求将UI完全贴合在模型表面(例如,在弯曲的表面上),那么我们需要将UI渲染到纹理,然后通过材质将纹理映射到模型表面。 下面我们详细说明两种方法: ### 方法1:使用Widget组件(简单,但可能不贴合曲面) 1. 在需要显示UI的Actor(例如,你的3D模型)上添加一个“Widget组件”。 2. 在Widget组件的细节面板中,设置要显示的UMG用户界面(例如,你创建的UMG类)。 3. 调整Widget组件的位置旋转,使其位于模型表面。 4. 设置Widget组件的“空间”属性为“世界”(World)或“玩家”(Player)等,根据需求调整。 - 选择“世界”空间,则UI会固定在3D空间中,不会随着摄像机旋转而改变朝向(除非你设置为总是面向摄像机)。 - 也可以设置成“保持相对旋转”,这样就不会自动面向摄像机,而是固定在模型上。 这种方法适用于在模型表面某个位置显示一个平面的UI,但如果模型表面是曲面,则UI不会弯曲,而是保持平面。 ### 方法2:使用渲染到纹理(适用于曲面) 1. **创建UMG界面**:首先创建一个UMG界面,设计好需要显示的UI元素。 2. **创建渲染目标(Render Target)**:在内容浏览器中右键单击,选择“渲染目标”下的“渲染目标2D”创建一个新的渲染目标(例如,命名为`RT_UI`)。 3. **创建场景捕获组件2D**(或使用Widget组件直接渲染到纹理): - 在场景中创建一个Actor(例如,一个Actor),添加一个“场景捕获组件2D”(SceneCaptureComponent2D)。 - 将场景捕获组件的“纹理目标”设置为之前创建的渲染目标(`RT_UI`)。 - 调整场景捕获组件的位置旋转,使其正对着一个平面(这个平面将用于显示UI,但我们不需要实际显示这个平面)。 - 注意:这种方法需要将UMG界面放在一个平面上,然后通过场景捕获来捕获这个平面。但更简单的方法是使用“Widget组件”的渲染到纹理功能。 UE5提供了更直接的方法:我们可以使用“Widget组件”的“Draw to Render Target”功能。 - 在蓝图中,添加一个Widget组件,并设置其“空间”为“世界”。 - 在Widget组件的细节面板中,找到“渲染目标”选项,勾选“绘制到渲染目标”(Draw to Render Target),并选择之前创建的渲染目标(`RT_UI`)。这样,Widget组件就会将其内容绘制到渲染目标上。 4. **创建材质**: - 创建一个材质(例如,`M_UI_OnModel`),在材质编辑器中,将纹理采样节点设置为渲染目标(`RT_UI`)。 - 根据需要调整材质,例如,设置不透明或半透明,以及如何映射到模型表面(可能需要使用UV坐标)。 5. **将材质应用到模型**:在模型的材质槽上应用这个材质。如果模型原本有材质,我们可以将UI材质作为贴花(Decal)或者使用材质混合。 注意:如果模型是静态的,我们可以直接将材质应用到模型上。如果是动态的,可能需要使用材质实例动态设置纹理。 ### 具体步骤(方法2)的蓝图示例: 1. 创建UMG:名为`WBP_ModelUI`。 2. 创建渲染目标:`RT_UI`(大小建议为512x512或1024x1024)。 3. 在需要显示UI的Actor(比如一个道具Actor)的蓝图中: - 添加一个Widget组件(命名为`UIWidget`)。 - 设置`UIWidget`的Widget Class为`WBP_ModelUI`。 - 勾选`UIWidget`的“Draw to Render Target”属性,并将渲染目标设置为`RT_UI`。 - 注意:此时Widget组件在场景中可见,但我们不需要它显示在场景中,所以可以将它的位置设置为远离模型(例如,放在一个不会被摄像机看到的地方),或者将其可见性设置为false(但这样可能不会渲染)。所以,我们通常将其放在一个不会影响主场景的位置,并且设置其“仅所有者可见”等属性。 4. 创建材质`M_UI_OnModel`: - 在材质编辑器中,使用纹理采样节点,纹理选择为`RT_UI`。 - 连接基础颜色(Base Color)不透明度(Opacity)(如果UI有透明部分,则需要设置混合模式为半透明)。 - 将材质应用到3D模型的表面(可以直接替换原有材质,或者使用材质层混合,或者使用贴花组件)。 ### 注意事项: - 更新问题:渲染目标不会自动更新。如果UI需要动态变化,需要确保渲染目标被更新。设置Widget组件的“Draw to Render Target”后,它会自动更新,但请注意性能。 - 分辨率:渲染目标的分辨率会影响UI在模型上的清晰度。 - 曲面UV:如果模型表面是曲面,需要确保UV映射正确,否则UI可能会变形。可以使用一个单独的UV通道来专门映射UI。 ### 示例项目: 在Unreal Engine官方提供的示例项目中,有一个名为《内容示例》(Content Examples)的项目,其中包含了UI相关的示例。特别是“UI_UMG”地图中,有关于UMG的各种用法,但没有直接展示在3D模型上。不过,在“UI_SlatePostBuffer”中展示了将UI渲染到纹理然后用于后期处理的效果,可以参考其渲染到纹理的方法。 ### 总结: 在UE5中将UMG附加到3D模型表面,可以通过将UMG渲染到纹理,然后将纹理作为模型材质的一部分来实现。主要步骤包括创建UMG、创建渲染目标、设置Widget组件渲染到目标纹理、创建材质并应用到模型。 [^1]: Unreal Engine 是一个功能强大且灵活的 3D 游戏引擎,适用于多种应用领域,从游戏开发到虚拟现实影视制作。 [^2]: 《内容示例》是学习UE5的基础示例,可以用此熟悉一遍UE5的功能。 [^3]: 通过学习实践,你已经掌握了Unreal Engine的基本高级功能。 §§相关问题§§ 1. 如何优化将UMG渲染到纹理的性能? 2. 在曲面模型上如何避免UI变形? 3. 除了渲染到纹理,还有哪些方法可以在3D模型上显示UI?</think>在 Unreal Engine 5 中将 UMG UI 附加到 3D 模型表面,可以通过 **Widget Component** 结合 **材质渲染** 实现。以下是详细步骤: --- ### 步骤 1:创建基础 UMG 界面 1. **新建 UMG 界面** - 在内容浏览器右键 → **用户界面** → **Widget Blueprint**,命名为 `WBP_ModelUI`。 - 设计所需 UI 元素(如文本、进度条等)。 2. **保存并编译** - 确保 UMG 逻辑正确,无报错。 --- ### 步骤 2:创建 Widget Component 并绑定到模型 1. **在模型 Actor 上添加 Widget Component** - 打开模型所属的 Actor 蓝图(或新建 Actor)。 - 在组件面板点击 **添加(+) → Widget Component**,命名为 `3D_UI`。 2. **配置 Widget Component** - 在细节面板中: - **Widget Class**:选择刚创建的 `WBP_ModelUI`。 - **空间位置**:设置为 **World**(3D 空间)。 - **绘制大小**:调整 UI 物理尺寸(如 `(50, 30)`)。 - **Pivot**:调整锚点位置(如 `(0.5, 0.5)` 居中)。 - **调整位置/旋转**:将组件移动到模型表面目标位置。 --- ### 步骤 3:实现 UI 贴合模型曲面(可选) 若需 UI 完全贴合曲面(非平面),需 **渲染到材质**: 1. **创建渲染目标(Render Target)** - 右键 → **渲染目标** → **Render Target 2D**,命名为 `RT_UI`。 2. **创建动态材质** - 右键 → **材质** → 命名 `M_UI_Projection`。 - 在材质编辑器中: - 添加 **Texture Sample** 节点 → 选择 `RT_UI`。 - 连接至 **基础颜色(Base Color)** **不透明度(Opacity)**。 - 设置材质域为 **User Interface**。 3. **绑定渲染逻辑** - 在模型 Actor 蓝图中: - 添加 **Scene Capture Component 2D**。 - 设置其 **Texture Target** 为 `RT_UI`。 - 通过蓝图将 UMG 渲染到 `RT_UI`: ```cpp // 在 BeginPlay 事件中 Get Render Target -> RT_UI Create Widget (WBP_ModelUI) -> Add to Viewport Draw Widget to Render Target ``` - 将材质 `M_UI_Projection` 应用到模型表面。 --- ### 步骤 4:优化交互与显示 1. **始终面向摄像机** - 在 Widget Component 细节中勾选 **Screen Space** → **Face Camera**。 2. **深度测试** - 设置 **Widget Component → Rendering → Depth Priority** 避免被遮挡。 3. **交互事件穿透** - 在 UMG 蓝图中启用 **Is Volatile**,允许鼠标事件穿透到模型。 --- ### 示例项目参考 官方示例 **《内容示例》**(Content Examples)中的 `UI_UMG` 地图演示了基础 3D UI 实现[^2]。若需曲面贴合技术,可参考 `UI_SlatePostBuffer` 的渲染目标应用[^2]。 --- ### 注意事项 - **性能开销**:渲染到纹理会增加 GPU 负载,建议用于静态 UI。 - **分辨率适配**:渲染目标尺寸需匹配模型表面比例,避免拉伸。 - **动态更新**:若 UI 需实时刷新(如血条),在 Tick 事件中调用 `Draw Widget to Render Target`。 > 通过 Widget Component 可直接实现基础 3D UI 附着,而渲染到材质方案适用于复杂曲面。根据需求选择方案,并优先测试性能影响[^1][^3]。 [^1]: Unreal Engine 是一个功能强大且灵活的 3D 游戏引擎,适用于多种应用领域,从游戏开发到虚拟现实影视制作。 [^2]: 《内容示例》是学习UE5的基础示例,可以用此熟悉一遍UE5的功能。 [^3]: 通过学习实践,你已经掌握了Unreal Engine的基本高级功能。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值