上面的文章解释了很多 Unreal 的扩展方式,包括提示消息、自定义细节面板、自定义数据类型、自定义世界大纲、自定义图标编辑器等
目前需要一个结构体,结构体中有一个属性可以配置指定类型的类,将其指定类型的类的属性显示到一个 Map 中
根据前面的展示效果,需要两个事件
EditObject 属性变化事件Edit Object Config View 显示对象的属性变化事件一般来说自定义细节面板是有着固定套路的
需要准备两个东西:对象类、自定义细节面板类,在模块加载的时候将对象类与自定义细节面板类关联起来
USTRUCT(BlueprintType)
struct CUSTOMDATATABLERUNTIME_API FObjectData
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere)
TSoftClassPtr<class UEditObject> EditObject;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
TMap<FString, FString> CustomData;
};
USTRUCT(BlueprintType)
struct CUSTOMDATATABLERUNTIME_API FEditData : public FTableRowBase
{
GENERATED_USTRUCT_BODY()
public:
// 自定义Object
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FObjectData ObjectData;
};
/**
*
*/
UCLASS(BlueprintType, Blueprintable)
class CUSTOMDATATABLERUNTIME_API UEditObject : public UObject
{
GENERATED_BODY()
};
以上述代码为例,public FTableRowBase 的结构体能够生成为 DataTable 表,表编辑的对象是 FObjectData 对象
FObjectData 中可以配置 EditObject,也就是能够选择继承自 UEditObject 的类,并将设置的属性值设置到 CustomData 映射表中
如上图所示,设置面板不仅 CustomData 不可手动修改,并且增加了一个名为 Edit ObjectCOnfig Value 的修改面板,其内容对应的就是 BP_Test 中的内容
前面这些代码写在 Runtime 模块中,因为这些结构体在游戏时会用到,对应的编辑界面则写在 Editor 模块中
根据 Unreal 自定义细节面板的套路,新建一个类 FEditObjectCustomization 继承自 IPropertyTypeCustomization,并且通过 RegisterCustomPropertyTypeLayout 将 ObjectData 与 FEditObjectCustomization 关联起来
由于细节面板需要模块加载的时候注册,在模块卸载的时候注销,所以在 StartupModule 中编写注册逻辑
void FCustomDataTableModule::StartupModule()
{
static FName PropertyEditor("PropertyEditor");
FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>(PropertyEditor);
PropertyModule.RegisterCustomPropertyTypeLayout("ObjectData", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FEditObjectCustomization::MakeInstance));
}
这里注册的
ObjectData起始就是需要自定义细节面板的FObjectData结构体不要前面的F
注册完毕之后,需要关注的就是 FEditObjectCustomization 类了,这个是实现自定义系界面面板的主要类
class FEditObjectCustomization : public IPropertyTypeCustomization
{
public:
static TSharedRef<IPropertyTypeCustomization> MakeInstance();
FEditObjectCustomization() {}
/** IPropertyTypeCustomization instance */
virtual void CustomizeHeader(TSharedRef<IPropertyHandle> StructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
virtual void CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
}
需要自己实现的就是有三个函数 MakeInstance、CustomizeHeader、CustomizeChildren
根据 RegisterCustomPropertyTypeLayout 函数可以推理出,当需要显示 ObjectData 时,会调用 FEditObjectCustomization::MakeInstance 函数获得面板对象
至于 CustomizeHeader 函数,顾名思义可以用来自定义头,一般来说不做什么操作直接显示 Title 就行了
void FEditObjectCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> StructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
const bool bDisplayResetToDefault = false;
// const FText DisplayNameOverride = FText::GetEmpty();
// const FText DisplayToolTipOverride = FText::GetEmpty();
const FText DisplayNameOverride = FText::FromString(TEXT("DisplayNameOverride"));
const FText DisplayToolTipOverride = FText::FromString(TEXT("DisplayToolTipOverride"));
HeaderRow
.NameContent()
[
StructPropertyHandle->CreatePropertyNameWidget(DisplayNameOverride, DisplayToolTipOverride, bDisplayResetToDefault)
];
}
为了表示
Header的作用这里设置了DisplayNameOverride的值,最简单的就可以像代码中的注释内容一样,直接设置为FText::GetEmpty()
CustomizeChildren 说白了就是自定义细节面板的主要内容
| 参数 | 说明 |
|---|---|
| TSharedRef StructPropertyHandle | 属性的句柄,用于操作属性值和获取属性信息 |
| FDetailWidgetRow& StructBuilder | 用于构建自定义头部行的 UI |
| IPropertyTypeCustomizationUtils& StructCustomizationUtils | 提供了一些辅助功能,如刷新面板等 |