zoukankan      html  css  js  c++  java
  • UE4插件创建


    分类:

    • Blank
    • BlueprintLibrary
    • ContentOnly
    • Editor Toolbar Button
    • Editor Standalone Window
    • Editor Mode
    • Third Party Library

    插件中的代码

    生成Visual Studio或Xcode的项目文件时,含有 Source 文件夹(包含 .Build.cs 文件)的插件将被添加到项目文件,以便导航到其源代码。编译游戏项目时,UBT将自动编译此类插件。

    插件可含有任意数量的模块源目录。多数插件仅有一个模块(但可创建多个模块,例如插件包含纯编辑器功能时),及游戏期间要运行的其他代码。

    插件源文件的大部分布局与引擎中其他C++模块相同。

    在模块的 Source 目录(或其子目录)内,插件可在标头文件中声明新反映的类型(UCLASSUSTRUCT 等)。引擎的构建系统将检测此类文件,并按需要生成代码支持新类型。需遵守C++模块中使用 Uobjects 时的一般规则,例如在模块的源文件中包含生成的标头文件和模块 generated.inl 文件。

    UE4支持共生模块和插件。通过在自身.uproject文件中启用插件,项目模块可依赖插件。类似地,通过在自身.uplugin文件中启用其他插件,插件可表明依赖性。但其中有一项重要限制:插件和模块将拆分为若干层级,仅能依赖同一级或更高级的其他插件或模块。例如,项目模块可依赖引擎模块,但引擎模块无法依赖项目模块。这是因为引擎(及其所有插件和模块)的级别高于项目,须能在无项目的情况下编译。以下图表展示了项目和模块间的依赖性层级:

    PluginAndModuleDependency.png

    箭头表明可能的依赖性。各插件或模块类型可依赖同级别或更高级别的其他插件或模块类型。

    引擎插件

    虚幻引擎4的 Engine 目录下包含部分内置插件。引擎插件和项目插件类似,但可用于所有项目。此类插件通常由引擎和工具程序员创建,目的在于提供可在多个项目中使用并能在单一位置维护的基线功能。利用此功能,用户可直接添加或覆盖引擎功能,而无需修改引擎代码。

    项目中的插件

    插件位于项目目录的 Plugins 子文件夹下,将在引擎或编辑器启动时被探测和加载。

    如插件包含具有 Source 文件夹(和 .Build.cs 文件)的模块,插件代码将被自动添加到生成的C++项目文件,以便在开发项目时开发插件。编译项目时,有可用源的插件都被作为游戏依赖项进行编译。

    项目生成器将忽略无 Source 文件夹的插件,其不会出现在C++项目文件中,但若存在二进制文件,启动时仍将加载此类插件。

    目前,无法将插件配置文件与项目打包。未来版本中可能会支持此功能,但目前需手动将此类文件复制到项目的 Config 文件夹。

    插件描述文件

    插件描述文件是命名以 .uplugin 结尾的文件。文件名的第一部分固定为插件命名。插件描述文件固定位于插件目录中,启动时将被引擎发现。

    插件描述文件使用Json(JavaScript对象表示法)文件格式。

    描述文件示例

    此范例描述文件来自引擎的 UObjectPlugin

    {
        "FileVersion" :3,
        "Version" :1,
        "VersionName" :"1.0",
        "FriendlyName" :"UObject Example Plugin",
        "Description" :"An example of a plugin which declares its own UObject type.This can be used as a starting point when creating your own plugin.",
        "Category" :"Examples",
        "CreatedBy" :"Epic Games, Inc.",
        "CreatedByURL" :"http://epicgames.com",
        "DocsURL" :"",
        "MarketplaceURL" :"",
        "SupportURL" :"",
        "EnabledByDefault" : true,
        "CanContainContent" : false,
        "IsBetaVersion" : false,
        "Installed" : false,
        "Modules" :
        [
            {
                "Name" :"UObjectPlugin",
                "Type" :"Developer",
                "LoadingPhase" :"Default"
            }
        ]
    }
    

    描述文件格式

    描述文件为JSON格式的变量列表,此类列表为 FPluginDescriptor 类型。其中具有一个附加字段"FileVersion",其是文件结构中唯一的必需字段。"FileVersion"提供插件描述文件的版本,通常应设为引擎支持的最高版本(当前为"3")。由于此版本应用于插件描述文件的格式,而非插件本身,因此其可能不会频繁变化,也不应随插件后续版本的发行而改变。要与引擎旧版本进行最大化兼容,可使用较旧版本号,但不建议进行此操作。

    欲了解其他支持字段的相关详情,参见FPluginDescriptor API参考页面。

    bEnabledByDefault
    

    插件入口

    添加按钮的方法

    首先找到设置“显示UI扩展点”便于我们看到我们添加按钮的地方

    给任意窗口的工具条、菜单条、菜单等添加按钮

    FUICommandList

    FUICommandInfo

    UI_COMMAND

    可以实现:扩展已有菜单栏、添加新菜单栏、扩展已有工具栏,无法实现添加新工具栏

    UI_COMMAND基础认知

    其实和UI_COMMAND相关模块有三个,分别是FUICommandList,FUICommandInfo以及UI_COMMAND宏,他们的关系是FUICommandList包含FUICommandInfo,并且映射FUICommandInfo到委托,UI_COMMAND宏负责正式注册FUICommandInfo。

    UToolMenus——新版本的方法

    先定位菜单类型通过UToolMenus::Get()->ExtendMenu获得一个UToolMenu对象指针,这个Menu可能是菜单条可能是工具条

    获得到Menu后通过 Menu->FindOrAddSection定位段落Section,最后对段落添加Menu来拓展编辑器,添加Menu可以选择AddMenuEntryWithCommandList(工具条就是AddEntry),也可以添加子菜单SubMenu

    工具栏

    //----------4.24版本的做法-------------
    	//拓展到已有工具条 位置 Setting
    	{
    	   UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar");
    	   {
    	      FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("Settings");
    	      {
    	         FToolMenuEntry& Entry = Section.
    	            AddEntry(FToolMenuEntry::InitToolBarButton(FFirstEditorToolBarButtonPluginCommands::Get().PluginAction));
    	         Entry.SetCommandList(PluginCommands);
    			//位置设置到段前
    	         FToolMenuEntry& Entry1 = Section.AddEntry(
    	          FToolMenuEntry::InitToolBarButton(FFirstEditorToolBarButtonPluginCommands::Get().PluginAction,
    	               TAttribute<FText>(),
    	               TAttribute<FText>(),
    	               TAttribute<FSlateIcon>(),
    	               NAME_None,
    	               TOptional<FName>("YourEntryName")));
    	         Entry1.SetCommandList(PluginCommands);
    	         Entry1.InsertPosition.Position = EToolMenuInsertType::First;
    	      }
    	   }
    	}
    	// //尝试添加到新工具条 位置 File 失败
    	// {
    	//    UToolMenu* ToolbarMenu = UToolMenus::Get()->RegisterMenu("LevelEditor.NewLevelToolBar");
    	//    {
    	//       FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("MyToolBarSection");
    	//       {
    	//          FToolMenuEntry& Entry = Section.
    	//                    AddEntry(FToolMenuEntry::InitToolBarButton(FFirstEditorToolBarButtonPluginCommands::Get().PluginAction));
    	//          Entry.SetCommandList(PluginCommands);
    	//
    	//       }
    	//    }
    	// }
    

    菜单栏

    //拓展到已有的菜单条
    {
       UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window");//扩展菜单
       {
          FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout");//找到一个段
          FToolMenuEntry& Entry =  Section.AddMenuEntryWithCommandList(FFirstEditorToolBarButtonPluginCommands::Get().PluginAction, PluginCommands);//用命令列表天啊及菜单入口
          Entry.InsertPosition.Position = EToolMenuInsertType::First;//设置插入方式       
       // Section.AddSubMenu("New Sub Menu", FText::FromString("???"), FText::FromString("???"),
       //                     FNewToolMenuChoice(
       //                         FNewMenuDelegate::CreateRaw(
       //                             this, &FOfficial_EditorToolbarButtonModule::FillSubmenu)));
       }
    }
    
    //添加一个新的菜单栏
    {
    
       UToolMenus* ToolMenus = UToolMenus::Get();
        UToolMenu* MyMenu = ToolMenus->RegisterMenu("LevelEditor.MainMenu.MySubMenu");//注册一个新入口
        FToolMenuSection& Section = MyMenu->AddSection("MyMenuSection");
        Section.AddMenuEntryWithCommandList(FFirstEditorToolBarButtonPluginCommands::Get().PluginAction,PluginCommands);
    
    }
    

    FExtender——经典方法

    分为几个步骤

    1、加载模块

    2、创建Extender对象

    3、给Extender对象添加扩展属性(在哪里扩展,绑定什么委托)、

    4、将Extender对象绑定到对应模块

    void FMyEditorToolbarButtonModule::RegisterMenus()
    {
        {//蓝图编辑器ToolBar
    		FBlueprintEditorModule& BlueprintEditorModule = FModuleManager::LoadModuleChecked<FBlueprintEditorModule>("Kismet");
    		BlueprintEditorModule.OnRegisterTabsForEditor().AddRaw(this, &FMyEditorToolbarButtonModule::OnBPToolBarRegister);
    	}
        
    	//SourceEngineSourceEditor
    	IAnimationBlueprintEditorModule& AnimationBlueprintEditorModule = FModuleManager::LoadModuleChecked<IAnimationBlueprintEditorModule>("AnimationBlueprintEditor");
    	{//动画蓝图拓展已有菜单栏
    		TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender);
    		MenuExtender->AddMenuExtension("HelpApplication", EExtensionHook::After, PluginCommands, FMenuExtensionDelegate::CreateRaw(this, &FMyEditorToolbarButtonModule::AddMenuExtension));
    		AnimationBlueprintEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
    	}
    
    	{//动画蓝图拓展添加新菜单栏
    		TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender);
    		MenuExtender->AddMenuBarExtension("Help", EExtensionHook::After, PluginCommands, FMenuBarExtensionDelegate::CreateRaw(this, &FMyEditorToolbarButtonModule::AddMenuBarExtension));
    		AnimationBlueprintEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
    	}
    
    	{//动画蓝图拓展添ToolBar
    		TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender);
    		MenuExtender->AddToolBarExtension("Settings", EExtensionHook::After, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FMyEditorToolbarButtonModule::AddToolBarExtension));
    		AnimationBlueprintEditorModule.GetToolBarExtensibilityManager()->AddExtender(MenuExtender);
    	}
    
        /*
    	If you try to do
    	FBlueprintEditorModule& BlueprintEditorModule = FModuleManager::LoadModuleChecked<FBlueprintEditorModule>(“Kismet”),
    	the code will compile, but the engine will crash when starting up.
    	One solution I found was to change LoadingPhase in .uplugin file to PostEngineInit.
    	*/
    	{
    		FBlueprintEditorModule& BlueprintEditorModule = FModuleManager::LoadModuleChecked<FBlueprintEditorModule>(TEXT("Kismet"));
    		TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender);
    		MenuExtender->AddMenuExtension("HelpApplication", EExtensionHook::After, PluginCommands,
    			FMenuExtensionDelegate::CreateRaw(this, &FMyEditorToolbarButtonModule::AddMenuExtension));
    		BlueprintEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
    	}
    }
    void FMyEditorToolbarButtonModule::AddMenuExtension(class FMenuBuilder& Builder)
    {
    	Builder.BeginSection(TEXT("MyButton"));
    	Builder.AddMenuEntry(FMyEditorToolbarButtonCommands::Get().PluginAction);
    	Builder.EndSection();
    }
    
    void FMyEditorToolbarButtonModule::AddMenuBarExtension(class FMenuBarBuilder& Builder)
    {
    	Builder.AddMenuEntry(FMyEditorToolbarButtonCommands::Get().PluginAction);
    }
    void FMyEditorToolbarButtonModule::AddToolBarExtension(class FToolBarBuilder& Builder)
    {
    	Builder.BeginSection(TEXT("MyButton"));
    	Builder.AddToolBarButton(FMyEditorToolbarButtonCommands::Get().PluginAction);
    	Builder.EndSection();
    }
    
    void FMyEditorToolbarButtonModule::OnBPToolBarRegister(FWorkflowAllowedTabSet& TabSet, FName Name, TSharedPtr<FBlueprintEditor> BP)
    {
    	TSharedPtr<FExtender> ToolBarExtender = MakeShareable(new FExtender);
    	ToolBarExtender->AddToolBarExtension("Settings", EExtensionHook::After, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FMyEditorToolbarButtonModule::AddToolBarExtension));
    	BP->AddToolbarExtender(ToolBarExtender);
    }
    

    一个模块有扩展器容器,可以容纳很多Extender

    扩展的位置取决于Extender想Add什么Extension,可以是ToolBar、MenuBar、Menu

    想改那个编辑器就取出那个编辑的ExtensibilityManager然后进行添加

    能够进行扩展的编辑器都继承了IHasMenuExtensibility、IHasToolBarExtensibility的接口!!

    class IAnimationBlueprintEditorModule : public IModuleInterface, public IHasMenuExtensibility, public IHasToolBarExtensibility
    {
        
    }
    

    特殊的:蓝图的编辑器不能扩展ToolBar方法特殊,如果插件要动到蓝图编辑器,我们需要将uplugin中的LoadingPhase设置到PostEngineInit在引擎初始化之后启动

    class FBlueprintEditorModule : public IModuleInterface,
    	public IHasMenuExtensibility
    {
    ...
    }
    

    插件面板

    编辑方法

    直接使用Slate做

    用Slate硬写,大体上生成的UI树长这样,重要的是要绑定委托,MVC框架下,VC代码写一起

    FGlobalTabmanager::Get()->RegisterNomadTabSpawner(MyTab1Name,
    			FOnSpawnTab::CreateLambda([](const FSpawnTabArgs& SpawnTabArgs) {
    			TSharedRef<SSlider> MySlider = SNew(SSlider).Value(0.2f);
    			return SNew(SDockTab)
    				.TabRole(ETabRole::NomadTab)
    				[
    					// Put your tab content here!
    					SNew(SVerticalBox)
    					+ SVerticalBox::Slot()
    					[
    						MySlider
    					]
    					+ SVerticalBox::Slot()
    					.HAlign(HAlign_Center)
    					.VAlign(VAlign_Center)
    					[
    						SNew(STextBlock)
    						.Text_Lambda(
    							[MySlider]() {
    								int32 Result = (int32)(MySlider->GetValue() * 100);
    								return FText::FromString(FString::FromInt(Result));
    							}
    						)
    					]
    				];
    			}))
    			.SetDisplayName(LOCTEXT("MyTab1TabTitle", "MyTab1"))
    			.SetMenuType(ETabSpawnerMenuType::Disabled);
    
    Slate语法

    SNew创建对象

    SAssignNew创建对象并赋值

    使用设计器

    局限性很大 UMG类继承Slate类,很多Slate类都没有对应的U类

    1、首先设计器必须是UMG类的内容

    2、然后他好像只能在关卡编辑器下起作用

    自定义一个SWidget类

    继承SUserWidget类,在引擎SUserWidget.cpp文件中有对应的教程

    /**
     * Use SUserWidget as a base class to build aggregate widgets that are not meant
     * to serve as low-level building blocks. Examples include: a main menu, a user card,
     * an info dialog for a selected object, a splash screen.
     *
     * See SUserWidgetExample
     *
     * SMyWidget.h
     * -----------
     * class SMyWidget : public SUserWidget
     * { 
     *   public:
     *   SLATE_USER_ARGS( SMyWidget )
     *  {}
     *   SLATE_END_ARGS()
     *
     *   // MUST Provide this function for SNew to call!
     *   virtual void Construct( const FArguments& InArgs ) = 0;
     *
     *   virtual void DoSomething() = 0;
     * };
     *
     * SMyWidget.cpp
     * -------------
     * namespace Implementation 
     * {
     *   class SMyWidget : public ::SMyWidget
     *   {
     *     public:
     *     virtual void Construct( const FArguments& InArgs ) override
     *     {
     *        SUserWidget::Construct( SUserWidget::FArguments()
     *        [
     *           SNew(STextBlock)
     *           .Text( NSLOCTEXT("x", "x", "My Widget's Content") )
     *        ]
     *     }
     *     
     *     private:
     *     // Private implementation details can occur here
     *     // without ever leaking out into the .h file!
     *   }
     * }
     *
     * TSharedRef<SMyWidget> SMyWidget::New()
     * {
     *   return MakeShareable( new SMyWidget() );
     * }
     */
    class SUserWidget : public SCompoundWidget
    {
     ...
    }
    

    官方也给了一个Example的类

    自定义资源

    创建一个自己的资源类型,一般需要两个模块,包括资源描述、资源编辑器(右键菜单要出现、特殊的编辑器等等)

    plugin中分模块

    依赖的模块

    Editor依赖的较多,UnrealEd模块管理资源创建面板,AssetTools管理资源操作 、PropertyEditor管理属性显示,以及上面提到的资源模块

    PrivateDependencyModuleNames.AddRange(
       new string[]
       {
          "CoreUObject",
          "Engine",
          "Slate",
          "SlateCore",
          "UnrealEd",
          "NewAsset",
          "AssetTools",
          "PropertyEditor",
          // ... add private dependencies that you statically link with here ... 
       }
    );
    

    资源模块一般是私有的,Editor模块的build的include路径应该增加一下

    toolkit 在引擎内部 等于 editor

    引擎默认会给一个资源编辑界面

    创建新资源

    需要继承一个UFactory类,不需要绑定

    /**
     * 从4.25.1版开始,若想新资源类型出现在内容浏览器的右键创建菜单中,必须为它注册IAssetTypeActions对象,具体见以下修改
     * https://github.com/EpicGames/UnrealEngine/commit/e8a2922b50b7659edabb9b0779ed9bfc7e593009
     */
    UCLASS()
    class UNewAssetFactoryNew : public UFactory
    {
       GENERATED_UCLASS_BODY()
    public:
       virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
       virtual bool ShouldShowInNewMenu() const override;
    };
    
    新版本

    新版本的操作不太一样,需要额外实现一个资源操作类

    #include "AssetTypeActions_Base.h"
    
    class FNewAssetAction :public FAssetTypeActions_Base
    {
    public:
       virtual uint32 GetCategories() override;
       virtual FText GetName() const override;
       virtual UClass* GetSupportedClass() const override;
       virtual FColor GetTypeColor() const override;
       virtual void OpenAssetEditor(const TArray<UObject*>& InObjects, TSharedPtr<IToolkitHost> EditWithinLevelEditor = TSharedPtr<IToolkitHost>()) override;
       virtual bool HasActions(const TArray< UObject* >& InObjects) const override;
       virtual void GetActions(const TArray< UObject* >& InObjects, FMenuBuilder& MenuBuilder) override;
    };
    

    再在进入模块时进行注册

    		{
    			IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
    			Action = MakeShareable(new FNewAssetAction());
    			AssetTools.RegisterAssetTypeActions(Action.ToSharedRef());
    		}
    

    关闭的时候也要处理一下

    {
    			FAssetToolsModule* Module = FModuleManager::GetModulePtr<FAssetToolsModule>("AssetTools");
    			if (Module) {
    				IAssetTools& AssetTools = Module->Get();
    				if (Action.IsValid()) {
    					AssetTools.UnregisterAssetTypeActions(Action.ToSharedRef());
    				}
    			}
    		}
    

    导入资源

    工厂继承FReimportHandler类

    构造函数需要继承文件的后缀,编辑器看到某个后缀的文件就将其使用该工厂进行导入

    导入函数FactoryCreateFile的实现

    UObject* UNewAssetFactory::FactoryCreateFile(UClass* InClass, UObject* InParent,
    	FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms,
    	FFeedbackContext* Warn, bool& bOutOperationCanceled)
    {
    	UNewAsset* NewAsset = nullptr;
    	TArray<uint8> Bytes;
    
    	if (FFileHelper::LoadFileToArray(Bytes, *Filename) && Bytes.Num() >= sizeof(int32))
    	{
    		NewAsset = NewObject<UNewAsset>(InParent, InClass, InName, Flags);
    		for (uint32 i = 0; i < sizeof(int32); ++i) {
    			NewAsset->IntValue |= Bytes[i] << (i * 8);
    		}
    	}
    
    	bOutOperationCanceled = false;
    
    	return NewAsset;
    }
    
    

    自定义资源编辑器

    在NewAssetAction类中继续重写一个OpenAssetEditor,不重写他会用默认的给我们

    然后在这个Open函数里

    void FNewAssetAction::OpenAssetEditor(const TArray<UObject*>& InObjects, TSharedPtr<IToolkitHost> EditWithinLevelEditor)
    {
        EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone;//套路
    
    	for (auto Obj = InObjects.CreateConstIterator(); Obj; ++Obj)
    	{
    		auto NewAsset = Cast<UNewAsset>(*Obj);
    
    		if (NewAsset)
    		{
    			TSharedRef<FNewAssetToolkit> EditorToolkit = MakeShareable(new FNewAssetToolkit());
    			EditorToolkit->Initialize(NewAsset, Mode, EditWithinLevelEditor);
    		}
    	}
    }
    

    这里涉及到一个新类,ToolKit,其实就是和Editor一样的意思

    把资源传到我们的自定义一个编辑器里

    toolkit
    #include "Toolkits/AssetEditorToolkit.h"
    class FNewAssetToolkit : public FAssetEditorToolkit
    {
    public:
       virtual void RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager) override;
       virtual void UnregisterTabSpawners(const TSharedRef<FTabManager>& InTabManager) override;
       void Initialize(class UNewAsset* InNewAsset, const EToolkitMode::Type InMode, const TSharedPtr<IToolkitHost>& InToolkitHost);
       virtual FText GetBaseToolkitName() const override;
       virtual FName GetToolkitFName() const override;
       virtual FLinearColor GetWorldCentricTabColorScale() const override;
       virtual FString GetWorldCentricTabPrefix() const override;
    
    private:
       class UNewAsset* NewAsset;
       TSharedPtr<STextBlock> TextBlock;
    
       TSharedPtr<class IDetailsView> DetailsView;
    };
    

    资源编辑后需要NewAsset->MarkPackageDirty();来让编辑器知道他需要保存

    自定义细节面板

    没有做操作就会给一个默认的细节面板

    写一个自己的类的Detail对应类,继承IDetailCustomization接口,然后这个类的特殊显示属性继承IPropertyTypeCustomization接口

    #include "CoreMinimal.h"
    #include "IDetailCustomization.h"
    #include "IPropertyTypeCustomization.h"
    
    class FNewAssetDetailCustomization : public IDetailCustomization
    {
    public:
       virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
    };
    
    
    

    在Module启动位置,给FPropertyEditorModule注册属性编辑器类和detail类的关系

    RegisterCustomPropertyTypeLayout
    RegisterCustomClassLayout
    
    FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
    PropertyEditorModule.RegisterCustomPropertyTypeLayout("StructMember", FOnGetPropertyTypeCustomizationInstance::CreateLambda(
       [] {return MakeShareable(new FNewAssetMemberCustomization); }
    ));
    PropertyEditorModule.RegisterCustomPropertyTypeLayout("ClassMember", FOnGetPropertyTypeCustomizationInstance::CreateLambda(
       [] {return MakeShareable(new FNewAssetMemberCustomization); }
    ));
    PropertyEditorModule.RegisterCustomClassLayout("NewAsset", FOnGetDetailCustomizationInstance::CreateLambda(
       [] {return MakeShareable(new FNewAssetDetailCustomization); }
    ));
    PropertyEditorModule.NotifyCustomizationModuleChanged();
    

    类的属性特定显示

    class FNewAssetMemberCustomization : public IPropertyTypeCustomization
    {
    public:
       virtual void CustomizeHeader(
          TSharedRef<IPropertyHandle> InPropertyHandle,
          FDetailWidgetRow& HeaderRow,
          IPropertyTypeCustomizationUtils& CustomizationUtils) override;
    
       virtual void CustomizeChildren(
          TSharedRef<IPropertyHandle> InPropertyHandle,
          IDetailChildrenBuilder& ChildBuilder,
          IPropertyTypeCustomizationUtils& CustomizationUtils) override;
    
    private:
       TSharedPtr<class STextBlock> TextBlock_MemberValue;
    };
    

    貌似是Header里负责填写改变属性row的样式,而children函数负责处理property数据

    待补充

    参考

    https://zhuanlan.zhihu.com/p/338067229

    https://www.bilibili.com/video/BV1tv41117qg

    https://docs.unrealengine.com/4.26/zh-CN/ProductionPipelines/Plugins/

    https://www.unrealengine.com/zh-CN/onlinelearning-courses/best-practices-for-creating-and-using-plugins?sessionInvalidated=true

  • 相关阅读:
    easyui
    mvc
    Servlet简单计算器 2.0
    简易Servlet计算器1.0
    javaBean 练习—封装学生信息
    application和javaBean练习
    远程存储程序
    通讯录
    黑名单管理代码总结
    DAO
  • 原文地址:https://www.cnblogs.com/FlyingZiming/p/15036560.html
Copyright © 2011-2022 走看看