How to create K2Node that appears as multiple node entries per every Component/Property within Blueprint

TL;DR

I’m trying to mimic the functionality of e.g. K2Node_VariableGet, where there is only one K2Node class that sweeps for avaible properties within a Blueprint, and populates Actions Menu with multiple Node entries per every avaible property.


Context:

I want to create a custom K2Node that would appear in Actions Menu only when the targeted Blueprint has a specific Component in it. I also want this node to behave slightly diffrent for each of these Components, so the Actions Menu should be populated with multiple entries of this Node (one Node per Component).


Example:

I bieleve every subclass of K2Node_Variable and K2Node_StructOperation does something similiar to what I’m looking for.
(For comparison, on the screenshots above are K2Node_VariableGet and K2Node_BreakStruct )


My issue:

Well, I’m just stuck at figuring out the logic behind this all, and I feel I’m wasting my time at this point.

As I understand such logic should be executed in override of UK2Node::GetMenuActions, but I failed with implementing it.


What I’ve tried:

  1. Googling… looking for tutorials…
    I believe the only good online resource about K2Nodes is this post on Gamedev.net but it doesn’t tell anything useful about my case. Haven’t seen any diffrent post about such case on this forum.

  2. Checking source code:

K2Node_BreakStruct.cpp

void UK2Node_BreakStruct::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
	Super::SetupMenuActions(ActionRegistrar, FMakeStructSpawnerAllowedDelegate::CreateStatic(&UK2Node_BreakStruct::CanBeBroken), EGPD_Output);
}

K2Node_StructOperation.h

/** Utility function to set up menu actions to set the struct type and promote category */
	DECLARE_DELEGATE_RetVal_TwoParams(bool, FMakeStructSpawnerAllowedDelegate, const UScriptStruct*, bool);
	void SetupMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar, const FMakeStructSpawnerAllowedDelegate& AllowedDelegate, EEdGraphPinDirection PinDirectionToPromote) const;
  • At this point diving into source code results in finding new questions more often than answering the ones already existing.
  1. I’ve tried to implement such logic on my own, by writing this code (I hope it’s readable):
void UK2Node_MyCustomNode::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
	UClass* ActionKey = GetClass();
	if (ActionRegistrar.IsOpenForRegistration(ActionKey))
	{
		UBlueprint* OwningBlueprint = GetBlueprint();
		UObject* OwningObject = Cast<UObject>(OwningBlueprint);
		check(OwningObject)

		TArray<UObject*> Subobjects;
		OwningObject->GetDefaultSubobjects(Subobjects);
		for (UObject* Object : Subobjects)
		{
			UMyComponent* MyComponent = Cast<UMyComponent>(Object);
			if (MyComponent)
			{
				UBlueprintNodeSpawner::FCustomizeNodeDelegate PostSpawnDelegate;
				PostSpawnDelegate.BindLambda(
					[MyComponent](UEdGraphNode* NewNode, bool bIsTemplateNode)
					{
						UK2Node_MyCustomNode* ThisNode = Cast<UK2Node_MyCustomNode>(NewNode);
						check(ThisNode)
						ThisNode->TargetedComponent = MyComponent;
					});

				UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(ActionKey, nullptr, PostSpawnDelegate);
				check(NodeSpawner != nullptr);

				ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
			}

		}
	}
}

But it always throws the following assertion at the beggining, so the GetBlueprint always returns nullptr. It happens right when I open Blueprint class in the Editor.

Assertion failed: Result [File:D:\build\++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\Kismet2\BlueprintEditorUtils.cpp] [Line: 2220] 
FBlueprintEditorUtils::FindBlueprintForNodeChecked(/Script/StatefulAbility.Default__K2Node_ExecuteAbility) failed to find a Blueprint.

I’ll be really thankful for any kind of help or guideance :slight_smile:
I’m not familiar with K2Nodes so I’m clearly missing something here.

The call to GetBlueprint likely fails because GetMenuActions seems to be called on the CDO only, an object that doesn’t actually live in any specific blueprint. See https://github.com/EpicGames/UnrealEngine/blob/5ca9da84c694c6eee288c30a547fcaa1a40aed9b/Engine/Source/Editor/BlueprintGraph/Private/BlueprintActionDatabase.cpp#L954 for the details

There are a few more functions in the same namespace/file that may be relevant including some that explicitly handle K2Node_VariableGet/Set. Not sure whether those functions can be extended in custom modules or whether you need to customize the engine for that.

There also seems to be a filtering mechanism (https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Source/Editor/BlueprintGraph/Private/BlueprintActionFilter.cpp) so maybe you can implement GetMenuActions to always add the required action and then implement a corresponding filter to restrict the action depending on the actual blueprint currently being edited.

1 Like

Thanks, I’ll check it out!


Small note, I forgot to mention:
this must be done without editing Engine source.


Then, I clearly misinterpreted what it does.
I was looking for the function that gets access to Blueprint Class, in which the Actions Menu is currently open.
Is there any equivalent of such thing?

EDIT: see next post :stuck_out_tongue:

I’ve checked it just to be sure, and seems like this is not true.
(I maybe missing something though)

What GetBlueprint() does, it just safely gets the outher of Node, which is suppoused to be a Graph, and then gets the outher of Graph, which is suppoused to be a Blueprint.

So GetBlueprint() does the same as:
Cast<UBlueprint>(GetGraph()->GetOuter())
or even
Cast<UBlueprint>(this->GetOuter()->GetOuter())

As I understand the UK2Node::GetMenuActions runs before the actual node is placed into the Graph, so at this point its outher is null, and accordingly GetBlueprint() fails.


EDIT:
Sorry, I clearly misunderstood the quote’s context, and had to figure out the thing that you’ve already pointed.
Yes, GetMenuActions indeed runs on CDO, and indeed GetBlueprint fails because of it (as the Node hasn’t been placed in the Graph yet).

I think if you want to do something like that maybe you can look into the spawn actor k2 node class, and how it iterates through the properties to create the Set Property By Name nodes

1 Like

I’ll dive into K2Node_SpawnActor & K2Node_SpawnActorFromClass later,
but after taking a brief look, I don’t think it will do the job here.

SpawnActorFromClass node is already placed onto the Graph when you start to customize its behavior, and all the logic is depended on a pluged pin.

In my case, I have to perform similiar logic before the actual node gets placed onto the Graph.

Small update - I mainly tried to adapt this concept:


I found out that in my case filtering isn’t needed.
I’m creating these nodes with specified type of SelfPin, so they’re getting filtered automatically.


There is a way of triggering GetMenuActions that I randomly stepped on while reading source:

UClass* MyNode = UK2Node_MyCustomNode::StaticClass(); 
FBlueprintActionDatabase& MyActionDatabase = FBlueprintActionDatabase::Get();
MyActionDatabase.RefreshClassActions(MyNode);

This code can be executed from literally everywhere.


Populating whole Actions Menu is probably the way to go…

I’m currently experimenting with storing pointers to Components within UEngineSubsystem singleton, however this aproach isn’t really useful, as these pointers often get nullified and their values differ between sessions (which is obvious, I’m not sure what I’ve expected…).

I need a different way of storing Component/Blueprint references that will somehow serialize and be persistent after resetting Editor.

This doesn’t seem impossible. Editor itself probably does something similar.

You might take a look at SwitchEnum node. I think it has the required components for what you are looking for.

1 Like