One core part of Charge! is its game mode path command, which has "command lines" of shootable objects that snake all around the map, creating back and forth tussles for territory. The visuals of which have changed for both functional reasons, and style, but also led to the need for performance improvements.
The first version was a line of flat panels, made simple to get the prototype running and testable.
We eventually turned them into nodes of rings, and later on, added spinning animations. The spherical hitbox made them more consistent to hit from all angles, and the rings made them stand out better as important.
The final result both worked for the visual style, and made their gameplay function clearer, with aspects like the rings spinning faster for a bit once they were hit. However, as you might expect, going from 80+ squares that didn't move, to giving each node 3 skeletally animated rings, did cause some changes in performance.
The result of the animated rings wasn't so drastic that the game became unplayable, but in profiling the performance, there was a major difference.
With the charge lines completely removed
With the charge lines & animations
NOTE: The above numbers were profiled in the unreal editor, so while the game would run better in an optimized release build, resulting in different average frame times, the performance gap was noticable in game, and we still wanted a good editor framerate as well.
The numbers to focus on here are the frame and game times, which should be around 16.33ms for the game to run at a good 60fps. With an about 18ms increase caused by the charge lines, this was clearly an issue, and additional profiling confirmed that a meaningful cause was the animations.
"Stat Anim" profiler, showing a high value for evaluating animations
Unreal Insights Game Frame Flame Graph Data (The bigger the sections, the slower they are)
Blue portion from above image
Zoomed in to see it's the ring's update function taking time
Green portion from above image
Zoomed in to see it's the ring's animation taking time
So, I figured that there would be a better way, and wanting to learn possible options, I started looking into potential optimizations, and came across the idea of vertex animation textures.
If you haven't heard of Vertex Animation Textures (or VATs) - which I certainly hadn't before this - it's a way of using the fact that the materials/ shaders that define how objects look also handle where the verticies of an object are drawn. And, if you change the positions of the verticies to match where they would be for an animation you want to show, you can mimick the animation.
This has some restrictions, such as only changing the object visually to make it look animated, and not affecting collisions, but for an object like the objective nodes, that only use a static sphere collision anyway, this wasn't an issue. And, because the materials that do this "vertex animation" run on the GPU, it makes used of the GPU's unused resources & ability to run much more at once, which ideally runs much faster.
Below shows an existing way to apply this in UE5, but if you'd like to read more, you can check out this article here, which is the first place I read about vertex animation. It talks about the overall idea, and a specific custom implementation used to make performant crowds in a game:
To test if vertex animation would help enough before completely diving in, I implemented vertex animation in one area first, finding this video on animation textures: https://youtu.be/zcDv5WfCTSU?si=qBzvo1_zUeAsoVmc&t=552, and using it to make a specific material that applied a vertical spinning animation, to replace the command node's center ring animation.
Center ring animating with vertex animation, while the others use skeletal animation
Performance before
Performance after. While situations varied, on average, it saved .5-1ms.
The overall fps was about the same, and could be higher or lower in some cases depending on the map location and where you were looking. But, after multiple tests, the animation execution time was consistently at least .5-1ms lower, sometimes up to 2ms. While this had some margin for uncertainty, it indicated that it was still worth looking into, and so I decided to try this on all 3 rings. And, I knew the specific function-based material method wouldn't work as well for the more complex spinning of the outer rings, so I looked for potential existing solutions before making my own system, and found that there existed an experimental official AnimToTexture plugin for the purpose of converting animations to VATs. If you'd like to try it in your own game for animations that don't need to switch between each other, I'd reccomend looking at this repository https://github.com/kromond/AnimToTextureHelpers, utilizing the tool widget it provides, and following the tutorial linked in the repository/ shown below.
It took some adjusting to fit UE 5.3 in my case, but aside from making sure to check "Enforce Power of Two" in the data asset in the video, it worked quite well to turn the ring animations into materials that were set on the objects, and would play the spinning animations.
I also needed the ability to change the speed of animations for a few seconds, and the object's color, when each node was hit. I did this with a material parameter for the color, and the built-in custom data value in the AnimToTexture plugin's bone animation material.
After implementing the vertex animation on both rings, and testing the differences between it and the original version, I found that it did save time, about 4-7 ms, which was at least an 8% boost to the framerate.
Before
After
And, there was a possiblility that this method decreased the game thread time by doing less animation calculations on the CPU, but increased the render thread time since we were doing more on the GPU now. However, after profiling the differences, the render thread time also decreased proportionately, meaning that we had actually saved time and players would see increased FPS.
Old Render Thread Time
New Render Thread Time
Overall, while this approach was only one of multiple methods that helped increase performance, it did give meaningful improvements, while maintaining visual quality/ vision of the artist's animation. So, if you've profiled your game, see you have skeletal animations that are weiging on performance, and this fits your use case (i.e. if you don't need to worry about the collisions of your object matching the animation, or switching animations on your object too much), I'd reccomend giving this method of Vertex Animation Textures a look.