Floating combat text and memory/widget destruction?

Hello,

I’ve made a system for floating combat text, but I am a little worried that this will cause issues with memory. As it stands, every time the player or an enemy receives damage a simple widget displaying the damage value (as well as effects for elements and critical hits) is displayed (fade in and fade out with transform animation).

Now as there’s no way as far as I’m aware to actually destroy widgets and my game has a lot of enemies, I am worried that during a regular play session as the player acrues potentially thousands of these in memory, it will cause an issue.

Does anyone have any ideas for workarounds? I thought having a set amount and reusing them with a sequence might be possible, but it doesn’t seem like a very efficient option.

I have a similar system in my game and I just call “Remove From Parent” after the animation completes playing. There is also a “Remove All Widgets” option.

Also I found this link where this issue seemed to be discussed in details.

Also here it seems a staff member suggested that it will be garbage collected following any command to “remove”.

Yeah I’m already doing remove from parent but that doesn’t actually destroy it though? It still exists in memory.

269474-endanim.png

Also wondering if there is a way to dynamically change the animation in case anyone knows.

What I mean by that is for example I have a starting transformation and an ending transformation as the widget fades in, floats up and fades out. Would be possible to change the transformation end point? Without resorting to any kind of event tick screen location interp.

Yeah saw some of that, just thought maybe there had been a development in the past couple years that allows us to do this.

I’ve been very careful in regards to this with my inventory system, as well as skills, equipment slots etc, to make sure they are only created once, not every time you access the widget or change a variable like a lot of systems I’ve seen.

For this floating combat text I can’t seem to find any workable solution though except reusables which do become cumbersome when you have 10+ enemies chopping away as well as dot-effects and status effects etc.

I’ve tried using that node but they still show up in the debug instance list?

Update: I tried that again now by manual activation. Can confirm it works. Maybe I called it at the wrong time last time or wasn’t paying attention.

Either way: Great. Convert your comment to an answer and I’ll accept the solution. Thank you!

This is an ancient issue with UMG; with pins sometimes creating unwanted references. I can confirm that calling Collect Garbage gets rid of them.

I think this thing goes much deeper. I remember the conversation I had with Rumbleball in the 1st thread Nebula linked. In the end, he was very right about GC not working right. So I’d hate to spread misinformation again and claim that calling Collect Garbage is the solution for all widget referencing problems - it’s a solution in a case where:

269506-capture.png

… a function returns widget references. Now, good luck trying to get rid of them without manually calling Collect Garbage.

It does work in my current project where I create widgets in the hundreds so I tend to keep close tabs on them. So far so good. Not sure what else it can affect, though. So can’t recommend it as solution for everyone and everything.

Do consider accepting Nebula’s answer, the links he has are quite pertinent here and this discussion could be useful as a reference later on.

I agree. Removing from parent and clearing a variable reference is not always enough to have a widget GCed. Apparently, it’s the pins that hold on to the reference sometimes - this was confirmed by Epic staff who also claimed it was fixed around 4,19. Which I doubt judging by what I’ve experienced since then.

Manually calling Collect Garbage helps.

I honestly had no idea about this prior to this discussion, so very informative. I just checked in my own implementation and indeed it was storing references to a large number of the “floating combat style” widgets. Linking “collect garbage” to “Event Destruct” or in my case the end of the animation removed the widgets from the debug list. However, I noticed as the warning for “collect garbage” suggests, this is a rather slow process if it happens a significant amount of times in a short period. So while it was fine for a “normal” amount of damage being taken by the player, if my player happened to come under rather “heavy” fire I could notice the decrease in frame rate. Not sure what the best solution is for this either. Seems like Unreal should have provided a definitive solution for this issue a while ago with a node similar to “Destroy”.

As far as I can tell it is for all intents and purposes gone when using collect garbage. Remove from parent hides it and kills it in a way, but it is still referenced in memory, which is why I asked the question.

Remove all widgets is not an option due to visible UI elements.

They claimed they fixed a lot of stuff in 4.19. Nested structs for example, which has recently corrupted my project to the point where simply opening an unrelated blueprint caused the engine to crash.

I noticed that too. I think there are several ways to avoid it. First would be to limit the threshold for floating combat text (does the player really need a visual representation of 0.1% of their health disappearing?).

Second would be to call collect garbage on other events that are frequent but not every frame if you have rapid damage for example. I’m guessing once every 10 secs may be more than enough actually, even if you had a shooter style game with high fire rates and every hit creating a widget.

Just gotta comb through and find an appropriate event or function to tie it into.

Nested structs is yet another dreadful story… Yup!

I think the correct solution would be to do this using an “object pool pattern”. I.e. reuse the widgets instead of trying to free them.

Pooling is absolutely fine and works well with widgets, a tad heavy on the memory footprint, perhaps.

While it’s a good suggestion, it does not change the fact that the Garbage Collection fails to do its thing here.

Pooling could be a solution, but it could be broken by unforseen events occuring, which is pretty much a guarantee once there’s an actual person at the controls instead of a dev-type fellow. Say you think that it’s unlikely the player gets hit more than 10 times per second, and the widget stays on screen for 3 seconds, a volume of 30 with a safety margin of 10 extra should be sufficient. Make it 50 for good measure.

Well, first you’re walking around with 50 of these at all times which isn’t optimal in itself, but if the player manages to get into a whole mess of trouble (which is very likely), the shortcomings will be painfully obvious once your pool size is no longer able to handle the load it needs. Of course you could adjust stay-time dynamically but… Is that really any better?

As a followup question though: Currently I am using get actor location → Project world to screen to make the widgets face the camera. Now this obviously has the undesired effect that if I hit an enemy, then pan the camera, the widget stays with the camera instead of sticking to the hit enemy, as per project to screen.

Is there any way I can make it always rotate to face the camera, but stay with the hit actor location-wise? I am of course aware of the draw to screen option for widget components, but this isn’t a widget component.

I’ve seen some handle this by putting camera rotation calculations on event tick, but that doesn’t seem like a very efficient option to me.

Regarding pooling, you can dynamically increase and decrease reusable pool size. Trim the pool size every now and then to keep its size in check.

Is there any way I can make it always
rotate to face the camera, but stay
with the hit actor location-wise?

Also a problem if the enemy moves away, eh? 2 options here:

  • track each widget every frame, projecting world to screen during tick, sounds expensive

edit: if you look at the bottom image in this post, you’ll see that the widget position is being updated during tick, the only thing that would need adding here is the screen location (deprojected world location) of the enemy over whom the text should display.

or

  • give each enemy a widget of their own, it would have a small canvas, and track that canvas only. Rather than adding the floating text to the viewport directly, add it to that canvas instead. Essentially, you’d have each enemy drag a canvas with them. And the floating text no longer needs to worry about world coordinates at all - it will use local canvas positioning instead.

Hmm that’s very interesting, sounds like a viable option. I’ll have to test that out and might come back with some questions later on, but it seems fairly straightforward. Simply make a transparent main canvas and add it as a widget component, then in the function for receiving damage I’d simply add it to the canvas. And the clearing functionality will remain the same.