I’ve noticed an issue when using dynamic multicast delegates in Unreal (5.1.0). Basically the issue comes into play when moving an AddDynamic() delegate binding from BeginPlay() to PostInitProperties() in the C++ source file.
Here’s how to reproduce it:
Create an Actor with a Dynamic Multicast Delegate:
AActor::PostInitProperties will call in Editor too,you can use conditional compile #if WITH_EDITOR #endif,if is Editor you can skip to bind Multicast delegate.
but why not bind in BindPlay()
AActor::PostInitProperties
Called after the C++ constructor and after the properties have been initialized, including those loaded from config.
That is odd. Maybe postinit gets run twice? that’s what the ensure seems to be implying. I would set some breakpoints and look at the call stack and current InvocationList when you call the adddynamic to see what is going on.
I either bind them in the constructor or beginplay, I’ve not tried postinit.
In the future I will most likely leave the bindings in BeginPlay() instead of moving them to PostInitProperties().
The problem with my current project is that even after I move the bindings back to BeginPlay() in C++ and recompile, I still get the error for these Actors. It now shows the error originating from BeginPlay() meaning it is already bound when BeginPlay() is reached. I know about options such as calling RemoveDynamic() before calling AddDynamic() or AddUnique() but I am more curious about the behavior here.
I could be wrong, but I think it’s possible that once the the bindings were moved to PostInitProperties() they somehow got saved/serialized into the Blueprint. So the Blueprint is still somehow trying to bind in PostInitProperties() even though the C++ code shows that nothing is there anymore. Maybe someone with more engine experience could comment on whether or not this is something that could be happening.
It doesn’t help to recompile and save the Blueprints – the error still shows. However, creating fresh Blueprints for these Actor types does seem to solve the problem. At least, that’s what I was seeing in my testing just now.
So, I guess the only way to “recover” is to create fresh Blueprints and avoid moving the C++ bindings to PostInitProperties() in the future (or use logic to check if not in editor).
I would be curious to know if my theory is correct because I have read that these dynamic delegates can be saved/serialized and that sometimes Blueprint doesn’t respect changes made in C++.
The error is because the function you bound in PostInitProperties cannot be found in the invocation list of the dynamic multicast delegate. Because in the Editor, your Actor has not been instantiated, it cannot be bound. I’m curious why you would bind it in PostInitProperties, the Blueprint serialization is save asset files on disk, and delegates can only be dynamically bound at runtime.
My understanding is that the error is occurring because the same delegate is being added twice. I see that it is already in the InvocationList when trying to add it a second time. It is the result of this ensure() from ScriptDelegates.h:
The second time it is added, and what triggers the error, is from my BeginPlay(). The problem that I am trying to solve is how and why the delegate has been bound the first time. The binding is already there before my BeginPlay() and PostInitProperties() is reached.
I did some debugging after starting a PIE play session:
Looking at the stack frame for ULevel::Serialize(), I was able to access the level’s array of actors:
/** Array of all actors in this level, used by FActorIteratorBase and derived classes */
TArray<AActor*> Actors;
The actor in question was in this list of Actors, with the delegate already bound! Since this is the call chain triggered by just starting a PIE play session, it seems that the delegate is already bound when the PIE session is still being initialized.
As I mentioned in my previous post, creating a fresh Blueprint for the Actors seems to solve the problem. That makes me think that the first delegate binding is somehow tied to or ingrained in the Blueprint.
I included some basic steps to reproduce in my original post, so if anyone has any questions or is able to reproduce or pin down the issue, please let me know.
The source of the problem was moving a dynamic multicast delegate binding from BeginPlay() to PostInitProperties(). That alone resulted in the duplicate delegate error (see ensure() above). I have since moved the binding back to BeginPlay() but I am still getting the error.
The problem is definitely some editor hotreload garbage.
Dynamic delegates work with the engine and can be called from blueprints and whatever. I would not be surprised if PostInitProperties is called when the editor starts up (given an actor is in the world), and then again when you hit play, and I would also not be surprised if this delegate was compiled into a hot reload file.
Honestly try running the game as standalone and see if the problem exists in standalone as well. If it doesn’t, do a full rebuild of your project and I guess don’t bind dynamic delegates in PostInitProperties in the future.
You could try adding an error/warning message log to PostInitProperties and see if it runs on editor startup.
I’m new to Unreal Engine and came across this issue in a demo (link below). I was wondering how to fix this without “re-parenting” my player class and redo everything again as suggested by the tutor, as I can imagine how this would increase your game creation workflow drastically in a big game. @Duti honestly I also felt this is some mismatch between the c++\VS project and the UE project. Reading a couple of posts, people are experiencing the issue in different functions or places in their classes. My AddDynamic (Multicast delegate) binding was in the constructor when this happened. After re-parenting, in other words, changing the base class in the BP editor and changing
it back to your previous base class my player (pawn) worked and the delegate was firing. This is not a solution because you need to reapply all you settings you did in the BP editor again.
I also got to the conclusion that this is some config mismatch between the 2 editors and I need to fire the EU engine mapping logic (between c++ and blueprints) in some way, any suggestions? a command line argument or magic button maybe?
If anyone wants to replicate you can go through this tutorial video and it happens before 1:35:00.
I had a similar problem, but only with Dynamic Delegate and not Multicast. The only problem was with the ExecuteIfBound() method; with regular Execute() everything is fine.
I advise you not to listen to the commentators above - if you want to use the unreal to the fullest - do not be afraid to do such things. This Epic reflection system was not created in order to be afraid of everything in the engine.