can i call a function on a constructed object inside a K2Node?

I’ve recreated ConstructObjectFromClass to create an object and set some variables/delegates, i’d like to call the equivalent of BeginPlay when the construction is complete, is this possible from the K2Node?

if i use the function library to call ‘beginplay’ it happens before the ‘ExposedOnSpawn’ variables are set in the K2Node, so i think i need to call it from the K2Node itself

Yes, absolutely. You would spawn a UK2Node_CallFunction, set it up to call your member function, attach the pin for the object to the pin named ‘self’ and attach any other input/output pins.

If you’ve recreated ConstructFromClass (why aren’t you deriving from it?) you’re probably calling FKismetCompilerUtilities::GenerateAssignmentNodes in your ExpandNode implementation. That function returns a pin that an exec output that is at the end of all the assignments. You would hook your function call exec pin to that and move the connections from your nodes then pin to the then pin of your function call.

You can see an example of this in K2Node_SpawnActorFromClass.cpp and how it calls FinishSpawningActor (except for how that one’s another static function instead of a member, but a member function will work just fine)

1 Like

i am deriving from Construct, but i had to override GenerateAssignmentNodes so it includes delegates

man there is so little info on K2Nodes and they seem amazing!

this is where im at

static FName InitFunction = GET_FUNCTION_NAME_CHECKED(UReplicatedObject, Initialize);

UK2Node_CallFunction* CallInitNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);

CallInitNode->FunctionReference.SetExternalMember(InitFunction , StaticClass());

i assume the problem is ‘this’ is referring to the node and not the constructed object so how do i get a reference to that?

or is the LastThen* the output pin? i assumed it was the then pin?

thanks

on that same note, can i retrieve class default variables from the newly created object? i might need to seem so code if possible, these K2Nodes scramble my brain.

thanks again

I’m not sure how you’re doing this as GenerateAssignmentNodes is part of the CompilerUtilities and not ConstructObject. Did you mean you’re overriding ExpandNode? or AllocateDefaultPins? both of which you’d have to override. Or have you duplicated it into your own code base with modifications?

Yeah, this tricky. StarfireK2Utilities.h/StarfireK2Utilities.cpp have some code that I’ve written to do the same thing that you’re doing. CreateEventDispatcherPins would be called in your override of CreatePinsForClass to make the exec pins when the class changes. ExpandDispatcherPins would be called during ExpandNode to do all the wiring and intermediate node creation.

One other gotcha is that GenerateAssignmentNodes doesn’t handle output pins that match property names very well (ie, at all). The good news is that this pull request of mine has been merged so it should be fixed in the 5.5 release (finger’s crossed). Until then, you can either apply the PR locally (if you’re on a source build) or you can temporarily duplicate GenerateAssignmentNodes with my tweak and call that one instead of the KismetCompiler version (I’m not sure how practical that actually is since I take the source build path).

Here’s my tutorial that I wrote a couple years ago now. Everything should still be relevant.

You don’t. Your SpawnIntermediateNode is correct. You’re using the wrong class for SetExternalMember. You’d want: SetExternalMember(InitFunction, UReplicatedObject::StaticClass()).

Right, LastThen is a then pin that is the final exec pin from the assignment nodes. That’s why you would either move your nodes then pin to that pin or wire the exec pin of your function call to it and move the then connection to the then pin of your function call.

It depends on when you want them. If you’re using them to populate anything on the K2Node, like the defaults for any ExposeOnSpawn members, no because the object won’t be created until game runtime and you would need that information at blueprint compile time. You can (and this is what the base class does) the the defaults from the CDO if the type is known at compile time. If it’s being chosen with the class picker drop down it’d be known, but if the pin is connected to something it depends on the type of the output pin it’s connected to.
If you want to use values from the created object at runtime, that would work. You just have to create the variable getters or setters as needed (similar to what GenerateAssignmentNodes is doing).

Don’t worry, it gets easier as you make more of them. I agree with you that they’re pretty amazing and can be used to make blueprint workflows much better for everyone on the team. There are a lot of gaps in the support for them to be made by game teams, that’s why I have so many PR’s for them to hopefully address the problems I’ve run into. So it can help to be on a source build if you really want to make full use of them. Not every problem can be worked around by duplicating stuff from the Engine.

1 Like

firstly thanks for your extensive answer and for your link, i had already read that but didn’t realize it was yours, great guide!

yep i override ExpandNode, AllocateDefaults etc everything is working well.

accessing variables isnt too important, i just wanted the option to change the node title/color depending on what was set in the child class for clarity.

I can override GetNodeTitle to get the ClassDefault DisplayName but for some reason it doesn’t work when the class is a variable, ive noticed this is the same behavior in spawn actor/ construct too.

but back to the main question of calling a function, i did initially have this

CallInitNode->FunctionReference.SetExternalMember(InitFunctionName, UReplicatedObject::StaticClass());

and it compiles but in editor it says function not found, i know its all named correctly so im thinking the node needs to be connected to the output/resultspin of the K2Node somehow.

i could probably create another static function to handle this but im curious if you can do it directly on the newly constructed object

I’d have to see more of your ExpandNode implemenation but you should have something like this (very much pseudo code):

AllocateObjectCall = SpawnIntermediateNode< UK2Node_CallFunction >( ... )
AllocateObjectCall->SetExternalMember( FunctionName, FunctionScope::StaticClass( ) )
AllocateObjectCall->AllocateDefaultPins( )

CallInit = SpawnIntermediateNode< UK2Node_CallFunction >( ... )
CallInit->SetExternalMember( InitFunctionName, UReplicatedObject::StaticClass( ) )
CallInit->AllocateDefaultPins( )

...
Lots of other pin connections and calling GenerateAssignmentNodes
...

ResultPin = AllocateObjectCall ->GetResultPin( )
SelfPin = CallInit->FindPinChecked( FName("self") )

ResultPin->MakeLinkTo( SelfPin )

Actually, after I typed up all that this jumped out at me. Is your Initialize function, the one you call GET_FUNCTION_NAME_CHECKED on, a UFUNCTION and is blueprint callable? Both of those things are required even though you’re making a node in native.

1 Like

finally got it thanks, had to do it like this

you really helped me to understand it better, i had looked at SpawnActor but it didnt quite click without you

       //connect create output to initialize self
	CallCreate_Result->MakeLinkTo(CallInitSelf);
	// move then connection from spawn to init
	CompilerContext.MovePinLinksToIntermediate(*CallCreateThen, *CallInitThen);
		// connect then pin to call init
	//CallCreateThen->MakeLinkTo(CallInit);
	
	// Make pins for all exposed class variables.
	UEdGraphPin* LastThen = GenerateAssignmentNodes(CompilerContext, SourceGraph, CallCreateNode, this, CallCreate_Result, ClassToSpawn);
			
	// connect then pin to call init
	if (bAutoActivate)
	{
		LastThen->MakeLinkTo(CallInit);
		CompilerContext.MovePinLinksToIntermediate(*ThenPin, *CallInitThen);
	}
	else CompilerContext.MovePinLinksToIntermediate(*ThenPin, *LastThen);

Happy to help!

1 Like