|
首先,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")); } }} |
|