原文链接
正文
新建一个 Plugin,这次我们选择 EditorToolbarButton 模板。
.uplugin 修改 LoadingPhase 为 PostEngineInit
这次制作两个小功能,一修改 StaticMesh 的 Collision Complexity,二设置 StaticMesh 的 DistanceFieldScale,插件的名字就叫 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!