Animation Events na Unity: Dano, Passos e Som no Frame Certo
Um Animation Event na Unity é um marcador colocado num frame de um clip de animação que chama uma função de um script quando a animação passa por ali. É a forma certa de aplicar dano no frame exato em que a espada encosta, tocar o som de passo quando o pé bate no chão e liberar o próximo ataque de um combo, tudo sincronizado com o que o jogador vê, sem WaitForSeconds chutado.
Aqui você vai criar eventos na janela Animation e em clips importados de FBX, escrever o script receptor em C# para Unity 6.3 e resolver os dois problemas mais comuns: o aviso de "has no receiver" e o evento que dispara duas vezes.
O que são Animation Events na Unity
Cada evento guarda três coisas: o tempo no clip, o nome da função e, opcionalmente, um parâmetro. Quando o Animator toca aquele clip e passa pelo tempo marcado, a Unity procura, nos componentes do mesmo GameObject que tem o Animator, um método com aquele nome e chama.
A função pode ter zero ou um parâmetro, e esse parâmetro precisa ser de um destes tipos: float, int, string, uma referência de Object ou um AnimationEvent. Esse último carrega todos os campos de uma vez e mais algumas informações sobre o clip que disparou.
Por que usar evento e não tempo fixo no script: a animação é a fonte da verdade do timing. Se o animador ajustar o golpe para ter mais antecipação, ou se você acelerar o ataque com o multiplicador de velocidade do estado, o evento anda junto. Uma corrotina de 0,3 segundos não.
Adicionando um evento na janela Animation
Para clips criados na Unity (ou clips já duplicados para o seu projeto):
- Selecione o GameObject com o Animator e abra Window > Animation > Animation.
- Escolha o clip na lista do canto superior esquerdo.
- Mova a agulha até o frame desejado. Use as setas ou o campo de frame para cair exatamente no frame do impacto.
- Clique no botão Event (o ícone com um marcador e um "+"), ao lado do botão de keyframe. O evento é criado na posição da agulha. Outra forma: clique com o botão direito na linha de eventos e escolha Add Animation Event.
- Com o marcador selecionado, o Inspector mostra o campo Function e os campos de parâmetro. Se o script receptor já está no objeto, o campo vira uma lista com os métodos disponíveis.
Para reposicionar, arraste o marcador pela linha de eventos. Para apagar, selecione e aperte Delete.
Escrevendo a função receptora no script
O script precisa estar no mesmo GameObject que o Animator. Essa é a regra que mais derruba gente: o Animator está no modelo filho, o script de combate está no objeto pai, e o evento não encontra ninguém.
A função pode ser pública ou privada. Prefira nomes que descrevem o momento do clip, e não o efeito, porque o mesmo momento pode disparar várias coisas: OnAttackImpact, OnFootstep, OnAttackRecoveryStart.
Uma organização que funciona: um script "receptor" no objeto do Animator, que só repassa para os sistemas de verdade. Assim o combate e o áudio não precisam morar no modelo.
Quando usar evento e quando usar um StateMachineBehaviour: o evento marca um frame dentro do clip ("a lâmina encostou"). O StateMachineBehaviour, que você adiciona no estado pelo botão Add Behaviour, reage à entrada, permanência e saída do estado inteiro ("começou o ataque", "saiu do ataque"). Se a informação depende da pose, é evento. Se depende só de estar ou não no estado, como travar o movimento durante qualquer ataque, o StateMachineBehaviour é mais robusto, porque dispara mesmo se a transição cortar o clip antes do frame marcado.
Exemplo 1: dano no frame do golpe
Arquivo Damageable.cs, o componente que recebe dano:
using UnityEngine;
public class Damageable : MonoBehaviour
{
[SerializeField] private int maxHealth = 30;
private int currentHealth;
private void Awake()
{
currentHealth = maxHealth;
}
public void TakeDamage(int amount)
{
currentHealth = Mathf.Max(currentHealth - amount, 0);
Debug.Log($"{name} levou {amount}. Vida: {currentHealth}");
}
}
Arquivo AttackAnimationEvents.cs, no mesmo objeto do Animator:
using System.Collections.Generic;
using UnityEngine;
public class AttackAnimationEvents : MonoBehaviour
{
[SerializeField] private Transform hitPoint;
[SerializeField] private float hitRadius = 0.6f;
[SerializeField] private LayerMask targetLayers;
[SerializeField] private int baseDamage = 10;
private bool canChainAttack;
private readonly HashSet<Damageable> alreadyHit = new HashSet<Damageable>();
public bool CanChainAttack => canChainAttack;
// Evento no frame em que a lâmina encosta. Parâmetro int = multiplicador do golpe.
private void OnAttackImpact(int damageMultiplier)
{
alreadyHit.Clear();
Collider[] hits = Physics.OverlapSphere(hitPoint.position, hitRadius, targetLayers);
foreach (Collider hit in hits)
{
Damageable target = hit.GetComponentInParent<Damageable>();
if (target != null && alreadyHit.Add(target))
{
target.TakeDamage(baseDamage * damageMultiplier);
}
}
}
// Evento no começo da recuperação: a partir daqui o próximo golpe pode ser encadeado.
private void OnAttackRecoveryStart()
{
canChainAttack = true;
}
// Evento no frame 0 do ataque.
private void OnAttackStart()
{
canChainAttack = false;
}
private void OnDrawGizmosSelected()
{
if (hitPoint == null) return;
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(hitPoint.position, hitRadius);
}
}
Crie um objeto vazio filho da mão ou da arma chamado HitPoint e arraste para o campo. No clip de ataque, adicione três eventos: OnAttackStart no frame 0, OnAttackImpact no frame em que a lâmina cruza o alvo (com Int = 1 no primeiro golpe e 2 no golpe final do combo) e OnAttackRecoveryStart logo depois do impacto.
O HashSet impede que um inimigo com vários colliders leve dano duas vezes no mesmo golpe. O gizmo vermelho mostra a área do golpe na Scene view: pause no frame do evento e confira se a esfera está onde a lâmina está.
Essa divisão entre início, impacto e recuperação é a mesma ideia de startup, active e recovery do sistema de combate corpo a corpo. O post mostra a lógica em Godot, mas o raciocínio é idêntico.
Combate, animação e áudio sincronizados no mesmo frame é o ponto em que muito projeto solo trava: cada sistema funciona isolado e o conjunto parece desencontrado. No curso de Unity, isso é montado em sequência dentro de um jogo completo, do personagem que anda até o golpe que acerta no frame certo.
Exemplo 2: som de passos sincronizado
Arquivo FootstepAnimationEvents.cs:
using UnityEngine;
[RequireComponent(typeof(AudioSource))]
public class FootstepAnimationEvents : MonoBehaviour
{
[SerializeField] private AudioClip[] footstepClips;
[SerializeField] private float minPitch = 0.92f;
[SerializeField] private float maxPitch = 1.08f;
[SerializeField] private float minBlendWeight = 0.5f;
private AudioSource audioSource;
private void Awake()
{
audioSource = GetComponent<AudioSource>();
}
private void OnFootstep(AnimationEvent animationEvent)
{
// Durante uma mistura, os eventos dos dois clips disparam.
// Só toca o passo do clip que domina a mistura.
if (animationEvent.animatorClipInfo.weight < minBlendWeight) return;
if (footstepClips.Length == 0) return;
AudioClip clip = footstepClips[Random.Range(0, footstepClips.Length)];
audioSource.pitch = Random.Range(minPitch, maxPitch);
audioSource.PlayOneShot(clip, animationEvent.floatParameter > 0f ? animationEvent.floatParameter : 1f);
}
}
Em cada clip de locomoção, coloque um evento OnFootstep em cada frame em que um pé encosta no chão (normalmente dois por ciclo). No campo Float, use 0,6 para andar e 1 para correr: o script usa como volume.
O parâmetro do tipo AnimationEvent é o que torna esse exemplo útil. Numa Blend Tree de andar e correr, os dois clips tocam ao mesmo tempo, e sem o filtro por animatorClipInfo.weight você ouviria quatro passos por ciclo. A variação de pitch e a escolha aleatória entre três ou quatro clips evitam o efeito metralhadora do mesmo som repetido. O post de sound design e efeitos sonoros ajuda a gravar ou escolher esses clips.
Animation Events em clip importado (FBX e Mixamo)
Clips dentro de um FBX são somente leitura: na janela Animation eles aparecem travados. Você tem dois caminhos.
Pelas Import Settings (recomendado). Selecione o arquivo FBX no Project, vá na aba Animation do Inspector, escolha o clip na lista, role até a seção Events e expanda. Mova a agulha da prévia até o frame, clique em Add Event (o botão com um marcador) e preencha Function e parâmetros. Clique em Apply no fim do Inspector. O evento fica salvo nos metadados do FBX: se você reimportar o modelo com a animação atualizada, os eventos continuam lá.
Duplicando o clip. Selecione o clip dentro do FBX e aperte Ctrl+D para gerar uma cópia .anim editável. Funciona, mas a cópia não acompanha mudanças futuras no FBX. Use só se você precisa editar curvas também.
Para animações do Mixamo, o primeiro caminho é o padrão: baixe, importe, configure o Rig, e adicione os eventos nas Import Settings.
Erros comuns: 'has no receiver' e evento disparando duas vezes
"AnimationEvent 'OnAttackImpact' has no receiver! Are you missing a component?" Essa é a forma da mensagem no Console. Significa que nenhum componente no GameObject do Animator tem um método com esse nome. Causas, da mais comum para a menos:
- O script está no pai ou num filho, e não no objeto do Animator.
- O nome no evento difere do método (maiúscula, espaço sobrando, typo).
- O evento é sobra: um
NewEventcriado sem querer. Procure marcadores vazios no clip. - Existe um segundo Animator num objeto filho tocando o mesmo clip. Remova o Animator duplicado.
Evento disparando duas vezes. As causas são outras:
- Mistura de transição ou Blend Tree: durante a mistura, o clip de origem e o de destino tocam juntos e ambos disparam seus eventos. Filtre pelo
animatorClipInfo.weight, como no script de passos, ou reduza a Transition Duration. - Evento no último frame de clip em loop: no frame final e no frame 0 do loop, a Unity pode passar pelos dois tempos quase juntos. Coloque o evento um pouco antes do fim.
- Dois eventos no mesmo tempo: um marcador em cima do outro parece um só. Arraste para conferir.
- Clip reiniciado por transição para o mesmo estado: um trigger em Any State com Can Transition To Self ligado reinicia o clip e passa pelo evento de novo.
Próximo passo
Pegue o ataque do seu personagem e mova todo timing do script para eventos: início, impacto, recuperação. Pause no frame do impacto e confira a esfera do gizmo sobre a lâmina. Depois faça o mesmo com os passos. Para entender os estados e transições onde esses clips vivem, siga para o guia do Animator Controller do zero.
Perguntas frequentes
Que tipo de parâmetro uma função de Animation Event aceita?
A função pode não ter parâmetro ou ter um único parâmetro do tipo float, int, string, uma referência de Object ou um AnimationEvent. Para receber vários valores de uma vez, use AnimationEvent como parâmetro e leia os campos floatParameter, intParameter, stringParameter e objectReferenceParameter.
Por que aparece AnimationEvent has no receiver?
Porque nenhum script no mesmo GameObject do Animator tem um método público ou privado com aquele nome exato. Confira se o script está no objeto que tem o Animator, e não num filho ou no pai, e se o nome no evento é idêntico ao do método.
Como adicionar Animation Event em animação do Mixamo ou FBX?
Clips importados são somente leitura na janela Animation. Selecione o FBX, abra a aba Animation no Inspector, escolha o clip, expanda a seção Events, posicione o tempo e clique em Add Event. Depois clique em Apply.
Animation Event ou corrotina com tempo fixo para dano?
Animation Event acompanha a animação: se você trocar o clip ou mudar a velocidade, o dano continua no frame do impacto. Uma corrotina com segundos fixos dessincroniza assim que a animação muda. Para tudo que precisa bater com o que o jogador vê, prefira o evento.



