閱讀此文章之前捞高,建議先看下入門版:http://www.reibang.com/p/ec0ae889f417
首先跟衅,UE本身提供了很多拓展接口攀芯,基本可以滿足日常插件開發(fā)的需要蒸殿,我舉些例子:
- LevelEditorModule.GetToolBarExtensibilityManager()可以拓展關(guān)卡編輯器的工具欄,GetMenuExtensibilityManager()可以拓展菜單欄
- GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OnAssetOpenedInEditor(),可以監(jiān)聽UE中终畅,任意編輯器打開的事件籍胯。
- FCoreUObjectDelegates::OnObjectPropertyChanged可以監(jiān)聽任意Object,任意屬性的變化事件离福。
UE中類似上述三個示例的接口還有茫茫多杖狼,歡迎大家繼續(xù)自行探索,不再贅述妖爷。
但是蝶涩,上述這些正道接口,也只能覆蓋90%的開發(fā)需求絮识。那么剩下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函數(shù),你都可以直接調(diào)用了国拇。
其他函數(shù)可能會報鏈接錯誤洛史。咋辦呢?
很簡單酱吝,把這個函數(shù)的實現(xiàn)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計算正確捆毫,這個太黑,就不提了冲甘。
技巧三: 實時替換虛函數(shù)
我業(yè)余時間绩卤,寫了個運行時,替換virtual函數(shù)的程序江醇,鏈接在此:https://github.com/xiaoyaoliu/vimrc/tree/master/documents/cpp_samples/ReplaceVirtualFunction
例如濒憋,在動畫藍圖中的Slot動畫節(jié)點的右鍵菜單中添加新的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);
}
// 函數(shù)聲明: HookAnimBlueprintEditor.h
struct FHookAnimBlueprintEditor
{
void GetNodeContextMenuActions_AnimNodeSlot(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context);
};
// 函數(shù)實現(xiàn): HookAnimBlueprintEditor.cpp
void FHookAnimBlueprintEditor::GetNodeContextMenuActions_AnimNodeSlot(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context)
{
UAnimGraphNode_Slot* slot = (UAnimGraphNode_Slot*)(this);
// 調(diào)用下被替換掉的函數(shù)凛驮。從而保證只增加,不刪除原有邏輯律适。
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"));
}
}
}