An animation I’ve created in Blender is not importing correctly; Constant interpolation keys are being resampled as though they were linear. I’ve tried all four combinations of “Always Sample” on/off and “Optimizer” on/off, yielding no apparent difference.
This means the box jumping from one position to another could end up anywhere along that path when the animation is sampled at sub-frame intervals at runtime, instead of being guaranteed to teleport from one position to the other across an arbitrary frame boundary.
Surely this is a solved problem. Can anyone point me at what I can do to correct this? Even just disabling all interpolation – importing all frames as though they were constant interpolation – on a per-animation basis would be acceptable for my purposes.
Importing the animation to its own file, manually changing each channel to constant interpolation mode, and then needing to re-do that every time each asset gets re-imported – repeated once for potentially hundreds of animation assets – seems like an absurdly poor and error-prone approach, but it’s the only workaround I can think of.
My hack so far is to enable “Always Sample”, disable “Optimizer”, and then at runtime scan all affected animations at startup and convert all their channels to Constant interpolation.
Costs me a small extra tax in VRAM in animation data, but that shouldn’t be a problem; my current project’s performance target is 1080p@30fps on low-end hardware; and the vibe is cozy, not e-sports; so certain animations being limited to 30fps should not be a visible issue.
It would be nice if the issue can be solved for everyone who can’t tolerate that limitation, though.
I found a script that can be run from the editor and converts all keyframes from an AnimationPlayer node to constant interpolation. That may save you some runtime processing power if you don’t have a massive amount of AnimationPlayers scattered all around:
@tool
extends EditorScript
func _run() -> void:
# Get the selected node
var selected_nodes: Array[Node] = get_editor_interface().get_selection().get_selected_nodes()
# Validate selection
if selected_nodes.is_empty() or not selected_nodes[0] is AnimationPlayer:
push_error("Please select an AnimationPlayer node.")
return
var animation_player: AnimationPlayer = selected_nodes[0]
# Process all animations in the player
for anim_name in animation_player.get_animation_list():
var animation: Animation = animation_player.get_animation(anim_name)
# Set interpolation to constant
for track_idx in animation.get_track_count():
animation.track_set_interpolation_type(track_idx, Animation.INTERPOLATION_NEAREST)
print("All tracks set to constant interpolation.")
Afaik Godot doesn’t feature keyframe types, so it cannot import different interpolation modes for separate keyframes, I believe most game engines don’t. Best you can do is change the easing type for the full tracks.
The most “proper” solution I can think of is to bake the action in Blender before exporting (which I believe you should do anyway), which will create keyframes for every frame of the animation, so when imported into Godot, each frame will match.
You might have to play with the settings here depending on whether or not you have any constraints in your armature.
After the action is baked, when you export it and then import into Godot, it should be visually identical between Blender and Godot.
Also when exporting into gltf, I believe you can enable baking automatically when exporting, so you don’t have to bake each time, but I’m not sure how or if it works.
Again, the issue is not that any given integer frame of the imported animation doesn’t match; they all do. The problem is that in the interpolated regions between integer frame numbers, linear interpolation of some keyframes puts objects into places they aren’t supposed to be.
I would like it if I could change the easing type for full tracks at export or import time; but regardless of what easing type I set in Blender, if the bone translation/rotation tracks change value at all over the course of the animation, Godot appears to import them with Linear interpolation.
(Separately, because this is unrelated to the above reply)
My current workaround is for any bone which is about to “teleport” across the space to spend a frame dropping from 1.0 scale to 0.0 beforehand, and back from 0.0 to 1.0 for a frame afterward; gives me 2 fewer frames per loop for each object to be visible and adds a Scale track to every affected bone, but now I can turn off “Sample All”, enable the optimizer, and use Linear interpolation.