添加自定义datatable属性面板

This commit is contained in:
997146918 2025-06-24 15:01:58 +08:00
parent 377eff645b
commit 54ba19f832
13 changed files with 280 additions and 0 deletions

View File

@ -8,6 +8,11 @@
"Name": "ProjectFish",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "ProjectFishEditor",
"Type": "Editor",
"LoadingPhase": "PostEngineInit"
}
],
"Plugins": [

View File

@ -11,5 +11,11 @@ public class ProjectFishTarget : TargetRules
DefaultBuildSettings = BuildSettingsVersion.V5;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_5;
ExtraModuleNames.Add("ProjectFish");
RegisterModulesCreatedByRider();
}
private void RegisterModulesCreatedByRider()
{
ExtraModuleNames.AddRange(new string[] { "ProjectFishEditor" });
}
}

View File

@ -130,6 +130,19 @@ struct FSkillData: public FTableRowBase
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "技能效果组"))
TArray<FSkillEffectData> SkillEffects;
};
USTRUCT(BlueprintType)
struct FSkillDataConfig
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (RowType = "FSkillData"))
UDataTable* DataTable;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FName SkillRowName;
};
/**
*
*/

View File

@ -11,5 +11,11 @@ public class ProjectFishEditorTarget : TargetRules
DefaultBuildSettings = BuildSettingsVersion.V5;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_5;
ExtraModuleNames.Add("ProjectFish");
RegisterModulesCreatedByRider();
}
private void RegisterModulesCreatedByRider()
{
ExtraModuleNames.AddRange(new string[] { "ProjectFishEditor" });
}
}

View File

@ -0,0 +1,154 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "DataTableRowSelectorCustomization.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "IDetailChildrenBuilder.h"
#include "IPropertyUtilities.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "ProjectFish/Definations.h"
void FDataTableRowSelectorCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle,
FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils)
{
HeaderRow
.NameContent()
[
PropertyHandle->CreatePropertyNameWidget()
];
}
void FDataTableRowSelectorCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> PropertyHandle,
IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils)
{
//获取meta中的过滤类型
DataTableHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FSkillDataConfig, DataTable));
FProperty* metaProperty = DataTableHandle->GetMetaDataProperty();
AllowRowType = metaProperty->GetMetaData("RowType");
TWeakPtr<IPropertyUtilities> PropertyUtils = CustomizationUtils.GetPropertyUtilities();
//动态生成DataTable下拉菜单
RefreshDataTableAfterFilter();
//获得默认的属性值
UObject* CurrentValue = nullptr;
DataTableHandle->GetValue(CurrentValue); // 获取当前设置的 DataTable
SelectedDataTable = CurrentValue ? FName(CurrentValue->GetName()) : NAME_None;
//添加datatable 列表
TSharedPtr<SComboBox<FName>> DataTableComboBox = SNew(SComboBox<FName>)
.OptionsSource(&FilteredDataTableNames)
.Content()
[
SNew(STextBlock)
.Text_Lambda([this]() -> FText {
return FText::FromName(SelectedDataTable);
})
]
.OnGenerateWidget_Lambda([](FName InItem)
{
UDataTable* dataTable = Cast<UDataTable>(FSoftObjectPath(InItem.ToString()).TryLoad());
return SNew(STextBlock)
.Text(FText::FromString(dataTable->GetName()));
})
.OnSelectionChanged_Lambda([this, PropertyHandle, PropertyUtils, &DataTableComboBox, &ChildBuilder](FName SelectedTableName, ESelectInfo::Type SelectInfo)
{
UDataTable* dataTable = Cast<UDataTable>(FSoftObjectPath(SelectedTableName.ToString()).TryLoad());
DataTableHandle->SetValue(dataTable);
SelectedDataTable = FName(dataTable->GetName());
//更新行选择器
GenerateRowNameSelecter( PropertyHandle, ChildBuilder);
PropertyUtils.Pin()->ForceRefresh();
});
ChildBuilder.AddCustomRow(FText::FromString(TEXT("DataTable")))
.NameContent()
[ SNew(STextBlock)
.Text(FText::FromString(TEXT("SkillDataTable")))
.Font(IDetailLayoutBuilder::GetDetailFont())
]
.ValueContent()
[DataTableComboBox.ToSharedRef()];
//添加默认的row列表
if (!SelectedDataTable.IsNone())
{
GenerateRowNameSelecter( PropertyHandle, ChildBuilder);
}
}
void FDataTableRowSelectorCustomization::RefreshDataTableAfterFilter()
{
FilteredDataTableNames.Empty();
FAssetRegistryModule& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
TArray<FAssetData> AllDataTables;
AssetRegistry.Get().GetAssetsByClass(UDataTable::StaticClass()->GetClassPathName(), AllDataTables);
//UEn内部存储是没有F的
FString BaseName = AllowRowType.StartsWith("F") ? AllowRowType.Mid(1) : AllowRowType;
for (const FAssetData& Asset: AllDataTables)
{
FString RowStructPath = Asset.GetTagValueRef<FString>("RowStructure");
if (RowStructPath.EndsWith(BaseName))
{
FilteredDataTableNames.Add(FName(Asset.GetObjectPathString()));
//FilteredDataTables.Add(MakeShareable(Cast<UDataTable>(FSoftObjectPath(Asset.GetObjectPathString()).TryLoad())));
}
}
}
void FDataTableRowSelectorCustomization::GenerateRowNameSelecter(TSharedRef<IPropertyHandle> PropertyHandle, IDetailChildrenBuilder& ChildBuilder)
{
RowNameHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FSkillDataConfig, SkillRowName));
UObject* CurrentTable = nullptr;
DataTableHandle->GetValue(CurrentTable);
UDataTable* CurrentDataTable = Cast<UDataTable>(CurrentTable);
if (CurrentDataTable)
{
//获得默认的选中行
FName defaultRowName;
RowNameHandle->GetValue(defaultRowName);
FSkillData* skillData = CurrentDataTable->FindRow<FSkillData>(defaultRowName, "");
if (skillData)
SelectedRowName = FName(skillData->SkillName.ToString());
DataTableRows = CurrentDataTable->GetRowNames();
TSharedPtr<SComboBox<FName>> RowNameComboBox = SNew(SComboBox<FName>)
.OptionsSource(&DataTableRows)
.Content()
[
SNew(STextBlock)
.Text_Lambda([this]() -> FText {
return FText::FromName(SelectedRowName);
})
]
.OnGenerateWidget_Lambda([this](FName rowName) {
UObject* CurrentTable = nullptr;
DataTableHandle->GetValue(CurrentTable);
UDataTable* CurrentDataTable = Cast<UDataTable>(CurrentTable);
FSkillData* skillData = CurrentDataTable->FindRow<FSkillData>(rowName, "");
return SNew(STextBlock).Text(skillData->SkillName);
})
.OnSelectionChanged_Lambda([&](FName SelectedDataTableName, ESelectInfo::Type) {
UObject* CurrentTable = nullptr;
DataTableHandle->GetValue(CurrentTable);
UDataTable* CurrentDataTable = Cast<UDataTable>(CurrentTable);
FSkillData* skillData = CurrentDataTable->FindRow<FSkillData>(SelectedDataTableName, "");
SelectedRowName = FName(skillData->SkillName.ToString());
RowNameHandle->SetValue(SelectedDataTableName);
});
ChildBuilder.AddCustomRow(FText::FromString(TEXT("Row")))
.NameContent()[
SNew(STextBlock)
.Text(FText::FromString(TEXT("SkillRowName")))
.Font(IDetailLayoutBuilder::GetDetailFont())
]
.ValueContent()[ RowNameComboBox.ToSharedRef() ];
}
}

View File

@ -0,0 +1,25 @@
#include "ProjectFishEditor.h"
#include "DataTableRowSelectorCustomization.h"
#define LOCTEXT_NAMESPACE "FProjectFishEditorModule"
void FProjectFishEditorModule::StartupModule()
{
FPropertyEditorModule& PropModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropModule.RegisterCustomPropertyTypeLayout("SkillDataConfig"
, FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FDataTableRowSelectorCustomization::MakeInstance));
}
void FProjectFishEditorModule::ShutdownModule()
{
if ( FPropertyEditorModule* PropModule = FModuleManager::GetModulePtr<FPropertyEditorModule>("PropertyEditor"))
{
PropModule->UnregisterCustomPropertyTypeLayout("SkillDataConfig");
}
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FProjectFishEditorModule, ProjectFishEditor)

View File

@ -0,0 +1,28 @@
using UnrealBuildTool;
public class ProjectFishEditor : ModuleRules
{
public ProjectFishEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
OptimizeCode = CodeOptimization.Never;
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"ProjectFish",
"InputCore"
}
);
}
}

View File

@ -0,0 +1,32 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
/**
*
*/
class FDataTableRowSelectorCustomization: public IPropertyTypeCustomization
{
public:
static TSharedRef<IPropertyTypeCustomization> MakeInstance()
{
return MakeShareable(new FDataTableRowSelectorCustomization());
}
FDataTableRowSelectorCustomization(){}
virtual void CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override;
virtual void CustomizeChildren(TSharedRef<IPropertyHandle> PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override;
private:
void RefreshDataTableAfterFilter();
void GenerateRowNameSelecter(TSharedRef<IPropertyHandle> PropertyHandle, IDetailChildrenBuilder& ChildBuilder);
TSharedPtr<IPropertyHandle> DataTableHandle;
FName SelectedDataTable = NAME_None; //选中的datatable
TSharedPtr<IPropertyHandle> RowNameHandle;
FName SelectedRowName = NAME_None;; //选中的row
TArray<FName> FilteredDataTableNames;
TArray<FName> DataTableRows;
FString AllowRowType; //dataTable过滤的行类型
};

View File

@ -0,0 +1,11 @@
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
class FProjectFishEditorModule : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};