Bootstrap

UE5 创建自定义工具上下文的方法

自定义工具管理类上下文

工具管理类:MyToolsManager,管理类依赖类:ContextQueriesAPI、ContextTransactionsAPI

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "ContextQueriesAPI.h"
#include "ContextTransactionsAPI.h"
#include "InteractiveToolsContext.h"
#include "Subsystems/WorldSubsystem.h"
#include "MyToolsManager.generated.h"

UCLASS()
class CUSTOMTOOLS_API UMyToolsManager : public UTickableWorldSubsystem 
{
	GENERATED_BODY()

public:

	static UMyToolsManager* Get(UObject* WorldContext = nullptr);
	
	// 1 重写父类方法
	virtual void OnWorldBeginPlay(UWorld& InWorld)override;
	virtual void Deinitialize() override;
	virtual bool DoesSupportWorldType(const EWorldType::Type WorldType) const override;

	virtual void Tick(float DeltaTime) override;
	virtual TStatId GetStatId() const override {RETURN_QUICK_DECLARE_CYCLE_STAT(UMyToolsManager, STATGROUP_Tickables);}
	virtual void Initialize(FSubsystemCollectionBase& Collection) override;

protected:

	// 2 初始化工具上下文
	void InitalizeToolsContext(UWorld& InWorld);

	//5 关闭工具上下文
	void ShutdownToolsContext();
	 
private:
	// 3 声明工具上下文
	UPROPERTY()
	TObjectPtr<UInteractiveToolsContext> ToolsContext;
	// 4 声明当前激活的工具
	UPROPERTY()
	TObjectPtr<UInteractiveTool> CurrentActiveTool;

	TSharedPtr<FContextQueriesAPI> QueriesAPI;
	TSharedPtr<FContextTransactionsAPI> TransactionsAPI;

	// 6 将这个管理类设置为单例模式
	static UMyToolsManager* g_MyToolsManager;
};

// Fill out your copyright notice in the Description page of Project Settings.


#include "MyToolsManager.h"

UMyToolsManager* UMyToolsManager::g_MyToolsManager = nullptr;

UMyToolsManager* UMyToolsManager::Get(UObject* WorldContext)
{
	if (!g_MyToolsManager)
	{
		UWorld* World = GEngine->GetWorldFromContextObject(WorldContext, EGetWorldErrorMode::LogAndReturnNull);
		if(!World)
		{
			World = GWorld;
		}
		if(World)
		{
			g_MyToolsManager = World->GetSubsystem<UMyToolsManager>();
		}
	}
	return g_MyToolsManager;
}

void UMyToolsManager::OnWorldBeginPlay(UWorld& InWorld)
{
	InitalizeToolsContext(InWorld);
}

void UMyToolsManager::Deinitialize()
{
	ShutdownToolsContext();
}

bool UMyToolsManager::DoesSupportWorldType(const EWorldType::Type WorldType) const
{
	return EWorldType::Game || EWorldType::PIE;
}

void UMyToolsManager::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
}

void UMyToolsManager::Initialize(FSubsystemCollectionBase& Collection)
{
	Super::Initialize(Collection);
	
}

void UMyToolsManager::InitalizeToolsContext(UWorld& InWorld)
{
	// 创建工具上下文
	ToolsContext = NewObject<UInteractiveToolsContext>();
	QueriesAPI = MakeShareable(new FContextQueriesAPI(ToolsContext,&InWorld));
	TransactionsAPI = MakeShareable(new FContextTransactionsAPI());
	
	//查看父类UInteractiveToolsContext中的Initialize方法,需要两个参数
	//IToolsContextQueriesAPI* QueriesAPI, IToolsContextTransactionsAPI* TransactionsAPI
	//所以创建了继承至他们的类ContextQueriesAPI、ContextTransactionsAPI
	ToolsContext->Initialize(QueriesAPI.Get(),TransactionsAPI.Get());
}

void UMyToolsManager::ShutdownToolsContext()
{
	if(ToolsContext)
	{
		if(ToolsContext->ToolManager)
		{
			if(ToolsContext->ToolManager->HasActiveTool(EToolSide::Mouse))
			{
				EToolShutdownType ShutdownType = ToolsContext->ToolManager->CanCancelActiveTool(EToolSide::Mouse)?EToolShutdownType::Cancel:EToolShutdownType::Completed;
				ToolsContext->ToolManager->DeactivateTool(EToolSide::Mouse,ShutdownType);
			}
		}
		ToolsContext->Shutdown();
		ToolsContext=nullptr;
	}
	QueriesAPI = nullptr;
	TransactionsAPI = nullptr;
}
#pragma once
#include "InteractiveToolsContext.h"
#include "ToolContextInterfaces.h"

class FContextQueriesAPI : public IToolsContextQueriesAPI
{
public:
	// 1 构造函数,查看父类,GetCurrentEditingWorld函数需要UWorld*,所以加入了该参数
	//   GetCurrentSelectionState函数的参数FToolBuilderState中需要UInteractiveToolManager* ToolManager,所以加入了该参数
	FContextQueriesAPI(UInteractiveToolsContext* ToolsContext, UWorld* World);
		
	// 2 重写父类方法
	virtual UWorld* GetCurrentEditingWorld() const override;
	virtual void GetCurrentSelectionState(FToolBuilderState& StateOut) const override;
	virtual void GetCurrentViewState(FViewCameraState& StateOut) const override;
	
	virtual EToolContextCoordinateSystem GetCurrentCoordinateSystem() const { return EToolContextCoordinateSystem::World; }
	virtual EToolContextTransformGizmoMode GetCurrentTransformGizmoMode() const { return EToolContextTransformGizmoMode::Combined; }
	virtual FToolContextSnappingConfiguration GetCurrentSnappingSettings() const { return FToolContextSnappingConfiguration(); }

	virtual UMaterialInterface* GetStandardMaterial(EStandardToolContextMaterials MaterialType) const override;

	//因为需要返回Viewport,所以需要声明一个Viewport变量
	virtual FViewport* GetHoveredViewport() const override;
	virtual FViewport* GetFocusedViewport() const override;

protected:

private:
	
	// 3 声明当前世界
	TWeakObjectPtr<UWorld> CurrentWorldWeakPtr;
	TWeakObjectPtr<UInteractiveToolsContext> ToolsContextWeakPtr;

	FViewport* CurrentActiveViewport = nullptr;
};
#include "ContextQueriesAPI.h"

#include "MaterialDomain.h"
#include "Kismet/GameplayStatics.h"

FContextQueriesAPI::FContextQueriesAPI(UInteractiveToolsContext* ToolsContext, UWorld* World)
	: ToolsContextWeakPtr(ToolsContext), CurrentWorldWeakPtr(World)
{
	check(ToolsContextWeakPtr.IsValid());
	
	CurrentActiveViewport = GEngine->GameViewport->Viewport;
}

UWorld* FContextQueriesAPI::GetCurrentEditingWorld() const
{
	if (CurrentWorldWeakPtr.IsValid()&&CurrentWorldWeakPtr->IsValidLowLevel())
	{
		return CurrentWorldWeakPtr.Get();
	}
	return nullptr;
}

void FContextQueriesAPI::GetCurrentSelectionState(FToolBuilderState& StateOut) const
{
	StateOut.World = CurrentWorldWeakPtr.Get();
	StateOut.ToolManager = ToolsContextWeakPtr->ToolManager;
	StateOut.TargetManager = ToolsContextWeakPtr->TargetManager;
	StateOut.GizmoManager = ToolsContextWeakPtr->GizmoManager;
	
}

void FContextQueriesAPI::GetCurrentViewState(FViewCameraState& StateOut) const
{
	TObjectPtr<APlayerCameraManager> CurrentPlayerCameraManager = UGameplayStatics::GetPlayerController(ToolsContextWeakPtr.Get(),0)->PlayerCameraManager;

	StateOut.Position = CurrentPlayerCameraManager->GetCameraLocation();
	StateOut.Orientation = CurrentPlayerCameraManager->GetCameraRotation().Quaternion();
	StateOut.HorizontalFOVDegrees = CurrentPlayerCameraManager->GetFOVAngle();
	StateOut.AspectRatio = 1.f;
	StateOut.bIsOrthographic = CurrentPlayerCameraManager->IsOrthographic();
	StateOut.bIsVR = false;	
}

UMaterialInterface* FContextQueriesAPI::GetStandardMaterial(EStandardToolContextMaterials MaterialType) const
{
	return  UMaterial::GetDefaultMaterial(MD_Surface);
}

FViewport* FContextQueriesAPI::GetHoveredViewport() const
{
	return CurrentActiveViewport;
}

FViewport* FContextQueriesAPI::GetFocusedViewport() const
{
	return CurrentActiveViewport;
}
#pragma once
#include "ToolContextInterfaces.h"

class FContextTransactionsAPI  :public IToolsContextTransactionsAPI
{
public:
	// 1 重写父类方法
	virtual void DisplayMessage(const FText& Message, EToolMessageLevel Level) override;
	virtual void PostInvalidation() override;
	virtual void BeginUndoTransaction(const FText& Description) override;
	virtual void EndUndoTransaction() override;
	virtual void AppendChange(UObject* TargetObject, TUniquePtr<FToolCommandChange> Change, const FText& Description) override;
	virtual bool RequestSelectionChange(const FSelectedObjectsChangeList& SelectionChange) override;
};


#include "ContextTransactionsAPI.h"
#include "InteractiveToolChange.h" 

 void FContextTransactionsAPI::DisplayMessage(const FText& Message, EToolMessageLevel Level)
{
  UE_LOG(LogTemp,Log,TEXT("[Tool Message] %s"),*Message.ToString());
}

 void FContextTransactionsAPI::PostInvalidation()
{
}

 void FContextTransactionsAPI::BeginUndoTransaction(const FText& Description)
{
}

 void FContextTransactionsAPI::EndUndoTransaction()
{
}

 void FContextTransactionsAPI::AppendChange(UObject* TargetObject, TUniquePtr<FToolCommandChange> Change,
	const FText& Description)
{
  //编译报错,需要添加"InteractiveToolsFramework"模块,并加入#include "InteractiveToolChange.h" 头文件
}

 bool FContextTransactionsAPI::RequestSelectionChange(const FSelectedObjectsChangeList& SelectionChange)
{
  return false;
}

 到这里是创建工具管理类的基本方法,创建完整的自定义工具类还需要继续创建自定义工具,然后在工具管理类中注册

;