Reflecting changes in a C++ component back to the parent BP actor in editor

Hey there!

This issue isn’t directly related to any of the courses at the moment, although I first ran into it while experimenting with them, specially #battle-tank and #building-escape. It kinda grew on me so I’ve been working on it as an independent “plugin”.

Sorry for the lengthy post, I’ve quoted the extra “backstory” parts in case someone doesn’t need much explanation. :cry:

TL;DR, I want to be able to send events from a component when its parameters are modified in editor, so that the actor containing them actually responds to them and shows the changes properly. It’s been surprisingly hard to do so in any intuitive way, and nothing I’ve tried works reliably.

I decided to experiment a bit and create some sort of interaction/activation system based on components so that they’d be highly modular. Basically, there’d be an “Interaction Component” holding all the logic necessary to bind player (or AI) inputs to allow them to interact with things, and an “Activator Component” which would then react to these interactions.

You’d place the first one on your pawn and bind input events to the “Interact” functions to be able to open/trigger things, and the second one in buttons, doors, keys, lights etc.

My main focus is this second one, the “Activator”. The component focuses on receiving inputs from the other one, but it can also be linked to other activators for chain interactions (like a button that opens a door, for example). The activator keeps track of a customizable number of states (so you can have stuff that opens/closes, locks/unlocks, turns on and off, etc.) and dispatches a handful of events to relay that state to the actor that holds the component. The events include tick-type versions so you can set stuff to interpolate directly from them without the need for timelines.

So basically, the designer would make a prefab blueprint for the interactable object, like the door, drop this component and hook the state events so that the door opens and closes on interactions.

The system performs decently, although I’m sure that I could have relied more on built-in stuff from the engine.

The part that doesn’t work so well is the fact that I couldn’t find a way to reliably reflect the designer’s tweaks to the component in the actor during editor work. Since the parameters are meant to be tweaked in a component, it’s been surprisingly difficult to make these changes to show up directly in the editor so that you can see how it looks without having to start the game.

I’ve tried so many things that I can’t even remember properly, but the three main attempts have been the following:

A) I’ve tried the obvious thing, which was to broadcast delegates directly from the PostEditChange() events. This doesn’t work, even though the delegates report that they are bound. I even tried to force the bound function call though hacky means (getting the UFunction pointer from the delegate and using CallFunction().

The closest I’ve gotten with this method has been to be able to trigger C++ functions in an owning C++ based actor. If I try to force call events or overridden functions in BP, nothing happens (apart from crashes when I touch something delicate).

B) I’ve tried to delay the delegate broadcast calls into overridden events that should happen later, hopefully after the actor has been initialized/updated enough to respond to them properly. The best candidate is OnRegister(), but unfortunately, this isn’t reliable. Specifically, it appears that the event is never called when the component is set to its default values.

The best I’ve managed with this one has been to add a dummy property so that there’s always an edited one even if the relevant ones are reverted to default. This is the solution I’ve settled with for the moment.

C) Out of desperation, I’ve also tried enabling in editor tick and see if I could at least brute force it to work by checking the actor state every frame and try to figure out a good time to broadcast the damn delegates. This didn’t work any better, since anything that I would use as a sign for the actor being “ready” usually happens in one of those overridable events anyways, so I’d be mostly doing the same in a much less efficient way.

From here, I honestly don’t know where to go. I assume that there must be ways to do this appropriately, maybe with an editor plugin or something similar, but I’ve got no experience whatsoever with that and the documentation isn’t exactly easy to follow past the common resources.

I’m not exactly clear on the what is interacting with what. Changing a property on the components gets reflected to the actor the component is attached to?

Could you describe the relationship in more detail? A diagram might help.

Wow, I realize I’ve been so immersed into this problem that I’ve managed to explain it even worse than I could have. I’m very sorry for that, but I’ve asked this before in the official Epic ask forums and that made me realize that the problem comes actually from a much simpler issue (simpler to explain, anyways, not sure if simpler to resolve). I feel like a mumbling conspiranoid now. :nerd_face:

Let’s see if I do it better this time. I’ve also quoted the explanation on how to reproduce the issue.

I’ve got a C++ object inheriting from ActorComponent. It has a handful of instance-editable properties that express its “state”, and it also dispatches events when they are modified. Ideally, I’d like that when you modify these properties in editor time the owning actor can be made aware of these changes and visually show them for convenience.

Since the actor should rerun its OnConstruction() event anytime something changes, the obvious thing would be simply to get the value of the component’s properties in the actor’s BP construction script and initialize stuff accordingly. But it doesn’t work as expected.

It’s very easy to reproduce this issue:

  1. Create a new component (C++) based on UActorComponent with an instance-visible property, for example a FString (you can also use a BP component, but anything else I tried after this had to be done in C++).

  2. Add this component to an actor (BP) and make it print the component’s variable in its construction script.

  3. Notice that, in the Actor’s blueprint editor, if you modify the component’s variable, it will print the correct value.

  4. Now place an instance of that actor in the level and try to modify the component’s variable there. You will see that, even though it does print something, the value will always be the one currently set in the blueprint itself, not in the instance.

Past here is just more backstory, so read further if you’re interested.

Everything else that I tried to explain in my previous post follows from this issue. Since it didn’t work with the construction script, my next attempt was to see if I could dispatch events in other places like PostEditChangeProperty() or OnRegister() and bind them in the actor. If I do that, the bound functions won’t be executed even though it’s possible to check that they are indeed bound to the delegate.

Just out of stubbornness, I even tried to retrieve the actor’s reference and the name of the bound function from the delegate and force the call. Interestingly, I had weird mixed results with this.

If I use functions like ProcessEvent() or CallFunctionByNameAndArguments(), the function call will succeed only if the bound function was defined in C++ and not overridden in BP. If I go “deeper” and use CallFunction() or ExecFunction(), again, it will succeed only with native C++ functions, otherwise it will not only fail, it will also crash the editor. Ironically, I need the functions to be part of the reflection system to be able to force-call them, but I only works if they are still using the C++ code (although this is probably because I’m mucking around with memory addresses directly, and I’m already way out of my depth to do it properly).

Based on this, I suspect that it has something to do with the timing of UE4’s reflection system updating the data in the blueprints. My guess is that the actor’s construction script runs before the component has time to be repopulate its instanced properties, so you get the values from the CDO instead (unless it’s done on purpose for some reason that I don’t know).

I’m seriously amazed by how something that initially seemed so simple got me into such deep research, but by now you’ve probably guessed how pigheaded I am.

I know I could simply set the actor itself to have some initial variables and use them to initialize the component’s state in the construction script, and also use them to se the actor’s visuals from there. And even though that’s good enough in practice it annoys me to no end that I need to do that when that’s what the damn component is supposed to be there for.

And what bothers me the most? I’m sure that I’ve gotten so obsessed over this train of thought that the solution is easy and I’m overlooking it. But at this point I don’t care if I feel like the village idiot, I just wanna know. :cry:

Not sure if this is possible

https://answers.unrealengine.com/questions/313788/construction-script-doesnt-get-properties-that-are.html
https://github.com/EpicGames/UnrealEngine/pull/5239#issuecomment-529974288

Alright, that’s a real pity. Somehow I feel both satisfied and disappointed. Satisfied, because I’ve been struggling with a real issue that has been acknowledged by the actual developers of the engine and not with something that I had overlooked or been too newbie to see. But disappointed because as I understand from those links, the posture of the devs is that this is intended behavior… I understand their caution if the proposed resolution would bring the possibility of worse errors, but the current behavior is clearly not “intended”.

In the end, this makes me feel like a bit of a fool, since I made a choice based on an interest for modularity and reusability, which was supposed to be the point of using components, and as it turns out I could have avoided much trouble if I had simply decided that the “Activator Component” should simply be an “Activator Actor”, since the only solution from the devs is “if you want that sort of modularity, well, don’t”.

:disappointed_relieved:

In any case, I understand that my previous guess was correct and the issue stems from the fact that the component’s instanced data is applied after the actor’s BP construction script runs.

So now I’m thinking of how to proceed. Initially the easiest solution would seem to change the component properties to not to be instance-editable and instead have them initialized in the actor’s construction script through duplicate variables, but I’ve used what I already had in many situations and that will be a real chore… I wonder if knowing the reason for the original issue wouldn’t help me somehow get around it.

I did manage some proper results by waiting until OnRegister() to visualize the component edits, the issue here being that it wasn’t called consistently. But it only fails to be called when the component values are set to the blueprint’s defaults… Which as it turns out is basically the only situation in which the previous issue isn’t relevant.

:thinking:

It feels like I am onto something here. I could try to run the edit visualization tasks once on the actor’s construction, which will cover the inconsistency with OnRegister(). It isn’t ideal because it will make the actor run those tasks basically twice on any change, and I’m not certain if there are other inconsistencies with OnRegister()… But I suppose I’ve got stuff to try now.

Thanks for the replies! I don’t know if I should mark anything as answer or keep this open to come back with whatever I find out.

Well it seems like a bit of a hacky solution but it sounds like you should be able to create a dynamic delegate which assigns values (might need duplicate variables) and broadcast it in PostEditChangeEvent?

You mean the actor broadcasting those delegates, or the component?

The actor doesn’t really need it because it reruns its constructor script anyways when something is changed even in the components (but then, the mentioned issue arises, there’s no way to get the proper data in the instances).

I’ve also tried to broadcast those changes from the component, but if I do it in the PostEditChangeEvent, the bound functions in the actor aren’t called (even though you can check in C++ that they are bound, honestly, is weird). For now, the only working solution has been to broadcast the changes from the component’s OnRegister, but this isn’t called when the component is set to its defaults.

To get around that I had to call the functions bound to those delegates from the actor’s construction script as well with the “wrong” instanced values (which wouldn’t be wrong when the component is set to default, and in any other case the OnRegister event runs later and fixes it… Sadly redundant but it’s the only way I could figure out).

The other thing that I was considering was to enable the component’s editor tick, set some variable to mark the component as “dirty” and check the owning actor every frame for an appropriate moment to broadcast, but I though that was kinda overkill.

1 Like

Privacy & Terms