【转载】 [UE4]扩展 AssetContextMenu(插件)

686 阅读3分钟

原文链接

[UE4] Extend AssetContextMenu(Plugin)| 凡尘旧事

正文

新建一个 Plugin,这次我们选择 EditorToolbarButton 模板。

.uplugin 修改 LoadingPhasePostEngineInit

这次制作两个小功能,一修改 StaticMeshCollision Complexity,二设置 StaticMeshDistanceFieldScale,插件的名字就叫 MeshTool

网上用 FExtender 的方法比较常见,我就介绍一下用 UToolMenu 进行扩展的方法。

EditorPreferences->Miscellaneous,勾选 Display UI Extension Points,开启 UI 扩展点。

先实现第一个功能:

RegisterMenus() 中添加扩展按钮:

UToolMenu* AssetContextMenu = UToolMenus::Get()->ExtendMenu("ContentBrowser.AssetContextMenu");
{
	FToolMenuSection& Section = AssetContextMenu->FindOrAddSection("MeshTool");
	{
		Section.AddSubMenu("MeshTool",
			LOCTEXT("MeshTool", "MESH TOOL"),
			LOCTEXT("setMeshProperty", "Set Mesh Property"),
			FNewToolMenuChoice(FNewToolMenuDelegate::CreateRaw(this, &FMeshToolModule::FillSubMenu))
		);
	}
}

添加 FillSubMenu 函数:

void FMeshToolModule::FillSubMenu(UToolMenu* InMenu)
{
	FToolMenuSection& Section = InMenu->FindOrAddSection("SubSection");
	Section.AddMenuEntryWithCommandList(FMeshToolCommands::Get().PluginAction, PluginCommands);	
}

要进行扩展的 Menu 的 Name 是:ContentBrowser.AssetContextMenu

通过 AddSubMenu 添加子菜单:

FToolMenuEntry& FToolMenuSection::AddSubMenu(const FName InName, const TAttribute<FText>& InLabel, const TAttribute<FText>& InToolTip, const FNewToolMenuChoice& InMakeMenu, bool bInOpenSubMenuOnClick, const TAttribute<FSlateIcon>& InIcon, const bool bShouldCloseWindowAfterMenuSelection)
{
	return AddEntry(FToolMenuEntry::InitSubMenu(InName, InLabel, InToolTip, InMakeMenu, bInOpenSubMenuOnClick, InIcon, bShouldCloseWindowAfterMenuSelection));
}

InMakeMenu 就是我们添加的 FillSubMenu 方法。

编译代码,在 ContentBrowser 中对某个物体右键,就可以看到上图所示的菜单。

接下来功能上,在 PluginButtonClicked() 函数中添加:

#include "EditorUtilityLibrary.h"
...
...
void FMeshToolModule::PluginButtonClicked()
{
	// Put your "OnButtonClicked" stuff here
	TArray<UObject*> SelectedObjects = UEditorUtilityLibrary::GetSelectedAssets();

	for (UObject* obj : SelectedObjects)
	{
		FString ObjectName = obj->GetName();
		UE_LOG(LogTemp, Warning, TEXT("%s"), *ObjectName);

		UStaticMesh* CurrentMesh;
		CurrentMesh = Cast<UStaticMesh>(obj);

		if (CurrentMesh)
		{
			CurrentMesh->BodySetup->CollisionTraceFlag = ECollisionTraceFlag::CTF_UseComplexAsSimple;
			CurrentMesh->Build();
			CurrentMesh->PostEditChange();
			CurrentMesh->MarkPackageDirty();
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("Please select StaticMehs"));
		}
	}
}

不要忘了在 Bulid.cs 中添加模块 “Blutility”。

引入 EditorUtilityLibrary.h 时,可能会提示无法打开源文件,忽略就好,编译可以通过。

修改完 StaticMesh 后需要标记为 Dirty ,不然数据不会保存。

OK,第一个功能就实现完了。

扩展:

通过 Python 脚本实现,在使用起来没有扩展菜单那么舒服,但是写起来快

import unreal

def main():
    sel = unreal.EditorUtilityLibrary.get_selected_assets()
    for i in sel:
        print (i)
        body_setup = i.get_editor_property('body_setup')
        collision_trace_flag = unreal.CollisionTraceFlag.CTF_USE_COMPLEX_AS_SIMPLE
        body_setup.set_editor_property('collision_trace_flag', collision_trace_flag)
        i.set_editor_property('body_setup', body_setup)

其实官方提供了一个修改 Property 的编辑器,选中一个或一些 asset,右键 AssetAction 中有个 Bulk Edit via Property Matrix...

通过这个编辑器可以方便的进行大量 Asset Property 的修改。

接下来我们注册第二个按钮

在头文件中添加 Function SetDFScale(),放着先不管它

在 Command 的头文件中,再声明一个用于动作映射中绑定委托的 CommandID,就叫 DFScaleAction

public:
	TSharedPtr< FUICommandInfo > PluginAction;
	TSharedPtr< FUICommandInfo > DFScaleAction;

相应的在 cpp 中:

void FMeshToolCommands::RegisterCommands()
{
	UI_COMMAND(PluginAction, "ComplexCollision", "Use Complex Collision", EUserInterfaceActionType::Button, FInputGesture());
	UI_COMMAND(DFScaleAction, "DistanceFieldScale", "Set Distance Field Scale", EUserInterfaceActionType::Button, FInputGesture());
}

回到 MeshTool.cpp 完善动作映射:

void FMeshToolModule::StartupModule()
{
	// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
	
	FMeshToolStyle::Initialize();
	FMeshToolStyle::ReloadTextures();

	FMeshToolCommands::Register();
	
	PluginCommands = MakeShareable(new FUICommandList);

	PluginCommands->MapAction(
		FMeshToolCommands::Get().PluginAction,
		FExecuteAction::CreateRaw(this, &FMeshToolModule::PluginButtonClicked),
		FCanExecuteAction());

	PluginCommands->MapAction(
		FMeshToolCommands::Get().DFScaleAction,
		FExecuteAction::CreateRaw(this, &FMeshToolModule::SetDFScale),
		FCanExecuteAction()
	);

	UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FMeshToolModule::RegisterMenus));
}

在 Func FillSubMenu 中注册这个按钮:

void FMeshToolModule::FillSubMenu(UToolMenu* InMenu)
{
	FToolMenuSection& Section = InMenu->FindOrAddSection("SubSection");
	Section.AddMenuEntryWithCommandList(FMeshToolCommands::Get().PluginAction, PluginCommands);	
	Section.AddMenuEntryWithCommandList(FMeshToolCommands::Get().DFScaleAction, PluginCommands);
}

编译,右键 asset,就能看到两个按钮了,且都绑定了各自的 Action

void FMeshToolModule::SetDFScale()
{
	TArray<UObject*> SelectedObjects = UEditorUtilityLibrary::GetSelectedAssets();

	for (UObject* obj : SelectedObjects)
	{
		FString ObjectName = obj->GetName();
		UE_LOG(LogTemp, Warning, TEXT("%s"), *ObjectName);

		UStaticMesh* CurrentMesh;
		CurrentMesh = Cast<UStaticMesh>(obj);

		if (CurrentMesh)
		{
			for (int32 i = 0; i < CurrentMesh->SourceModels.Num(); i++)
			{
				FStaticMeshSourceModel& Model = CurrentMesh->SourceModels[i];
				Model.BuildSettings.DistanceFieldResolutionScale = 2.0;
			}
			CurrentMesh->Build();
			CurrentMesh->PostEditChange();
			CurrentMesh->MarkPackageDirty();
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("Please select StaticMehs"));
		}

	}
}

Done!

Ref:

  1. 【合集】UE4插件与Slate_哔哩哔哩_bilibili​

  2. Michaela-羊羊羊:UE4 同时修改2400个照片的同一个属性1 赞同 · 0 评论文章

  3. YivanLee:虚幻4渲染编程(工具篇)【第八卷:Asset creation】26 赞同 · 9 评论文章

  4. UE4插件开发总结​

  5. LokiSparken/Computer-Graphics-Notes​