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的参数。运行测试即可。

相关推荐
寂寞的船5 小时前
【Unity知识速递】第5期 Unity 6.2 深度解析:AI Assistant 登场,World Space UI 与 Sentis 全面升级
人工智能·ui·unity
yeccheng6 小时前
MySQL 索引超详解:从底层 B+ 树到面试高频考点
java·开发语言·jvm·mysql·面试
联犀6 小时前
纯 Go 的 CAD 开源库 go-cad:直接解析 DWG/DXF,现在也能导出 PDF
开发语言·golang·pdf
Nerd(BUG版)7 小时前
Flutter学习day5--事件手势与命中测试
学习·flutter
zjxtxdy7 小时前
C++面向对象程序开发-类与对象
开发语言·c++
Nerd(BUG版)8 小时前
KMP学习day4--Kotlin空安全与泛型
学习·安全·kotlin
東隅已逝,桑榆非晚8 小时前
算法6. 双指针法-查找总价格为目标的两个商品
c++·学习·算法
秋名山小白8 小时前
Playwright UI 自动化测试实战教程
python·ui
qq_406981039 小时前
c++拷贝构造函数
开发语言·c++
wdyan2979 小时前
带着小白学习-微服务-5 实战:配置中心-数据库连接
数据库·学习·微服务