Gostaria de saber como eu faço pra animar por exemplo 3 golpes diferentes dentro de um combo, como eu interligaria essas 3 animações.
E na parte de programação como eu checaria se o primeiro e segundo golpe foi dado.
Essa é uma pergunta bastante abrangente, e existem várias abordagens para implementar um sistema de combos. Vou apresentar uma solução básica que une a configuração do Animator com uma lógica simples em C# para detectar os inputs do jogador e acionar as animações correspondentes.
1. Configurando o Animator
- Crie os estados de animação:
- Importe as animações dos seus 3 golpes e crie 3 states no Animator (por exemplo,
Attack1
,Attack2
eAttack3
).
- Importe as animações dos seus 3 golpes e crie 3 states no Animator (por exemplo,
- Defina parâmetros para controlar as transições:
- Uma estratégia comum é usar Triggers (por exemplo,
Attack1Trigger
,Attack2Trigger
,Attack3Trigger
) ou um parâmetro do tipo int (por exemplo,ComboStep
) para indicar qual ataque deve ser executado.
- Uma estratégia comum é usar Triggers (por exemplo,
- Crie as transições:
- Configure a transição do estado
Idle
paraAttack1
usando a condição de disparo do trigger ou verificando seComboStep == 1
. - Configure a transição de
Attack1
paraAttack2
(condição: trigger ouComboStep == 2
). - Configure a transição de
Attack2
paraAttack3
(condição: trigger ouComboStep == 3
).
- Configure a transição do estado
- Ajuste os tempos e condições:
- Defina o Exit Time e a Transition Duration para garantir uma transição suave entre as animações.
2. Lógica de Controle no Script
No script, você vai detectar os inputs do jogador e verificar se eles ocorrem dentro de um período adequado para manter o combo. Veja um exemplo simples em C#:
using UnityEngine;
public class ComboController : MonoBehaviour
{
public Animator animator; // Referência ao Animator do personagem
public float comboDelay = 1f; // Tempo máximo entre ataques para manter o combo
private int comboStep = 0; // Contador para saber qual golpe é o atual
private float lastAttackTime = 0f; // Tempo do último ataque
void Update()
{
// Verifica se o botão de ataque foi pressionado (substitua "Fire1" pelo input desejado)
if (Input.GetButtonDown("Fire1"))
{
// Se o tempo desde o último ataque for maior que o delay, o combo reinicia
if (Time.time - lastAttackTime > comboDelay)
{
comboStep = 0;
}
// Incrementa o contador do combo
comboStep++;
lastAttackTime = Time.time;
// Dependendo do passo do combo, aciona o trigger correspondente no Animator
switch (comboStep)
{
case 1:
animator.SetTrigger("Attack1Trigger");
break;
case 2:
animator.SetTrigger("Attack2Trigger");
break;
case 3:
animator.SetTrigger("Attack3Trigger");
// Após o terceiro ataque, o combo pode ser reiniciado (ou continuar se desejar)
comboStep = 0;
break;
default:
// Se necessário, trate casos adicionais ou resete o combo
comboStep = 0;
break;
}
}
}
}
- Controle de Combo:
Cada vez que o botão de ataque é pressionado, verifica-se se o tempo decorrido desde o último ataque é menor quecomboDelay
. Caso contrário, o combo é reiniciado (isto é,comboStep
volta a 0). - Acionamento das Animações:
Dependendo do valor decomboStep
, o script aciona um trigger diferente no Animator para executar a animação correspondente. - Aplicação de Dano:
Para isso você pode adicionar eventos dentro do clip de animação, no momento que acontecer o Hitbox (já falei no curso como adicionar eventos em animações).
Não sei se essa solução vai te atender, mas acredito que pode ser um caminho inicial para esse tipo de mecânica que você quer aplicar.
Fiz tudo mas os 3 triggers estão ativando sozinhos. Estou usando tanto o trigger quanto o int ComboStep.
Como está seu código? Mostra aqui também uns prints do seu cenário e do Animator do Personagem, para eu entender melhor sua demanda.
Você disse que está usando a lógica de trigger e de int também, no caso deve-ser obtar por apenas uma no Animator.
Você pode deletar o parâmetro int que está no animator e usar o parâmetro trigger. Pelo menos foi o que eu entendi. Assim talvez funciona.
Ainda não resolveu, eu mexi no código mas ficou confuso.
Professor, pode me mostrar o código só usando triggers ou int.
Pq nesse tava tudo junto
Eu estava justamente dando uma olhada sobre esse assunto agora. Fiz um exemplo usando apenas Int. Ele funciona assim:
- O jogador pode pressionar a tecla F para iniciar um ataque.
- Se o jogador pressionar F durante um ataque, 0.5 segundos antes do final da animação atual, o combo é encadeado para o próximo ataque.
- Caso o tempo entre os ataques seja maior do que esse intervalo (ou seja, se o jogador não pressionar F a tempo), o combo se encerra e o sistema reseta para um novo ciclo.
Segue vídeo mostrando o funcionamento:
Segue mais alguns prints:
Vou deixar abaixo o link do AnimatorController que usei, para que você possa observar o fluxo de transições:
Por fim, o script:
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[Header("Configurações de Movimento")]
[Tooltip("Velocidade de movimento do personagem.")]
public float moveSpeed = 2f;
[Tooltip("Velocidade de rotação do personagem.")]
public float rotationSpeed = 10f;
[Tooltip("Força da gravidade aplicada no personagem.")]
public float gravity = -9.81f;
[Tooltip("Referência à câmera (atribua no Inspetor ou ela será buscada automaticamente pela MainCamera).")]
public Transform cameraTransform;
private CharacterController characterController;
private Animator animator;
private Vector3 velocity;
[Header("Configurações de Combo de Ataque")]
[Tooltip("Tempo limite (em segundos) para encadear o próximo ataque do combo.")]
public float comboResetTime = 2f;
[Tooltip("Número máximo de ataques que podem ser encadeados em um combo.")]
public int maxCombo = 3;
private float lastAttackTime;
private int comboStep;
private bool isAttacking;
void Start()
{
// Obtém os componentes necessários
characterController = GetComponent<CharacterController>();
animator = GetComponent<Animator>();
// Inicializa as variáveis do combo
comboStep = 0;
lastAttackTime = -comboResetTime;
// Caso a referência da câmera não tenha sido atribuída, busca a MainCamera
if (cameraTransform == null)
{
Camera cam = Camera.main;
if (cam != null)
{
cameraTransform = cam.transform;
}
else
{
Debug.LogError("Nenhuma câmera foi encontrada. Atribua uma câmera ao PlayerController.");
}
}
}
void Update()
{
HandleMovement();
HandleAttack();
}
/// <summary>
/// Trata da movimentação do personagem relativa à câmera e aplica a gravidade.
/// </summary>
private void HandleMovement()
{
// Bloqueia o movimento caso o personagem esteja atacando
if (isAttacking) return;
// Obtém as entradas do jogador
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
// Calcula a direção de movimento relativa à câmera
Vector3 moveDirection = GetCameraRelativeDirection(horizontal, vertical);
// Se houver movimento, rotaciona o personagem em direção do movimento
if (moveDirection.magnitude >= 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(moveDirection);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
}
// Move o personagem
Vector3 movement = moveDirection.normalized * moveSpeed;
characterController.Move(movement * Time.deltaTime);
// Aplica a gravidade
if (characterController.isGrounded && velocity.y < 0)
{
// Garante que o personagem permaneça "grudado" ao chão
velocity.y = -2f;
}
velocity.y += gravity * Time.deltaTime;
characterController.Move(velocity * Time.deltaTime);
// Atualiza o parâmetro do Animator para a animação de movimento (use o nome do parâmetro conforme configurado no Animator)
animator.SetBool("Move", moveDirection.magnitude >= 0.1f);
}
/// <summary>
/// Calcula a direção de movimento baseada nas entradas do jogador e na orientação da câmera.
/// </summary>
private Vector3 GetCameraRelativeDirection(float horizontal, float vertical)
{
// Obtém os vetores forward e right da câmera e zera a componente vertical
Vector3 camForward = cameraTransform.forward;
Vector3 camRight = cameraTransform.right;
camForward.y = 0f;
camRight.y = 0f;
camForward.Normalize();
camRight.Normalize();
// Combina as direções com as entradas do jogador
return (camForward * vertical + camRight * horizontal);
}
/// <summary>
/// Trata da lógica de ataque e gerenciamento do combo.
/// </summary>
private void HandleAttack()
{
// Se o jogador pressionar a tecla "F", executa o ataque
if (Input.GetKeyDown(KeyCode.F))
{
lastAttackTime = Time.time;
if(comboStep <= 0)
{
PerformAttack();
}
}
}
/// <summary>
/// Reseta o combo e atualiza o Animator.
/// </summary>
private void ResetAttackCombo()
{
isAttacking = false;
comboStep = 0;
animator.SetInteger("ComboIndex", comboStep);
}
/// <summary>
/// Executa um ataque, gerenciando o combo e disparando a animação correspondente.
/// </summary>
private void PerformAttack()
{
lastAttackTime = Time.time;
isAttacking = true;
comboStep++;
if (comboStep > maxCombo)
{
comboStep = 1;
}
// Atualiza os parâmetros do Animator (certifique-se de que os nomes correspondem aos parâmetros configurados)
animator.SetInteger("ComboIndex", comboStep);
}
/// <summary>
/// Chamado ao final de uma animação de ataque.
/// </summary>
public void FinishAttack()
{
if (isAttacking && Time.time - lastAttackTime > 0.5f)
{
ResetAttackCombo();
}
else
{
PerformAttack();
}
}
}
Além disso, adicionei eventos nas animações de ataque para chamar o método FinishAttack()
no script. Assim, o script avalia se o combo deve ser finalizado ou se deve encadear o próximo ataque.
Espero que esse exemplo te dê uma ideia de como implementar e expandir esse sistema de combo.
Você nao mostrou as transições entre idle e ataque1, e ataque1 idle. E acho que eu ainda nao vi evento, to no 2º Módulo.
Pode me mostrar como chega nessa aba de eventos?Ainda não cheguei nessa parte.
Opa, desculpa a demora. As trasições de animação você pode ver diretamente no meu AnimatorController, segue link:
Só você importar ele no projeto e olhar.
Em relação aos eventos de animação, basta você clicar na sua animação, que está no projeto, e ir na aba de Animações:
Após isso, basta descer no Inspetor até a aba de Events. Nesta aba você pode configurar os eventos que quer, ou seja, qual método quer chamar quanto chegar em determinado ponto da animação (para definir o ponto basta arrastar a agulha do preview até onde quer e adicionar um novo evento).
Eu preciso chamar esse evento em um script? Eu cheguei a ver o método pra aplicar sons.
Ao adicionar um evento à animação, quando a animação chegar ao ponto configurado para esse evento, o método definido será chamado.
No caso do print, será chamado o método “FinishAttack”. A Unity procurará esse método em todos os scripts do mesmo GameObject onde está o Animator e o executará.
Isso é uma ótima forma que executar ações em momentos específicos de uma animação.
Eu tenho que colocar no finishAttack o número 1?
Depois 2 e 3 (em cads golpe no caso)
Pode me mostrar a parte do script?
Não, apenas FinishAttack.
No campo de Function da evento você coloca o método que quer executar quando a animação chegar naquele momento.
Dá uma olhada no meu script acima, nele eu tenho um método de FinishAttack.
Com isso, quando a animação for tocada e chegar no ponto do evento, o método definido vai ser executado.
Nesse caso seu código ou seu Animator Controller estão configurados errados.
Se Animator está com as transições iguais as que eu coloquei nesse arquivo?
Para funcionar corretamente é fundamental as transições estajam bem definidas. Qualquer coisa grava um vídeo mostrando passo a passo seu projeto, com isso eu consigo ver com mais detalhes como está.
Eu troquei as animaçoes pelas minhas. Como envia vídeo aqui?No mesmo lugar que foto?