This post is for the 3D aspect of the Godot Engine and contains heavy speculation.
Skeletal animations tend to be the biggest bottleneck in game development. Despite the power of an engine, it always seems we need more. Here I'll speculate about improvements that could be made along with some tips to squeeze out those last few frames.
Let's look at how I understand the workings of animations.
Skeletal animations usually have groups of vertices 'attached' to specific bones. Every frame those bones move, the engine has to recalculate the entire mesh. That is the main reason animated objects give more lag than static objects.
2D animations just replace a sprite/image with the next frame. It is similar to a flipbook and nothing has to be recalculated. Even 2D animations using skeletons just rotate images, so there is less to recalculate for each frame.
Non-skeletal animations for 3D objects (Rotation, changing scale) have less impact on the engine. Thus, having slimes just change their scale as an animation can help you achieve more slimes on screen. I haven't tried this in Godot, but I assume this is how slime rancher handled animations.
If the engine recalculates each mesh, why can't it keep the mesh frames and just reuse the data? This seems like a good idea, baking the poses into separate meshes, but it could consume too much data.
Blender can turn animation frames into shape keys. Deleting that shape key last freezes a mesh in that pose.
I've tried this, swapping the different meshes, with mixed results. This method is useless if you need to combine animations. Swapping meshes doesn't conserve many frames either, but it could be useful for very small animations.
Animation baking just fills in keyframes between frames or makes keyframes for simulations, resulting in slightly less calculations.
If all of your characters weren't animating every frame you could get some more characters onscreen.
If you cannot re-use a frames calculated mesh (I'f tried and failed), then maybe you could split the animations across frames.
Most game engines can update an animation by an amount (usually the Delta or a single frame). The Delta is the time between frame draws.
In an autoload script, you can setup a variable (boolean works best) and have it bounce between two different values each frame.
var frameGroup = false
frameGroup = !frameGroup
Then just give each animated character a variable for the 'group' it is in and set it true or false. You can load each character individually and make sure the groups are distributed evenly.
var animGroup = true
or
var animGroup = false
Load character
get current group variable from autoload script
Set character group variable
change group variable in autoload script
Or you can just randomly set the animated character's group variable on load.
Now you just update the animation when the frameGroup and the character's group are equal. Remember to double the amount you progress the animation to cover both frame's updates. This allows you to split the animation load across frames.
More than two groups can be used, but the animations start to look jerky depending on the framerate.
I have tried this in a few versions of Godot with a 25%-50% framerate increase. It doesn't exactly double the amount of characters you can have on-screen, but it does give a good boost for minimal work I plan to include this in Adventures' EDGE soon.
Other options to reduce load from animations is just not running them. Only run the animations when near the player. You can put the player node into an actual group, look for that, and check the distance to the player.
(most games have a copy of the player in each map and the player never 'leaves' the map. If the player ever has to be removed from the map, look into using weakref() )
Hiding objects that are too far away from the player can also help, but I think animations are still calculated (not sure).
No comments:
Post a Comment