5
\$\begingroup\$

How could I set it so that an animation track switches update mode mid animation? For example, when I need to have separate parts of a sprite's image remain in place as the frame changes?

An example of what I would like to know how to do:

Create an animation like this:

A diagram of an animation track changing from continuous to discrete

(*off)

so that a node's movement might look something along the lines of like this (suppose we actually wanted it to jump across the screen):

An animation of the Godot icon moving left and right; jumping mid animation

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

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: Four numbers are arranged in a square 1-4 clockwise. A sprite slides 1 to 2, pauses, jumps to the 3, and slides to 4. 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:

enter image description here

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:

enter image description here

Beautiful.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .