UE4 Asset Editor Graph Node

UE4 Asset Editor Graph Node

1 Overview


After creating graph view, you need to create graph node to organize the logic inside the graph.


2 Graph Node Construction

Create your own graph node class, derived form UEdGraphNode.

UCLASS()
class UTestGraphNode : public UEdGraphNode
{
	GENERATED_UCLASS_BODY()

public:
	//~ Begin UEdGraphNode Interface
	virtual void AllocateDefaultPins() override;
	virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
	virtual FText GetTooltipText() const override;
	//~ End UEdGraphNode Interface
	...
}
//Create pins
void UTestGraphNode::AllocateDefaultPins()
{
	CreatePin(EGPD_Output, UTestEditorTypes::PinCategory_SingleNode, FString(), nullptr, TEXT(""));
}

Create node instance and add to graph.


UEdGraphNode* CreateGraphNode(class UEdGraph* ParentGraph, const FVector2D Location)
{
	if(ParentGraph == NULL) return NULL;
	UEdGraphNode* ResultNode = NewObject<UTestGraphNode>(ParentGraph);
	ParentGraph->Modify();
	ResultNode->SetFlags(RF_Transactional);
	// set outer to be the graph so it doesn't go away
	ResultNode->Rename(NULL, ParentGraph, REN_NonTransactional);
	ResultNode->AddNode(NodeTemplate, true);
	ResultNode->CreateNewGuid();
	NodeTemplate->NodePosX = Location.X;
	NodeTemplate->NodePosY = Location.Y;
	ResultNode->SnapToGrid(AE_SNAP_GRID);
	// setup pins after placing node
	NodeTemplate->AllocateDefaultPins();
	return ResultNode;
}

GraphNode


3 Graph Node Connection


In the schema class (Introduced in previous) article) , you can determine the node pins connection condition.


const FPinConnectionResponse UEdGraphSchema_Test::CanCreateConnection(const UEdGraphPin* PinA, const UEdGraphPin* PinB) const
{
	// Make sure the pins are not on the same node
	if (PinA->GetOwningNode() == PinB->GetOwningNode())
	{
		return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorSameNode", "Both are on the same node"));
	}
	// Make sure the pins are not on the same node
	return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, LOCTEXT("PinConnect", "Connect nodes"));
}


4 Graph Node Context Menu

Also, in the schema class, you can add context menu for graph node as follow.

void UEdGraphSchema_Test::GetContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const
{
	if (Context->Node)
	{
		/* before UE4.24
		MenuBuilder->BeginSection("TestGraphSchemaNodeActions", LOCTEXT("ClassActionsMenuHeader", "Node Actions"));
		{
			MenuBuilder->AddMenuEntry(FGenericCommands::Get().Delete);
			MenuBuilder->AddMenuEntry(FGenericCommands::Get().Cut);
			MenuBuilder->AddMenuEntry(FGenericCommands::Get().Copy);
			MenuBuilder->AddMenuEntry(FGenericCommands::Get().Duplicate);
			MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().BreakNodeLinks);
		}
		MenuBuilder->EndSection();
		*/
		FToolMenuSection& Section = Menu->AddSection("TestGraphSchemaNodeActions", LOCTEXT("ClassActionsMenuHeader", "Node Actions"));
		{
			MenuBuilder->AddMenuEntry(FGenericCommands::Get().Delete);
			MenuBuilder->AddMenuEntry(FGenericCommands::Get().Cut);
			MenuBuilder->AddMenuEntry(FGenericCommands::Get().Copy);
			MenuBuilder->AddMenuEntry(FGenericCommands::Get().Duplicate);
			MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().BreakNodeLinks);
		}
	
	}

	Super::GetContextMenuActions(Menu,  Context);
}

GraphNodeContextMenu


5 Graph Node, Connection, Pin Style


If you need to customize graph node style, you can add style factory to do this.

GraphNodeStyle

For example (Build-in AnimationGraph):

struct ANIMATIONBLUEPRINTEDITOR_API FAnimationGraphNodeFactory : public FGraphPanelNodeFactory
{
	virtual TSharedPtr<class SGraphNode> CreateNode(class UEdGraphNode* InNode) const override;
};

struct ANIMATIONBLUEPRINTEDITOR_API FAnimationGraphPinFactory : public FGraphPanelPinFactory
{
public:
	virtual TSharedPtr<class SGraphPin> CreatePin(class UEdGraphPin* Pin) const override;
};

struct ANIMATIONBLUEPRINTEDITOR_API FAnimationGraphPinConnectionFactory : public FGraphPanelPinConnectionFactory
{
public:
	virtual class FConnectionDrawingPolicy* CreateConnectionPolicy(const class UEdGraphSchema* Schema, int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const class FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const override;
};

Register these factories in StartupModule().

void FAnimationBlueprintEditorModule::StartupModule()
{
	...
	AnimGraphNodeFactory = MakeShareable(new FAnimationGraphNodeFactory());
	FEdGraphUtilities::RegisterVisualNodeFactory(AnimGraphNodeFactory);

	AnimGraphPinFactory = MakeShareable(new FAnimationGraphPinFactory());
	FEdGraphUtilities::RegisterVisualPinFactory(AnimGraphPinFactory);

	AnimGraphPinConnectionFactory = MakeShareable(new FAnimationGraphPinConnectionFactory());
	FEdGraphUtilities::RegisterVisualPinConnectionFactory(AnimGraphPinConnectionFactory);
	...
}

5.1 Graph Node Style


For graph node style, you need to create your own class, derived from SGraphNode. SGraphNode is the visual widget shown in graph.

For example:

class SGraphNodeAnimState : public SGraphNode
{
public:
	SLATE_BEGIN_ARGS(SGraphNodeAnimState){}
	SLATE_END_ARGS()
	void Construct(const FArguments& InArgs, UAnimStateNodeBase* InNode);
	// SNodePanel::SNode interface
	virtual void GetNodeInfoPopups(FNodeInfoContext* Context, TArray<FGraphInformationPopupInfo>& Popups) const override;
	// End of SNodePanel::SNode interface

	// SGraphNode interface
	virtual void UpdateGraphNode() override;
	virtual void CreatePinWidgets() override;
	virtual void AddPin(const TSharedRef<SGraphPin>& PinToAdd) override;
	virtual TSharedPtr<SToolTip> GetComplexTooltip() override;
	// End of SGraphNode interface
	...
};

Determine which SGraphNode you want to used in the node factory.

For example:

TSharedPtr<class SGraphNode> FAnimationGraphNodeFactory::CreateNode(class UEdGraphNode* InNode) const 
{
	...
	if (UAnimStateNode* StateNode = Cast<UAnimStateNode>(InNode))
	{
		return SNew(SGraphNodeAnimState, StateNode);
	}
	return nullptr;
}

Here, the UAnimStateNode is corresponding to UTestGraphNode (derived form UEdGraphNode) above.


5.2 Connnection Style


For the pin connection style, you need to create a drawing policy class, derived from FKismetConnectionDrawingPolicy;

For example:

// This class draws the connections for an UEdGraph with an animation schema
class FAnimGraphConnectionDrawingPolicy : public FKismetConnectionDrawingPolicy
{
public:
	// Constructor
	FAnimGraphConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj);

	// FKismetConnectionDrawingPolicy interface
	virtual bool TreatWireAsExecutionPin(UEdGraphPin* InputPin, UEdGraphPin* OutputPin) const override;
	virtual void BuildExecutionRoadmap() override;
	virtual void DetermineStyleOfExecWire(float& Thickness, FLinearColor& WireColor, bool& bDrawBubbles, const FTimePair& Times) override;
	// End of FKismetConnectionDrawingPolicy interface
};

Determine which drawing policy used in the graph in the factory.

For example:

class FConnectionDrawingPolicy* FAnimationGraphPinConnectionFactory::CreateConnectionPolicy(const class UEdGraphSchema* Schema, int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const class FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const
{
	if (Schema->IsA(UAnimationGraphSchema::StaticClass()))
	{
		return new FAnimGraphConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
	}
	else if (Schema->IsA(UAnimationStateMachineSchema::StaticClass()))
	{
		return new FStateMachineConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
	}
	return nullptr;
}

Notice that you can also determine the drawing policy class in the schema class. It will override the policy from the connection factory class.

UCLASS()
class UEdGraphSchema_Test : public UEdGraphSchema
{
	GENERATED_UCLASS_BODY()
	//~ Begin EdGraphSchema Interface
	virtual class FConnectionDrawingPolicy* CreateConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float InZoomFactor, const FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const override;
	...
};

5.3 Pin Style


For the pin style, you need to create a pin class, derived from SGraphPin;

For example:

class SGraphPinPose : public SGraphPin
{
public:
	SLATE_BEGIN_ARGS(SGraphPinPose)	{}
	SLATE_END_ARGS()

	void Construct(const FArguments& InArgs, UEdGraphPin* InPin);

protected:
	//~ Begin SGraphPin Interface
	virtual const FSlateBrush* GetPinIcon() const override;
	//~ End SGraphPin Interface
	mutable const FSlateBrush* CachedImg_Pin_ConnectedHovered;
	mutable const FSlateBrush* CachedImg_Pin_DisconnectedHovered;
};

Determine which pin style used in the graph in the factory.

For example:

TSharedPtr<class SGraphPin> FAnimationGraphPinFactory::CreatePin(class UEdGraphPin* InPin) const
{
	if (InPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Struct)
	{
		if ((InPin->PinType.PinSubCategoryObject == FPoseLink::StaticStruct()) || (InPin->PinType.PinSubCategoryObject == FComponentSpacePoseLink::StaticStruct()))
		{
			return SNew(SGraphPinPose, InPin);
		}
	}
	...
	return nullptr;
}

The default sytle will be used if return nullptr.





Tags: UE4 Editor Vistied:
Share: Twitter Facebook LinkedIn