RedZero9 发表于 2021-9-26 18:48

Unreal插件开发的一些技巧

首先,UE本身提供了很多拓展接口,基本可以满足日常插件开发的需要,我举些例子:
LevelEditorModule.GetToolBarExtensibilityManager()可以拓展关卡编辑器的工具栏,GetMenuExtensibilityManager()可以拓展菜单栏GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OnAssetEditorOpened(),可以监听UE中,任意编辑器打开的事件。
[*]FCoreUObjectDelegates::OnObjectPropertyChanged可以监听任意Object,任意属性的变化事件。
UE中类似上述三个示例的接口还有茫茫多,欢迎大家继续自行探索,不再赘述。

但是,上述这些正道接口,也只能覆盖90%的开发需求。那么剩下10%如何处理呢?
技巧一: 插件中include UE的Private头文件


public目录中头文件引用方法太入门,本文不再赘述。

private目录中的头文件引用方法,例如:Unreal\Engine\Source\Editor\AnimationBlueprintEditor\Private\AnimationBlueprintEditor.h

首先,在当前插件模块的build.cs文件中添加Private目录:
var EngineDir = Path.GetFullPath(Target.RelativeEnginePath);PrivateIncludePaths.AddRange(    new string[] {      Path.Combine(EngineDir, "Source/Editor/AnimationBlueprintEditor/Private/"),   });
然后在某个源码文件,直接include:
// 用尖括号也可以#include "AnimationBlueprintEditor.h"
使用private头文件后,virtual或inline函数,你都可以直接调用了。

其他函数可能会报链接错误。咋办呢?
很简单,把这个函数的实现copy到你的cpp里即可。
技巧二:访问private或protected变量的官方办法。


UE官方其实提供了一种访问private变量的方法:所有标记了UPROPERTY的变量都是可以通过正道访问的。
例如, FAnimLinkableElement的SlotIndex变量:
USTRUCT()struct FAnimLinkableElement{    GENERATED_USTRUCT_BODY()    protected:    /** The slot index we are currently using within LinkedMontage */    UPROPERTY(EditAnywhere, Category=AnimLink)    int32 SlotIndex;};
访问SlotIndex的方法如下:
int32* GetSlotIndex(void *pAnimLinkableElement) {    static UScriptStruct* Struct = FindObjectSafe<UScriptStruct>(ANY_PACKAGE, TEXT("AnimLinkableElement"));    auto Prop = Struct->FindPropertyByName(TEXT("SlotIndex"));    check(Prop);    return Prop->ContainerPtrToValuePtr<int32>(pAnimLinkableElement);}
另外,不标记UPROPERTY的私有变量,其实也可以访问的,前提是变量的offset计算正确,这个太黑,就不提了。
技巧三: 实时替换虚函数


我业余时间,写了个运行时,替换virtual函数的程序,链接在此:https://github.com/xiaoyaoliu/vimrc/tree/master/documents/cpp_samples/ReplaceVirtualFunction

例如,在动画蓝图中的Slot动画节点的右键菜单中添加新的UI入口。
    #include <ReplaceVirtualFunction/ReplaceVirtualFunction.h>    // 开始替换    {    // Register command list      auto obj = UAnimGraphNode_Slot::StaticClass()->GetDefaultObject();      // Add context menu for UAnimGraphNode_Slot. 用插件中的GetNodeContextMenuActions_AnimNodeSlot方法,替换unreal的UAnimGraphNode_Slot::GetNodeContextMenuActions      ReplaceVirtualWithNonVirtualMember(obj, UAnimGraphNode_Slot::GetNodeContextMenuActions, FHookAnimBlueprintEditor::GetNodeContextMenuActions_AnimNodeSlot);    }      // 取消替换    {      RecoverReplace(FHookAnimBlueprintEditor::GetNodeContextMenuActions_AnimNodeSlot);    }// 函数声明: HookAnimBlueprintEditor.hstruct FHookAnimBlueprintEditor{void GetNodeContextMenuActions_AnimNodeSlot(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context);};    // 函数实现: HookAnimBlueprintEditor.cppvoid FHookAnimBlueprintEditor::GetNodeContextMenuActions_AnimNodeSlot(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context){    UAnimGraphNode_Slot* slot = (UAnimGraphNode_Slot*)(this);    // 调用下被替换掉的函数。从而保证只增加,不删除原有逻辑。    SuperVirtualCall(slot->GetNodeContextMenuActions(Menu, Context));    if (!Context->bIsDebugging)    {      // add an option to Play Animation Asset In Slot         {            FToolMenuSection& Section = Menu->AddSection("AnimGraphNodeSlot", LOCTEXT("SlotHeading", "Slot"));                }    }}
页: [1]
查看完整版本: Unreal插件开发的一些技巧