UE4 扩展详细信息面板

12 篇文章 0 订阅

在我进一步深入代码之前,这是最终结果:
在这里插入图片描述

为了达到上述结果,我们需要执行以下步骤:
创建自定义模块
创建特定资产
创建我们自己的详细信息面板(将扩展默认面板)

测试功能

这篇文章不会涵盖该过程的第一步,因为我已经在这里写了一篇关于它的教程

添加所需的依赖项
对于这篇文章,我创建了一个名为“BlogpostModule”的自定义模块。在它的 .build.cs 文件中,我添加了以下依赖项:

公共依赖模块名称。AddRange ( new string [ ] { "Core" , "CoreUObject" , "Engine" , "PropertyEditor" , "Slate" , "SlateCore" } ) ;
我们需要这些依赖项的原因是因为我们将使用 Slate 来扩展详细信息面板。

在继续之前,请确保编译您的代码。

创建测试 Actor

为了让我们的自定义详细信息面板正常工作,我们需要告诉我们的模块我们想要绑定一个特定的详细信息面板,以便在我们修改特定类时出现。这就是我们要添加一个测试类来展示功能的原因。我将我的类命名为“FancyCube”,并将其也放入 BlogpostModule。这是它的代码:

#pragma once
 
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "FancyCube.generated.h"
 
UCLASS()
class BLOGPOSTMODULE_API AFancyCube : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AFancyCube();
 
	void AddKeyValue();
 
protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
 
	UPROPERTY(VisibleAnywhere)
	UStaticMeshComponent* CubeSM;
 
public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;
 
	
	
};
#include "FancyCube.h"
 
 
// Sets default values
AFancyCube::AFancyCube()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
	CubeSM = CreateDefaultSubobject<UStaticMeshComponent>("CubeSM");
}
 
void AFancyCube::AddKeyValue()
{
	if (CubeSM)
	{
		//Do Some Thing
	}
	
}
 
// Called when the game starts or when spawned
void AFancyCube::BeginPlay()
{
	Super::BeginPlay();
	
}
 
// Called every frame
void AFancyCube::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
 
}

一旦我们创建了自定义详细信息面板,我们将告诉我们的模块将其分配给上面的“AFancyCube”类。现在,根据上面的类创建一个蓝图,并将以下材质分配给它的网格:

扩展详细信息面板

为了扩展详细信息面板,您必须添加一个继承对象类的类。请记住,此类不会使用典型的 UCLASS 宏进行标记,稍后我们将替换默认的构造函数和析构函数。创建类后,在其头文件中输入以下代码:


#pragma once
 
#include "CoreMinimal.h"
#include "Input/Reply.h"
#include "IDetailCustomization.h"
 
class FCustomDetailsPanel : public IDetailCustomization
{
 
private:
 
	/* Contains references to all selected objects inside in the viewport */
	TArray<TWeakObjectPtr<UObject>> SelectedObjects;
 
public:
 
	/* Makes a new instance of this detail layout class for a specific detail view requesting it */
	static TSharedRef<IDetailCustomization> MakeInstance();
 
	/* IDetalCustomization interface */
	virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
 
	/* The code that fires when we click the "ChangeColor" button */
	FReply ClickedOnButton();
 
};

然后,在源文件中键入以下代码:

#include "CustomDetailsPanel.h" //make sure to replace this include to match your class name
#include "IDetailsView.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "DetailCategoryBuilder.h"
#include "Widgets/SNullWidget.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/SBoxPanel.h"
#include "Text.h"
#include "FancyCube.h"
#include "UObject/Class.h"


TSharedRef<IDetailCustomization> FCustomDetailsPanel::MakeInstance()
{
	return MakeShareable(new FCustomDetailsPanel);
}

void FCustomDetailsPanel::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
	//Edits a category. If it doesn't exist it creates a new one
	IDetailCategoryBuilder& CustomCategory = DetailBuilder.EditCategory("CustomCategory");

	//Store the currently selected objects from the viewport to the SelectedObjects array.
	DetailBuilder.GetObjectsBeingCustomized(SelectedObjects);

	//Adding a custom row
	CustomCategory.AddCustomRow(FText::FromString("Add Key-Value"))
		.ValueContent()
		.VAlign(VAlign_Center) // set vertical alignment to center
		.MaxDesiredWidth(250)
		[ //With this operator we declare a new slate object inside our widget row
		  //In this case the slate object is a button
			SNew(SButton)
			.VAlign(VAlign_Center)
		.OnClicked(this, &FCustomDetailsPanel::ClickedOnButton) //Binding the OnClick function we want to execute when this object is clicked
		.Content()
		[ //We create a new slate object inside our button. In this case a text block with the content of "Change Color"
		  //If you ever coded a UMG button with a text on top of it you will notice that the process is quite the same
		  //Meaning, you first declare a button which has various events and properties and then you place a Text Block widget
		  //inside the button's widget to display text
			SNew(STextBlock).Text(FText::FromString("Add Key-Value"))
		]
		];
	
}

FReply FCustomDetailsPanel::ClickedOnButton()
{	
	if (GEngine)
	{
		for (const TWeakObjectPtr<UObject>& Object : SelectedObjects)
		{
			AFancyCube* FancyCube = Cast<AFancyCube>(Object.Get());
			if (FancyCube)
			{
				FancyCube->AddKeyValue();
			}
		}
		GLog->Log("Add a Key-Value!");
	}
	return FReply::Handled();
}

正如您在 CustomizeDetails 函数中看到的那样,我们使用“[ ]”运算符来输入“不寻常”的代码。本质上,在 slate 中,这些运算符创建了一个新的 Slate Widget,我们在其中提供了描述其功能的属性(例如其外观和/或内容)。如果您深入研究引擎的代码,例如 第 113 行的 DetailWidgetRow.h,您会注意到该运算符背后的逻辑非常简单。(即每次使用此运算符时,您都必须提供一个新的 Slate Widget)。如果你仔细想想,这个逻辑类似于 UMG 小部件的工作方式。

将详细信息面板绑定到Actor

在这一点上,我们需要做的最后一件事是将所有内容联系在一起。进入模块的启动函数并输入以下代码:

#include "BlogpostModule.h"
#include "FancyCube.h"
#include "CustomDetailsPanel.h"
#include "PropertyEditorModule.h"

DEFINE_LOG_CATEGORY(BlogpostModule);

#define LOCTEXT_NAMESPACE "FBlogpostModule"

void FBlogpostModule::StartupModule()
{
	UE_LOG(BlogpostModule, Warning, TEXT("BlogpostModule module has started!"));
	//Get the property module
	FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
	//Register the custom details panel we have created
	PropertyModule.RegisterCustomClassLayout(AFancyCube::StaticClass()->GetFName(), FOnGetDetailCustomizationInstance::CreateStatic(&FCustomDetailsPanel::MakeInstance));
}

void FBlogpostModule::ShutdownModule()
{
	UE_LOG(BlogpostModule, Warning, TEXT("BlogpostModule module has shut down"));
}

#undef LOCTEXT_NAMESPACE

IMPLEMENT_MODULE(FBlogpostModule,BlogpostModule)

此时,编译你的模块,当你选择任何“FancyCube”Actor时,你应该会看到自定义详细信息面板。请记住,您可能必须重新启动编辑器才能看到更改。

但是这个方式有个巨大的问题,当

PropertyModule.RegisterCustomClassLayout(AFancyCube::StaticClass()->GetFName(), FOnGetDetailCustomizationInstance::CreateStatic(&FCustomDetailsPanel::MakeInstance));

改为

PropertyModule.RegisterCustomClassLayout(AActor::StaticClass()->GetFName(), FOnGetDetailCustomizationInstance::CreateStatic(&FCustomDetailsPanel::MakeInstance));

时是完全不生效的,因为这个方法只适用于继承于UObject的自定义的类,而不能是引擎自带的类,所以如果想在原来的Actor基类上进行添加是需要修改引擎源码的

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dawn·张

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值