ue4 自定义节点

1.按照前面的教程创建模块,将我们的自定义节点类 这里注意 需要在.build.cs 添加KismetCompiler 模块

PrivateDependencyModuleNames.AddRange(new string[] { "UnrealEd", "KismetCompiler" });

2.定义功能函数 我这里是创建一个object

.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "TestItemObject.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "MyBlueprintFunctionLibrary.generated.h"

/**
 * 
 */
UCLASS()
class MYNODE_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
public:
	UFUNCTION(BlueprintCallable,  Category = Test)
		static UTestItemObject* CreateItemObject(TSubclassOf<UTestItemObject> ItemClass);
	
};



.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "MyBlueprintFunctionLibrary.h"
#include "TestItemObject.h"

UTestItemObject* UMyBlueprintFunctionLibrary::CreateItemObject(TSubclassOf<UTestItemObject> ItemClass)
{
	return NewObject<UTestItemObject>();
}

3..添加TestNode类 继承自UK2Node_ConstructObjectFromClass

具体的注释在代码里 这里主要需要重载的是AllocateDefaultPins和 ExpandNode函数

AllocateDefaultPins是主要用来创建你自定义的pin口 根据你的功能 可以自定义接口类型。

ExpandNode就是实现功能

主要过程说一下 就是首先 你可以获得你当前node 输入 输入节点对应的pin

然后将对应pin根据需要传递到你需要使用的函数

比如这里我的函数CreateItemObject 需要ItemClass这个参数,那么我就需要将node中的class pin口的值传递到函数绑定的node几点中

    //连接class pin
    UEdGraphPin* CallCreateClass = CallCreateNode->FindPin(TEXT("ItemClass"));
    if (SpawnClassPin->LinkedTo.Num() > 0)
    {
        //如果node有链接
        CompilerContext.MovePinLinksToIntermediate(*SpawnClassPin, *CallCreateClass);
    }
    else
    {
        //没有指定链接 使用默认class
        CallCreateClass->DefaultObject = SpawnClass;
    }

具体代码看下面

.h


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "K2Node_ConstructObjectFromClass.h"
#include "TestNode.generated.h"

/**
 * 
 */
UCLASS()
class MYNODE_API UTestNode : public UK2Node_ConstructObjectFromClass
{
	GENERATED_BODY()
		UTestNode();
		virtual void AllocateDefaultPins() override;
	virtual FLinearColor GetNodeTitleColor() const override;

	virtual FText GetMenuCategory() const override;

	UEdGraphPin* GetOwningPlayerPin() const;

	virtual void ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override;
	void BreakAllNodeLinks();
protected:
	virtual FText GetBaseNodeTitle() const override;
	virtual FText GetNodeTitleFormat() const override;
	virtual UClass* GetClassPinBaseClass() const override;



	
};
// Fill out your copyright notice in the Description page of Project Settings.

#include "TestNode.h"
#include "GameFramework/PlayerController.h"
#include "TestItemObject.h"
#include "MyBlueprintFunctionLibrary.h"
#include "KismetCompiler.h"
#include "K2Node_CallFunction.h"

#define LOCTEXT_NAMESPACE "TestNode"
struct FBPNode_CreateItemDataHepler
{
	static FString OwningPlayerPinName;
};
FString FBPNode_CreateItemDataHepler::OwningPlayerPinName(TEXT("PinName"));

UTestNode::UTestNode()
{
	int a = 2;
}

void UTestNode::AllocateDefaultPins()
{
	Super::AllocateDefaultPins();
	//创建一个Pin 口 类型是playercontroller Object类型
	UEdGraphPin* OwningPlayerPin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, TEXT(""), APlayerController::StaticClass(), false, false,
		FBPNode_CreateItemDataHepler::OwningPlayerPinName);
	SetPinToolTip(*OwningPlayerPin, LOCTEXT("PlayerPinDes", "PinToolTip"));
}
//修改标题颜色
FLinearColor UTestNode::GetNodeTitleColor() const
{
	return FLinearColor(1.0, 0.f, 0.f, 1.0f);
}


FText UTestNode::GetMenuCategory() const
{
	return FText::FromString("TestNode Menu");
}

UEdGraphPin* UTestNode::GetOwningPlayerPin() const
{
	UEdGraphPin* Pin = FindPin(FBPNode_CreateItemDataHepler::OwningPlayerPinName);
	check(Pin == NULL || Pin->Direction == EGPD_Input);
	return Pin;
}

//具体实现功能函数 重现创建一个新的node 并将原node先关连接转移到新node上
void UTestNode::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
	Super::ExpandNode(CompilerContext, SourceGraph);
	static FName Create_FunctionName = GET_FUNCTION_NAME_CHECKED(UMyBlueprintFunctionLibrary, CreateItemObject); //返回函数的名称
	//获得原node 各个pin指针 后续如有需要进行连接
	UEdGraphPin* SpawnNodeExec = this->GetExecPin(); 
	UEdGraphPin* SpawnWorldContextPin = this->GetWorldContextPin();
	UEdGraphPin* SpawnOwningPlayerPin = this->GetOwningPlayerPin();
	UEdGraphPin* SpawnClassPin = this->GetClassPin();

	UEdGraphPin* SpawnNodeThen = this->GetThenPin();
	UEdGraphPin* SpawnNodeResult = this->GetResultPin();
	//检测节点的class是否为空 并且没有连接
	UClass* SpawnClass = (SpawnClassPin != NULL) ? Cast<UClass>(SpawnClassPin->DefaultObject) : NULL;
	if ((0 == SpawnClassPin->LinkedTo.Num()) && (NULL == SpawnClass))
	{
		CompilerContext.MessageLog.Error(*LOCTEXT("CreateItemDAtaNodeMissingClass_Error", "Spawn node @@ must have a class specified.").ToString(), this);
		// we break exec links so this is the only error we get, don't want the CreateItemData node being considered and giving 'unexpected node' type warnings
		BreakAllNodeLinks();
		return;
	}

	//执行创建步骤

	//创建执行函数
	UK2Node_CallFunction* CallCreateNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
	CallCreateNode->FunctionReference.SetExternalMember(Create_FunctionName, UMyBlueprintFunctionLibrary::StaticClass());
	CallCreateNode->AllocateDefaultPins();

	//连接exe pin
	UEdGraphPin* CallCreateExec = CallCreateNode->GetExecPin();
	CompilerContext.MovePinLinksToIntermediate(*SpawnNodeExec, *CallCreateExec);
	//////////////////////根据你的函数参数名称 传入指定的pin 这里我的CreateItemObject函数只有ItemClass一个参数 所以我只需要将原node的class传入到CreateClassPin节点 
	//连接class pin
	UEdGraphPin* CallCreateClass = CallCreateNode->FindPin(TEXT("ItemClass"));
	if (SpawnClassPin->LinkedTo.Num() > 0)
	{
		//如果node有链接
		CompilerContext.MovePinLinksToIntermediate(*SpawnClassPin, *CallCreateClass);
	}
	else
	{
		//没有指定链接 使用默认class
		CallCreateClass->DefaultObject = SpawnClass;
	}
	////////////////////////////////////////////////////
	//连接结果
	UEdGraphPin* CallCreateResult = CallCreateNode->GetReturnValuePin();
	CallCreateResult->PinType = SpawnNodeResult->PinType; //使用原来的类型
	CompilerContext.MovePinLinksToIntermediate(*SpawnNodeResult, *CallCreateResult);

	//运行上面绑定的函数 返回then pin
	UEdGraphPin* CallCreateThen = FKismetCompilerUtilities::GenerateAssignmentNodes(CompilerContext, SourceGraph, CallCreateNode, this, CallCreateResult, GetClassToSpawn());
	CompilerContext.MovePinLinksToIntermediate(*SpawnNodeThen, *CallCreateThen);

	BreakAllNodeLinks();
}


void UTestNode::BreakAllNodeLinks()
{
	TSet<UEdGraphNode*> NodeList;

	NodeList.Add(this);

	//// Iterate over each pin and break all links
	//for (int32 PinIdx = 0; PinIdx < Pins.Num(); PinIdx++)
	//{
	//	Pins[PinIdx]->BreakAllPinLinks();
	//	NodeList.Add(Pins[PinIdx]->GetOwningNode());
	//}

	//// Send all nodes that received a new pin connection a notification
	//for (UEdGraphNode* Node : NodeList)
	//{
	//	Node->NodeConnectionListChanged();
	//}
}



//在Editor搜索框中的名称
FText UTestNode::GetBaseNodeTitle() const
{
	return LOCTEXT("SearchTitle", "Search Node");
}


//节点在event graph上显示的名称
FText UTestNode::GetNodeTitleFormat() const
{
	return LOCTEXT("NodeTitle", "Node Title");
}

//默认的pin口 class类型
UClass* UTestNode::GetClassPinBaseClass() const
{
	return UObject::StaticClass();
}


#undef LOCTEXT_NAMESPACE
发布了144 篇原创文章 · 获赞 15 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/maxiaosheng521/article/details/100144176