Voltar para o Blog
Quest Log

AnimationPlayer no Godot: Como Animar Tudo na Cena

Personagem de jogo 2D sendo animado em uma timeline de keyframes no editor do Godot

Tutorial completo do AnimationPlayer no Godot 4: anime sprites, propriedades de qualquer node e dispare funções com Call Method tracks na prática.

AnimationPlayer no Godot: Como Animar Tudo na Cena

O AnimationPlayer no Godot é provavelmente o node mais subestimado da engine. A maioria dos iniciantes acha que ele serve só pra trocar frame de sprite, descobre o AnimatedSprite2D, e nunca mais volta. E aí perde o ponto: o AnimationPlayer anima qualquer propriedade de qualquer node da cena. Posição, cor, escala, volume de áudio, texto de uma Label, o disabled de uma colisão. Se aparece no Inspector, dá pra colocar numa timeline.

Isso muda como você constrói coisas. Cutscene, ataque com hitbox sincronizada, porta que abre, HUD que pisca quando toma dano, tela de game over que entra deslizando: tudo isso é um AnimationPlayer com meia dúzia de keyframes, sem uma linha de código de interpolação manual.

Nesse tutorial eu monto o fluxo completo: animar sprite por frames, animar propriedades, controlar tudo por código e usar Call Method tracks pra disparar funções no meio da animação. Todo código é GDScript do Godot 4.x.

Como o AnimationPlayer funciona

A estrutura é simples: o node AnimationPlayer guarda uma lista de animações (resources do tipo Animation), e cada animação é uma timeline com tracks. Cada track aponta pra uma propriedade de um node da cena, e dentro da track você coloca keyframes: pares de tempo e valor. A engine interpola entre os keyframes sozinha.

Pra começar:

  1. Adicione um node AnimationPlayer na cena (pode ser filho de qualquer coisa, ele acha os outros nodes por caminho relativo).
  2. Selecione ele e abra o painel Animation que aparece na parte de baixo do editor.
  3. Clique em Animation > New e dê um nome, tipo idle ou run.
  4. Com a animação aberta, selecione qualquer node da cena, e repare que todas as propriedades no Inspector ganharam um ícone de chave ao lado. Clicar na chave cria um keyframe daquela propriedade no tempo atual da timeline.

É esse ícone de chave que destrava tudo. Você não escolhe de uma lista do que "pode" ser animado: você anima o que quiser, direto do Inspector.

A track RESET

Na primeira vez que você cria um keyframe, o Godot pergunta se quer criar uma track RESET. Aceite. O RESET é uma animação especial que guarda o valor padrão de cada propriedade animada, e o editor aplica ela ao carregar a cena. Sem isso, você edita a animação de morte do personagem, salva a cena com ele deitado e transparente, e ele nasce assim no jogo. Com RESET, tudo volta pro estado inicial.

Animando um sprite por frames

Dá pra animar sprite com AnimatedSprite2D, mas fazer isso no AnimationPlayer tem uma vantagem enorme: a troca de frame vive na mesma timeline que o resto (som de passo, hitbox, partícula), tudo sincronizado por padrão.

O setup com spritesheet:

  1. No Sprite2D, configure Hframes e Vframes com a grade da sua spritesheet (uma sheet de corrida com 8 frames em linha é Hframes = 8, Vframes = 1).
  2. Crie a animação run no AnimationPlayer e defina a duração (0.8 segundos pra 8 frames dá um frame a cada 0.1s).
  3. Com a timeline em 0, selecione o Sprite2D, coloque Frame em 0 no Inspector e clique na chave.
  4. Avance a timeline e crie um keyframe pra cada frame, ou use o atalho: depois do primeiro keyframe, a setinha pra cima no campo Frame com a chave de keyframe ligada vai criando os próximos.

Detalhe que confunde todo mundo na primeira vez: a propriedade frame é um inteiro, então a track usa interpolação discreta (o valor pula, não desliza). O Godot configura isso sozinho pra propriedades inteiras, mas se o sprite parecer "borrado" entre frames, confira o modo de interpolação da track no painel de animação.

Pra deixar em loop, clique no ícone de loop no canto direito do painel Animation. Animação de ciclo (idle, run) fica em loop; animação de evento (attack, death) fica sem.

Animando propriedades: a parte que vale ouro

Aqui o AnimationPlayer deixa de ser "trocador de sprite" e vira ferramenta de polish. Alguns exemplos práticos do que dá pra montar só com keyframes de propriedade, sem código:

  • Item flutuando: keyframes na position do sprite subindo e descendo, loop ligado, interpolação cúbica pra suavizar.
  • Dano piscando: keyframes no modulate do sprite alternando entre branco e vermelho.
  • Porta abrindo: keyframes na position da porta e no disabled da CollisionShape2D, na mesma animação. Quando a porta abre, a colisão desliga junto, sempre no mesmo timing.
  • Fade de tela: keyframes no modulate:a (só o canal alfa) de um ColorRect preto cobrindo a tela.

O padrão se repete: em vez de escrever código de interpolação com lerp e timer, você descreve o resultado na timeline e a engine executa. Código você reserva pra decidir quando a animação toca, não como ela acontece.

Próximo nível
Quer aprender isso na prática?

No CursoGame.Dev você sai dos tutoriais soltos e constrói jogos publicáveis, com trilha progressiva, quests práticas e feedback real.

Conhecer a plataforma
+500 alunos4.9/5Garantia 7 dias

Controlando o AnimationPlayer no Godot por código

Timeline pronta, agora o script decide o que toca e quando. A API central é pequena:

extends CharacterBody2D

const SPEED = 300.0

@onready var anim = $AnimationPlayer
@onready var sprite = $Sprite2D

func _physics_process(delta):
    var direction = Input.get_axis("ui_left", "ui_right")
    velocity.x = direction * SPEED
    move_and_slide()

    if direction != 0:
        sprite.flip_h = direction < 0
        anim.play("run")
    else:
        anim.play("idle")

Repare que play() é chamado todo frame e está tudo bem: se a animação pedida já está tocando, o Godot não reinicia ela. Isso simplifica muito a lógica de locomoção.

Outros métodos e propriedades que você vai usar direto:

anim.play("attack")           # toca do início
anim.play_backwards("door")   # toca de trás pra frente (porta fechando)
anim.stop()                   # para e volta pro começo
anim.pause()                  # congela onde está
anim.queue("idle")            # enfileira pra tocar quando a atual acabar
anim.speed_scale = 2.0        # tudo nesse player toca em dobro de velocidade
anim.seek(0.5, true)          # pula pro segundo 0.5 e aplica o estado na hora

if anim.is_playing():
    print(anim.current_animation)  # nome da animação atual

O speed_scale é mais útil do que parece: um buff de velocidade de ataque pode ser só anim.speed_scale = 1.3, e toda a sincronia interna da animação (som, hitbox, frames) escala junto de graça.

Esperando a animação acabar

Pra animação de evento, você quase sempre precisa saber quando ela terminou. O sinal animation_finished resolve, e com await o código fica linear:

var atacando = false

func atacar():
    if atacando:
        return
    atacando = true
    anim.play("attack")
    await anim.animation_finished
    atacando = false
    anim.play("idle")

Uma pegadinha clássica: animation_finished não dispara em animação com loop, porque ela nunca termina. Se o seu await ficou pendurado pra sempre, confere se a animação não está em loop sem querer.

Call Method tracks: disparando funções na timeline

Essa é a feature que separa quem usa o AnimationPlayer de verdade. Uma Call Method track chama uma função de um node num ponto exato da animação. O caso de uso canônico é ataque corpo a corpo: a hitbox só pode existir nos frames em que a espada está cruzando o ar, nem antes, nem depois.

Sem Call Method track, o pessoal resolve isso com timer no código, e o timer dessincroniza na primeira vez que alguém ajusta a duração da animação. Com a track, o timing vive dentro da própria animação.

O setup:

  1. Na cena do personagem, tenha uma Area2D chamada Hitbox com a CollisionShape2D desabilitada por padrão.
  2. No script, escreva os métodos que a animação vai chamar:
func ativar_hitbox():
    $Hitbox/CollisionShape2D.set_deferred("disabled", false)

func desativar_hitbox():
    $Hitbox/CollisionShape2D.set_deferred("disabled", true)

O set_deferred aqui não é frescura: mexer em colisão no meio de um passo de física pode dar erro, e adiar a mudança pro fim do frame resolve.

  1. Na animação attack, clique em Add Track > Call Method Track e selecione o node do personagem.
  2. Clique com o botão direito na timeline no tempo em que o golpe conecta e escolha Insert Key. O Godot lista os métodos do node: escolha ativar_hitbox.
  3. Alguns frames depois, outra key chamando desativar_hitbox.

Pronto. Agora se o animador (ou você daqui a três meses) esticar a antecipação do golpe, a hitbox acompanha, porque ela é parte da animação e não um número mágico solto no script.

Outros usos que valem a pena: tocar som de passo no frame exato em que o pé toca o chão, spawnar partícula no impacto, chamar queue_free() no fim da animação de morte, dar o tiro no frame certo da animação de atirar.

Audio tracks e Animation tracks

Mais dois tipos de track que completam o kit:

Audio Playback Track: aponta pra um AudioStreamPlayer da cena e toca um stream num ponto da timeline. Pra som curto e sincronizado (passo, golpe, porta), é mais direto que Call Method chamando play(), e o editor mostra a forma de onda na timeline, o que ajuda a acertar o timing no olho.

Animation Playback Track: um AnimationPlayer dispara animações de outro AnimationPlayer. É assim que se monta cutscene: um player "diretor" na cena da fase controla a timeline geral, e nos momentos certos dispara o walk do personagem A, o open da porta, o shake da câmera. Cada objeto mantém suas próprias animações e o diretor só rege.

AnimationPlayer, AnimatedSprite2D ou Tween?

Os três se sobrepõem e a dúvida é legítima. Meu critério na prática:

AnimatedSprite2D quando a única coisa animada é o frame do sprite e acabou. Protótipo, personagem simples sem hitbox sincronizada, item de cenário. É mais rápido de montar.

Tween quando a animação é criada em runtime com valores que você não conhece no editor. Mover um card até a posição da mão do jogador, dar zoom até onde o jogador clicou. Tween é código:

func recolher_moeda():
    var tween = create_tween()
    tween.tween_property(self, "scale", Vector2.ZERO, 0.3)
    tween.tween_callback(queue_free)

AnimationPlayer pra todo o resto: qualquer animação que você consegue descrever no editor, qualquer coisa com mais de uma propriedade sincronizada, qualquer coisa que precisa de Call Method ou áudio na timeline. Na dúvida entre AnimatedSprite2D e AnimationPlayer pro personagem principal, vai de AnimationPlayer: na hora que precisar sincronizar som e hitbox (e vai precisar), você já está na ferramenta certa.

E quando o personagem cresce pra um ponto em que o if/else de qual animação tocar vira uma árvore monstruosa, o passo seguinte é o AnimationTree, que monta uma máquina de estados em cima das animações do AnimationPlayer. Mas isso é assunto pra outro artigo, e o AnimationTree não substitui nada do que está aqui: ele consome as animações que você criou no AnimationPlayer.

Fechando

O resumo honesto: o AnimationPlayer é um sequenciador de propriedades, não um trocador de frames. Quando essa ficha cai, um monte de código de polish que você escreveria na mão (lerp, timer, flag de estado) vira keyframe no editor, e o jogo fica mais fácil de ajustar porque o timing está visível numa timeline em vez de espalhado em constantes.

Pra fixar, pega um personagem que você já tem e monta o ataque completo: animação de sprite, Call Method track ligando e desligando a hitbox, audio track com o som do golpe, e o script esperando animation_finished pra voltar ao idle. É um exercício de uma tarde e cobre todo o fluxo que importa.