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

// 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

// 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

// 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

// 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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值