How to - Optimize Floating Damage Text (Actor, not Widget)

Hello there !

Another day, another question :

I’ve done a pretty decent floating text number feature, everything works as intended BUT I encounter some fps issue that need some optimization.

So, I have a BP_FloatingText that handle a Widget component in Screen Space, that is a basic WBP with a text to display incoming damage. This BP handle also the display settings (color, opacity, …)

I spawn this BP_FloatingText each time an enemy take damage, at a spawn transform set by enemy location + an offset.

This feature works pretty well, fps stay at 120, until I spam some AoE on 30+ enemy, where my fps drop from 120 to 60 70 until floating text disappear.

So, my question is : Is there any solution to optimize many spawn of a basic actor like floating text to stop these fps drop issue ?

EDIT : A way that I want to give a try is to create a pool of actor with a bunch of floating text load and available, and use this pool of actor to display/hide them when needed, instead of spawning/destroying each time I need to display a floating text. But dunno how to achieve this.

Create a BP_Pool that I place in my level ? On each enemy ? On my player character ? …

EDIT 2 : Is there also any way to optimize render of many widget displayed at same time ?

Is the BP strictly displaying the widget or is it doing other things like attempting to face the players camera on tick?

I’ve heard that the widget components can be expensive so to use them sparingly so it’s possible you’re running into that. I guess I wouldn’t expect it to drop our FPS in half though, that’s intense. Since I haven’t dealt with this issue specifically the best I can offer is guidance on what I would try in order to narrow down the issue.

First try switching to the text render component. This is for giggles, just to see if it really is the widget component that is causing the slow down. If you can spawn thirty of these at a go and not notice the same slow down then it’s the widget component and there might not be a lot you can do to fix this. Or heck, spawn 30 of anything (spheres, cubes, etc.) other than the widget component.

Barring that I think you’re in the territory of running unreal insights and/or the profiler to see where the bottleneck is.

As for you widget pool, many ways to approach that. All of your suggestions are viable. You can create an actual pool (BP_DamagePool) that spawns a bunch up front and then you simply tell it where to display and what to display when the time comes. I would personally probably put it on each actor that can take damage, then whenever they receive damage simply show the widget with the damage value…seems like a cleaner approach.

EDIT: Another thought I had…widget components are expensive partly because they can accept input. Have you tried disabling tick on the widget component?

EDIT 2: It’s probably worth pointing out that if you’re going to use a pool of any sort (ie an actual pool or a widget component on each actor) whenever you’re not displaying the widget you’ll want to disable it completely by turning tick off…that will certainly save you some cycles.

EDIT 3: A little digging brought up the following on potentially improving widget performance in case you haven’t already seen them.
How to design an efficient Blueprint class and widget in VR — Wave VR 4.5.0 documentation (vive.com)
Widget Component slow performance - Programming & Scripting / UI - Unreal Engine Forums

1 Like

Thanks for your answer !

I’ve tried with a render text component, performance are slightly better. But with a raw text render, I guess that if I put some logics on it like face cam and so on, perf will be impacted.

I use a timeline in my BP Floating Text to lower the opacity of the widget to 0 + destroy it. But nothing in the tick. Thing is, if I disable the tick on my widget component, timeline stop working. Dunno if there is any workaround for this.

Thanks for links, I’ll check this righ now!

Edit : Other solution will be to have one widget to display all floating text as text in it, and get world location of enemy, translate it in screen position and display floating text here. Guess it will give me better performance, I’ll try that soon.

Another solution is to find how to fade the actor himself without a timeline or a call to the widget, to be able to disable tick on it to save performance. Or, throw out opacity fade and just display/destroy the floating text.

Yeah, that will certainly be a side-effect. If you disable tick and simply display at full opacity does that improve performance at all? I guess what I’m interested in is whether or not it’s truly just the rendering of the widget versus other stuff that is causing the slowdown. Would give us some insight on how to possibly solve this.

I like your “one widget to rule them all” concept. You could also attempt to scale each text display by the distance from the screen, or something like that, in order to give the illusion of depth. Sounds kind of sexy/clever to be honest. I would be interested if that worked out.

No, not without getting into dynamic material/shader related stuff which of course will have it’s own impact on performance. Again, I would first be interested in nailing down the impact on performance for the individual pieces in question which are actor related stuff (like tick) and visual related stuff (like actually displaying the widget). If you can simply display that many widgets/text renderers with no extra stuff and maintain performance then we have to solve the tick problem. Otherwise you’re in a visual performance issue where widgets/text renderers are just to expensive and you are forced into clever tricks like the one you suggested…naturally a combination of the two is the best solution :slight_smile:

Something else I’ve come across in the past is the following…

FText’s FormatText isn’t Free
Calling FormatText is vital to creating localizable UIs. However FormatText isn’t free. I’ve seen one example of it taking 0.04ms per call on PS4. You should especially avoid calling it every frame. Instead call it only when it needs to be changed using delegates and events. Or cache the value you’re using for the FormatText (e.g. an integer), and only update your text when the value itself changes.

SetVisibility isn’t Free
If you’re like me, you maybe assumed that calling SetVisibility on a widget is very performance-light, or that calling SetVisibility(Hidden) on an already hidden widget is effectively free. It’s not. Calling SetVisibility every frame with your desired visibility is very easy, but it can also be surprisingly heavy. As with FormatText, you should change your UI so that SetVisibility is only called when a change of state is needed.

Both taken from Unreal UIs and Performance · ben🌱ui (benui.ca) (at the bottom)

I know I’ve been guilty of the last one.

1 Like

Can I suggest a slightly different approach -

Instead of creating actors with widget components, you can use a single container widget to your HUD, with a full screen canvas panel. Upon damage dealt, create new small widgets (just a text, basically) and set their position in the canvas using Project World to Screen.

Something along those lines :

Of course you can then improve this to add scaling based on distance and/or damage, and add animations in the sub-widgets to make them fly/bounce and fade-out.

The only true limitation is that I only call ProjectWorldToScreen once when creating each widget (for performance reasons). So the number initially appears on screen to match the corresponding world location but if you quickly move camera then it remains at same position on screen, not in world. In my experience however the damage numbers don’t really stay long enough on screen for this to be an issue, and it actually helps with readability this way.

2 Likes

This is exactly one of the methods the OP discussed above. Thanks for the validation and better yet, screenshots of a potential implementation.

One question/concern I have is what happens if the player moves the screen after all of the text widgets have been laid out? Seems like some sort of ontick functionality is going to be required in order to maintain alignment right? You could of course disable movement during that time but seems heavy handed.

Thanks both of you for your intereset in my question !

I’ve tried a bit of all these things but nothing really change, until… I move from UE5 EA to UE5.0.3, there is a HUGE perf gain, the fps issue is now barely noticable without any optimization, everything works great for my current need !

I’ll certainly test deeper in the above method about displaying all text in a single widget, + maybe a pool of actor. But for the moment, everything is smooth with this UE5 update.

Thanks again ! Have a great day / evening folks !

I guess you can try using FUserWidgetPool
It allows you to create widgets upfront then just use pre created widgets from the pool
You can vary the amount of pre created widgets