APSchmidt 发表于 2022-7-19 10:25

unreal自定义蓝图节点1

前提配置

因为蓝图的节点是仅仅在Editor中使用的,并不需要打包的,为了和项目的代码做好区分处理,所以我使用了module的形式去处理!
module创建
现在是我们希望在Editor的模块下创建自定义的蓝图节点,该节点调用的是运行时的节点! 我们需要在module声明中将该模块改为UncookedOnly(Editor也可以!)
"Modules": [
      ...
      {
            "Name": "MyEditorTools",
            "Type": "UncookedOnly",
            "LoadingPhase": "Default",
            "AdditionalDependencies": [
                "UnrealGameDemo"
            ]
      }
    ]在 MyEditorTools.Build.cs 中添加一些必须的依赖,要不然在后续的使用和编译中会有报错!

[*]KismetCompiler
无法解析的外部符号 "declspec(dllimport) public: struct FPinConnectionResponse cdecl FKismetCompilerContext::MovePinLinksToIntermediate(class UEdGraphPin &,class UEdGraphPin &)" (imp_?MovePinLinksToIntermediate@FKismetCompilerContext@@QEAA?AUFPinConnectionResponse@@AEAVUEdGraphPin@@0@Z),函数 "public: virtual void cdecl UCustomNode::ExpandNode(class FKismetCompilerContext &,class UEdGraph *)" (?ExpandNode@UCustomNode@@UEAAXAEAVFKismetCompilerContext@@PEAVUEdGraph@@@Z) 中引用了该符号

[*]BlueprintGraph
无法解析的外部符号 "declspec(dllimport) public: cdecl FBlueprintNodeSignature::FBlueprintNodeSignature(class TSubclassOf)" (imp_??0FBlueprintNodeSignature@@QEAA@V?$TSubclassOf@VUEdGraphNode@@@@@Z),函数 "public: virtual struct FBlueprintNodeSignature cdecl UK2Node::GetSignature(void)const " (?GetSignature@UK2Node@@UEBA?AUFBlueprintNodeSignature@@XZ) 中引用了该符号

[*]UnrealEd
无法解析的外部符号 "declspec(dllimport) public: void cdecl FCompilerResultsLog::NotifyIntermediateObjectCreation(class UObject ,class UObject )" (imp_?NotifyIntermediateObjectCreation@FCompilerResultsLog@@QEAAXPEAVUObject@@0@Z),函数 "public: virtual void cdecl UCustomNode::ExpandNode(class FKismetCompilerContext &,class UEdGraph *)" (?ExpandNode@UCustomNode@@UEAAXAEAVFKismetCompilerContext@@PEAVUEdGraph@@@Z) 中引用了该符号PrivateDependencyModuleNames.AddRange(new string[]{"KismetCompiler", "BlueprintGraph", "UnrealEd"});
具体的蓝图节点实现


[*]直接使用UFUNCTION
[*]自定义蓝图节点
直接使用UFUNCTION

UCLASS()
class UNREALGAMEDEMO_API UCustomRunningNode : public UObject
{
public:
    GENERATED_BODY()
    UFUNCTION(BlueprintCallable, Category="Custom|CustomRunningNode", meta=(ToolTip="this is a tooltip"))
    static void TestBlueprintNode1(FString arg1)
    {
      UE_LOG(LogUnrealGameDemo, Display, TEXT("UCustomRunningNode::TestBlueprintNode1"));
    }
};






自定义蓝图节点

这里使用的是继承UK2Node实现的! 在MyEditorTools的模块中添加节点类,并继承UK2Node
#pragma once
#include "K2Node.h"
#include "CustomNode.generated.h"

UCLASS()
class UCustomNode : public UK2Node
{
public:
    GENERATED_BODY()
    virtual FText GetTooltipText() const override {return FText::FromString(TEXT("This is a test case"));};
    virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override {returnFText::FromString(TEXT("Custom Node"));};
    virtual FText GetMenuCategory() const override{return FText::FromString(TEXT("Custom|CustomNode"));};
    virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;

    virtual void ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override;
    virtual void AllocateDefaultPins() override;
    UEdGraphPin* GetThenPin() const;
};
cpp
#include "CustomNode.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "BlueprintNodeSpawner.h"
#include "EdGraphSchema_K2.h"
#include "K2Node_CallFunction.h"
#include "KismetCompiler.h"

void UCustomNode::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
    Super::GetMenuActions(ActionRegistrar);
    auto Classkey = GetClass();
    if (ActionRegistrar.IsOpenForRegistration(Classkey))
    {
      auto nodeSpawner = UBlueprintNodeSpawner::Create(Classkey);
      check(nodeSpawner!= nullptr);
      ActionRegistrar.AddBlueprintAction(nodeSpawner);
    }
}

void UCustomNode::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
    Super::ExpandNode(CompilerContext, SourceGraph);
}

void UCustomNode::AllocateDefaultPins()
{
    Super::AllocateDefaultPins();
}

UEdGraphPin* UCustomNode::GetThenPin() const
{
    UEdGraphPin* Pin = FindPin(UEdGraphSchema_K2::PN_Then);
    check(Pin == nullptr || Pin->Direction == EGPD_Output); // If pin exists, it must be input
    return Pin;
}
这个时候编译完成后,我们就能在蓝图里看到新加的节点了!





GetTooltipText方法是鼠标浮在节点上的提示!
GetNodeTitle是节点的名称!
GetMenuCategory是节点的归类目录
GetMenuActions是将节点注册到蓝图的右键菜单中!
注意:如果是plugin或者是独立的module的话,想要生效,需要重新reload或者在ue中compiler!


蓝图节点在构建展示的时候,会默认调用AllocateDefaultPins,所以我们如果想要想其他默认节点那样能接入到流程中,需要在AllocateDefaultPins中添加一些代码
void UCustomNode::AllocateDefaultPins()
{
    CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
    CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
    Super::AllocateDefaultPins();
}
使用CreatePin去创建流入和流出的pin,需要注意的是,这里的第三个参数是name,但是有不全是!最好是先默认UEdGraphSchema_K2::PN_Execute和UEdGraphSchema_K2::PN_Then;
你可以试试换成中文;eg: TEXT("输入"),TEXT("输出");看看能不能调用和debug!!


试试连接起来编译看看



原因是该节点没有执行的函数!需要在ExpandNode中绑定!
void UCustomNode::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
    Super::ExpandNode(CompilerContext, SourceGraph);
    // 获取流入的pin
    auto execPin = GetExecPin();
    // 获取流出的pin
    auto thenPin = GetThenPin();
    if (execPin && thenPin)
    {
      // 获取要执行的方法名,需要注意,一定要有UFUNCTION,
      auto functionName = GET_FUNCTION_NAME_CHECKED(UCustomRunningNode, TestBlueprintNode2);
      // 构建要执行的方法节点!
      auto callFunction = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
      // 设置并绑定执行的方法体
      callFunction->FunctionReference.SetExternalMember(functionName, UCustomRunningNode::StaticClass());
      callFunction->AllocateDefaultPins();

      // 流程转移!
      CompilerContext.MovePinLinksToIntermediate(*execPin, *(callFunction->GetExecPin()));
      CompilerContext.MovePinLinksToIntermediate(*thenPin, *(callFunction->GetThenPin()));
    }
    // 断开所有的link
    BreakAllNodeLinks();
}
这里的UCustomRunningNode是在项目运行时的module中的
UCLASS()
class UNREALGAMEDEMO_API UCustomRunningNode : public UObject
{
public:
    GENERATED_BODY()
    UFUNCTION(BlueprintCallable)
    static void TestBlueprintNode2()
    {
      UE_LOG(LogUnrealGameDemo, Display, TEXT("UCustomRunningNode::TestBlueprintNode2"));
    }
};
执行编译和reload后,一切正常了!debug和执行都没有问题了!
补充


[*]PinDefaultValueChanged
当蓝图的节点的内容发生改变的时候,此函数是回调!

[*]CreatePin
该节点支持很多种类型的内容
Bool,
Byte,
Class,
SoftClass,
Int,
Int64,
Float,
Name,
Delegate,
Object,
Interface,
SoftObject,
String,
Text,
Struct,
Enumeg : CreatePin(EGPD_Input, TEXT("Struct"), TEXT("AName"));
页: [1]
查看完整版本: unreal自定义蓝图节点1