UE5 Lyra PocketWorld进行3D内容UI预览 - 下

PocketWorld是UE5 Lyra Demo中的插件,用于在UI中预览3D内容,其内部包含了RT生成等逻辑。这部分主要分享PocketLevel的使用,通过流送形式加载关卡进行拍摄。以及一些PocketWorld代码相关内容。

上半部分:https://blog.csdn.net/grayrail/article/details/163824157

最终效果:


1.将Init函数更改为事件

2.初始化部分沿用上半部分文章的逻辑,图像绑定更换为SetBrushResourceObject接口,便于演示。

3.因为PocketLevel部分没有被标记为蓝图可调,需要修改C++。

PocketLevelSystem.h

cpp 复制代码
// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "Subsystems/WorldSubsystem.h"
#include "PocketLevelSystem.generated.h"

class ULocalPlayer;
class UObject;
class UPocketLevel;
class UPocketLevelInstance;

/**
 * Manages streaming pocket level instances per local player.
 */
UCLASS(BlueprintType)
class POCKETWORLDS_API UPocketLevelSubsystem : public UWorldSubsystem
{
	GENERATED_BODY()

public:
	UFUNCTION(BlueprintCallable, Category = "Pocket Level", meta = (WorldContext = "WorldContextObject"))
	static UPocketLevelSubsystem* Get(const UObject* WorldContextObject);

	/** Loads or reuses a pocket level instance for the given player. */
	UFUNCTION(BlueprintCallable, Category = "Pocket Level")
	UPocketLevelInstance* GetOrCreatePocketLevelFor(ULocalPlayer* LocalPlayer, UPocketLevel* PocketLevel, FVector DesiredSpawnPoint);

private:
	UPROPERTY()
	TArray<TObjectPtr<UPocketLevelInstance>> PocketInstances;
};

PocketLevelSystem.cpp

cpp 复制代码
// Copyright Epic Games, Inc. All Rights Reserved.

#include "PocketLevelSystem.h"

#include "Engine/Engine.h"
#include "PocketLevel.h"
#include "PocketLevelInstance.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(PocketLevelSystem)

UPocketLevelSubsystem* UPocketLevelSubsystem::Get(const UObject* WorldContextObject)
{
	if (const UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
	{
		return World->GetSubsystem<UPocketLevelSubsystem>();
	}

	return nullptr;
}

UPocketLevelInstance* UPocketLevelSubsystem::GetOrCreatePocketLevelFor(ULocalPlayer* LocalPlayer, UPocketLevel* PocketLevel, FVector DesiredSpawnPoint)
{
	if (PocketLevel == nullptr)
	{
		return nullptr;
	}

	float VerticalBoundsOffset = 0;
	for (UPocketLevelInstance* Instance : PocketInstances)
	{
		if (Instance->LocalPlayer == LocalPlayer && Instance->PocketLevel == PocketLevel)
		{
			return Instance;
		}

		VerticalBoundsOffset += Instance->PocketLevel->Bounds.Z;
	}

	const FVector SpawnPoint = DesiredSpawnPoint + FVector(0, 0, VerticalBoundsOffset);

	UPocketLevelInstance* NewInstance = NewObject<UPocketLevelInstance>(this);
	NewInstance->Initialize(LocalPlayer, PocketLevel, SpawnPoint);

	PocketInstances.Add(NewInstance);

	return NewInstance;
}

PocketLevelInstance.h

cpp 复制代码
// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "Math/BoxSphereBounds.h"

#include "UObject/ObjectPtr.h"
#include "PocketLevelInstance.generated.h"

class UPocketLevelSubsystem;

class ULevelStreamingDynamic;
class AActor;
class ULocalPlayer;
class UPocketLevel;
class UPocketLevelInstance;
class UWorld;
struct FFrame;

DECLARE_MULTICAST_DELEGATE_OneParam(FPocketLevelInstanceEvent, UPocketLevelInstance*);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPocketLevelInstanceReady, UPocketLevelInstance*, PocketLevelInstance);

/**
 * A single streamed pocket level instance owned by PocketLevelSubsystem.
 */
UCLASS(Within = PocketLevelSubsystem, BlueprintType)
class POCKETWORLDS_API UPocketLevelInstance : public UObject
{
	GENERATED_BODY()

public:
	UPocketLevelInstance();

	virtual void BeginDestroy() override;

	UFUNCTION(BlueprintCallable, Category = "Pocket Level")
	void StreamIn();

	UFUNCTION(BlueprintCallable, Category = "Pocket Level")
	void StreamOut();

	UFUNCTION(BlueprintCallable, Category = "Pocket Level")
	bool IsReady() const;

	UFUNCTION(BlueprintCallable, Category = "Pocket Level")
	FVector GetSpawnOrigin() const;

	UFUNCTION(BlueprintCallable, Category = "Pocket Level")
	TArray<AActor*> GetLevelActors() const;

	FDelegateHandle AddReadyCallback(FPocketLevelInstanceEvent::FDelegate Callback);
	void RemoveReadyCallback(FDelegateHandle CallbackToRemove);

	UPROPERTY(BlueprintAssignable, Category = "Pocket Level")
	FOnPocketLevelInstanceReady OnReady;

	virtual class UWorld* GetWorld() const override { return World; }

private:
	bool Initialize(ULocalPlayer* LocalPlayer, UPocketLevel* PocketLevel, FVector SpawnPoint);

	UFUNCTION()
	void HandlePocketLevelLoaded();

	UFUNCTION()
	void HandlePocketLevelShown();

private:
	UPROPERTY()
	TObjectPtr<ULocalPlayer> LocalPlayer;

	UPROPERTY()
	TObjectPtr<UPocketLevel> PocketLevel;

	UPROPERTY()
	TObjectPtr<UWorld> World;

	UPROPERTY()
	TObjectPtr<ULevelStreamingDynamic> StreamingPocketLevel;

	FPocketLevelInstanceEvent OnReadyEvent;

	FBoxSphereBounds Bounds;

	friend class UPocketLevelSubsystem;
};

PocketLevelInstance.cpp

cpp 复制代码
// Copyright Epic Games, Inc. All Rights Reserved.

#include "PocketLevelInstance.h"

#include "Engine/Level.h"
#include "Engine/LevelStreaming.h"
#include "Engine/LevelStreamingDynamic.h"
#include "Engine/LocalPlayer.h"
#include "GameFramework/PlayerController.h"
#include "PocketLevel.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(PocketLevelInstance)

UPocketLevelInstance::UPocketLevelInstance()
{

}

bool UPocketLevelInstance::Initialize(ULocalPlayer* InLocalPlayer, UPocketLevel* InPocketLevel, FVector InSpawnPoint)
{
	LocalPlayer = InLocalPlayer;
	World = LocalPlayer->GetWorld();
	PocketLevel = InPocketLevel;
	Bounds = FBoxSphereBounds(FSphere(InSpawnPoint, PocketLevel->Bounds.GetAbsMax()));

	if (ensure(StreamingPocketLevel == nullptr))
	{
		if (ensure(!PocketLevel->Level.IsNull()))
		{
			bool bSuccess = false;
			StreamingPocketLevel = ULevelStreamingDynamic::LoadLevelInstanceBySoftObjectPtr(LocalPlayer, PocketLevel->Level, Bounds.Origin, FRotator::ZeroRotator, bSuccess);

			if (ensure(bSuccess && StreamingPocketLevel))
			{
				StreamingPocketLevel->OnLevelLoaded.AddUniqueDynamic(this, &ThisClass::HandlePocketLevelLoaded);
				StreamingPocketLevel->OnLevelShown.AddUniqueDynamic(this, &ThisClass::HandlePocketLevelShown);
			}

			return bSuccess;
		}
	}

	return false;
}

void UPocketLevelInstance::StreamIn()
{
	if (StreamingPocketLevel)
	{
		StreamingPocketLevel->SetShouldBeVisible(true);
		StreamingPocketLevel->SetShouldBeLoaded(true);
	}
}

void UPocketLevelInstance::StreamOut()
{
	if (StreamingPocketLevel)
	{
		StreamingPocketLevel->SetShouldBeVisible(false);
		StreamingPocketLevel->SetShouldBeLoaded(false);
	}
}

bool UPocketLevelInstance::IsReady() const
{
	return StreamingPocketLevel && StreamingPocketLevel->GetLevelStreamingState() == ELevelStreamingState::LoadedVisible;
}

FVector UPocketLevelInstance::GetSpawnOrigin() const
{
	return Bounds.Origin;
}

TArray<AActor*> UPocketLevelInstance::GetLevelActors() const
{
	TArray<AActor*> Result;

	if (StreamingPocketLevel)
	{
		if (const ULevel* LoadedLevel = StreamingPocketLevel->GetLoadedLevel())
		{
			for (AActor* Actor : LoadedLevel->Actors)
			{
				if (Actor)
				{
					Result.Add(Actor);
				}
			}
		}
	}

	return Result;
}

FDelegateHandle UPocketLevelInstance::AddReadyCallback(FPocketLevelInstanceEvent::FDelegate Callback)
{
	if (StreamingPocketLevel && StreamingPocketLevel->GetLevelStreamingState() == ELevelStreamingState::LoadedVisible)
	{
		Callback.ExecuteIfBound(this);
	}
	
	return OnReadyEvent.Add(Callback);
}

void UPocketLevelInstance::RemoveReadyCallback(FDelegateHandle CallbackToRemove)
{
	OnReadyEvent.Remove(CallbackToRemove);
}

void UPocketLevelInstance::BeginDestroy()
{
	Super::BeginDestroy();

	if (StreamingPocketLevel)
	{
		StreamingPocketLevel->bShouldBlockOnUnload = false;
		StreamingPocketLevel->SetShouldBeLoaded(false);
		StreamingPocketLevel->OnLevelShown.RemoveAll(this);
		StreamingPocketLevel->OnLevelLoaded.RemoveAll(this);
		StreamingPocketLevel = nullptr;
	}
}

void UPocketLevelInstance::HandlePocketLevelLoaded()
{
	if (StreamingPocketLevel)
	{
		// Make everything in the level setup so that it's setup on the client, and we treat
		// everything as locally spawned, rather than bExchangedRoles = true, where it's spawned
		// on the client, but the expectation is the server said do it, and the server is going to 
		// be telling us about them later.
		if (ULevel* LoadedLevel = StreamingPocketLevel->GetLoadedLevel())
		{
			LoadedLevel->bClientOnlyVisible = true;

			for (AActor* Actor : LoadedLevel->Actors)
			{
				if (Actor)
				{
					Actor->bExchangedRoles = true;  // HACK, Remove when bClientOnlyVisible is all we need.
				}
			}

			// TODO: Don't put ownership over shared pocket spaces.
			if (LocalPlayer)
			{
				if (APlayerController* PC = LocalPlayer->GetPlayerController(GetWorld()))
				{
					for (AActor* Actor : LoadedLevel->Actors)
					{
						if (Actor)
						{
							Actor->SetOwner(PC);
						}
					}
				}
			}
		}
	}
}

void UPocketLevelInstance::HandlePocketLevelShown()
{
	OnReadyEvent.Broadcast(this);
	OnReady.Broadcast(this);
}

PocketLevel.h

cpp 复制代码
// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "Engine/DataAsset.h"

#include "PocketLevel.generated.h"

class UObject;
class UWorld;

/**
 * Data asset describing a pocket level to stream in off-world.
 */
UCLASS(BlueprintType)
class POCKETWORLDS_API UPocketLevel : public UDataAsset
{
	GENERATED_BODY()

public:
	UPocketLevel();

public:
	// The level that will be streamed in for this pocket level.
	UPROPERTY(EditAnywhere, Category = "Streaming")
	TSoftObjectPtr<UWorld> Level;
	
	// The bounds of the pocket level so that we can create multiple instances without overlapping each other.
	UPROPERTY(EditAnywhere, Category = "Streaming")
	FVector Bounds;	
};

4.Pocket Level部分蓝图接口,其中Get or Create Pocket Level For中的参数暂时没有先不填。

5.创建空场景CaptureMap

6.创建DataAsset对象PocketLevel01,配置Level,设置Level的Bounds,该参数会被应用于堆叠逻辑。

7.补全Get or Create Pocket Level For的参数。运行测试即可。

相关推荐
poiu12346571 小时前
视频离线观看用什么软件方便?2026多场景工具实测对比
学习
爱编程的Zion1 小时前
Jenkins 从 0 到 1 学习笔记
笔记·学习·jenkins
平常心的技术小牛1 小时前
Qt-快速上手-QLabel
开发语言·qt
XR1234567881 小时前
工厂办公楼无线网络:多楼层覆盖与访客体验怎么选?
开发语言·php
JL151 小时前
Java并发编程面试全攻略-从synchronized到AQS底层原理
java·开发语言·面试·并发编程
云泽8081 小时前
Python 开发环境搭建全指南:从 Python 安装到 PyCharm 配置详解
开发语言·python·pycharm
不会代码的小猴1 小时前
2. 了解Qt
开发语言·c++·笔记·qt·算法
ZJU_统一阿萨姆1 小时前
【推理优化进阶】Hopper_Blackwell 微架构:从指令、流水线到真实性能上限
开发语言·人工智能·语言模型·架构·开源
程序喵大人3 小时前
【C++进阶】STL算法与函数对象 - 09 函数对象保存状态并复用规则
开发语言·c++·算法·stl·函数对象