Note: This solution is actually for Godot 4, but the question is tagged Godot 3.
You can use a method call in the animation to change the interpolation type of the position track mid-animation.
For reference here's the result I set up in this example:
It has spots labeled 1-4. The sprite slides 1->2, pauses, snaps 2->3, then slides 3->4.
Background Info
Quite a lot of docs to cover, so first the background info:
- AnimationPlayer contains your Animation, with some string animation name. "slide" in my example. The animation is accessible from AnimationPlayer's parent class AnimationMixer
- Animation has a track for each property you can add keyframes to. They are numbered starting from 0, top to bottom. Your Sprite position track index would be track 0.
- There's no class/object for animation tracks. You interact with them with various Animation methods, referring to them by their index.
- The track has an interpolation type which is an enum (int). The continuous is 1, jump (nearest) is 0.
- You can add a call method track that calls whichever methods you like at certain keyframes in the animation. Add Track > Call Method > Pick the node holding the desired methods.
- We can write a method that changes the interpolation type an animation and it can be run on the animation while it's playing.
- Method calls in animations do not call when playing the animation in-editor. You'll need to run the scene, and start the animation on-ready to preview the animation. This is documented in this GitHub issue. In-editor it'll use whichever interpolation type it starts with.
Solution
First I set up an AnimationPlayer and Sprite2D like you have it. I keyframe the Sprite2D to move to each numbered position in order, with continuous interpolation. 1 -> 2 -> 3 -> 4. I'll also refer to these numbers when adding keyframes for function calls.
Next I'll add a script to the AnimationPlayer node. I'll write a method to change the interpolation type of the currently running animation to a provided interpolation type (0 is nearest/jump, 1 is continuous) . I'll use self
to describe the animation player in this script for emphasis, but it is optional.
func _ready():
# Start animation on scene start
self.play("Slide")
## Given an InterpolationType, set the current animation's InterpolationType
func switch_current_interpolation_type(new_type: int):
# Needs a running animation to work
if not self.get_current_animation():
return
var animation_name: String = self.get_current_animation()
var animation: Animation = self.get_animation(animation_name)
var track_number = 0
# Sprite Position keyframes are in the first (0) track in this example animation.
animation.track_set_interpolation_type(track_number, new_type)
Now we need to add calls to this in the animation track, to swap the interpolation type.
We start with continuous interpolation (1) set in the editor. Change the interpolation type to 0 at keyframe 2, and back to 1 at keyframe 3.
Click Add Track > Call Method Track. Select the AnimationPlayer. This creates a track to call methods on our AnimationPlayer.
Move the timeline header (the white line) to keyframe 2, right click > Insert Key. Select the function we defined above. (Note, if it's missing, don't forget to save the script!)
This adds a call to the function with null as the default interpolation type. If you look over to the Inspector you should see an AnimationTrackKeyEdit tab. Hidden in the args > 0 you'll find the input param of type Nil. Switch it to int, and put the value 0 for nearest interpolation. This should switch the interpolation type from continuous to nearest (snap) when the sprite is at position 2.
We'll repeat the process to swap it back to continuous (1) at keyframe 3. That should complete the demo animation. Here's what the keyframe editor looks like:
This generates the example GIF posted above. (Note I have playback speed set to 0.5)
Some extra things to mention:
- If the sprite is moving fast enough, it may stutter when you change the interpolation type to linear. In that case you can add a duplicate position keyframe shortly after it's supposed to stop to have it more reliably stop.
- I set a bunch of null checks in the functions to prevent errors when changing things in editor.
Setter workaround
It works, but I really, really dislike how the function calls look in the animation editor, with the overlapping text etc. I'll also show off a neat workaround to clean this up.
We can add an export variable to the animationPlayer, arbitrarily named "interpolation_type" and set it to the default 1 (continuous).
We can use a setter to call a function when we change this variable by keyframing it in the animationplayer. When the value changes, call our previously defined function.
@export
var interpolation_type = 1 : set = _set_interpolation_type
# When variable changed, call _set_interpolation_type instead
## When the interpolation_type changes, call the other method.
func _set_interpolation_type(new_type):
if new_type == null:
return
switch_current_interpolation_type(new_type)
# Update the variable in case you wanna use it later
animationType = new_type
Then delete function call track to replace it with the new track.
Add Track > Property > AnimationPlayer > interpolation_type
Or just add the keyframes with the key button next to the exported variable.
Add keyframes with just the type number where we had the function calls. Starts at 1, switches to 0 at position 2, and back to 1 at position 3. It would now look like this:
Beautiful.