Barra de Vida na Unity: Slider, Image Fill e Inimigos
Para fazer uma unity barra de vida você precisa de duas coisas separadas: um script que guarda a vida e avisa quando ela muda, e um elemento de UI que mostra essa vida como fração entre 0 e 1. A UI pode ser um Slider ou uma Image com Image Type em Filled, e o valor que você passa é sempre vidaAtual / vidaMaxima. Feito assim, a mesma barra serve para o jogador no HUD e para o inimigo no mundo.
Aqui você monta os dois formatos, liga a barra por evento (sem a UI perguntar a vida todo frame), coloca uma barra em cima do inimigo em World Space e faz o efeito de barra de dano atrasada, aquele trecho de outra cor que desce depois do golpe. O código está no Unity 6.3 LTS e funciona em 2D e 3D.
Barra de vida na Unity: Slider ou Image Fill
Os dois funcionam. A diferença está no que vem de brinde.
Slider (GameObject > UI > Slider) já vem com fundo, área de preenchimento e um Handle para arrastar. Para virar barra de vida você precisa: apagar o Handle, desmarcar Interactable, colocar Navigation em None (senão o controle pode selecionar a barra) e definir Min Value 0 e Max Value 1. Ele tem uma chatice conhecida: em valor zero, dependendo das margens do Fill Area, sobra um pedacinho de cor visível.
Image Filled é uma Image comum com Image Type em Filled. Você ganha os campos Fill Method (Horizontal, Vertical, Radial 90, Radial 180, Radial 360), Fill Origin (de que lado começa) e Fill Amount (0 a 1). Zero é zero, sem sobra, e o modo Radial dá barra circular de graça. É o que eu uso para vida.
Um detalhe que trava quem tenta pela primeira vez: o campo Image Type só aparece quando a Image tem um Source Image. Sem sprite, você não acha a opção Filled. Use qualquer sprite branco, por exemplo o quadrado criado em Assets > Create > 2D > Sprites > Square, e pinte pela cor da Image.
Montando a barra do jogador no HUD
Monte esta hierarquia dentro do Canvas do HUD:
BarraVida(objeto vazio com RectTransform), ancorado no canto superior esquerdo, 300 x 28 pixels.- Filho
Fundo: Image, cor cinza escura, esticada no pai (Anchor Presets com Alt + stretch total). - Filho
Atrasada: Image, sprite branco, cor amarela, Image Type: Filled, Fill Method: Horizontal, Fill Origin: Left, esticada no pai. - Filho
Preenchimento: igual ao anterior, mas vermelho. Ele fica abaixo deAtrasadana Hierarchy, para ser desenhado por cima. - Opcional: um texto TextMeshPro com "100/100" por cima de tudo.
A ordem dos filhos importa: no uGUI, quem está mais embaixo na Hierarchy é desenhado por último, logo por cima. Desmarque Raycast Target em todas essas Images; barra de vida não precisa receber clique e pode roubar o clique de botões próximos.
Script de vida com evento de dano
O script de vida não sabe que a barra existe. Ele só anuncia "minha vida mudou". Quem quiser ouvir, ouve.
Arquivo Vida.cs:
using System;
using UnityEngine;
public class Vida : MonoBehaviour
{
[SerializeField] private int vidaMaxima = 100;
private int vidaAtual;
public event Action<int, int> VidaMudou;
public event Action Morreu;
public int VidaAtual => vidaAtual;
public int VidaMaxima => vidaMaxima;
private void Awake()
{
vidaAtual = vidaMaxima;
}
public void ReceberDano(int quantidade)
{
if (vidaAtual <= 0)
{
return;
}
vidaAtual = Mathf.Max(vidaAtual - quantidade, 0);
VidaMudou?.Invoke(vidaAtual, vidaMaxima);
if (vidaAtual == 0)
{
Morreu?.Invoke();
}
}
public void Curar(int quantidade)
{
if (vidaAtual <= 0)
{
return;
}
vidaAtual = Mathf.Min(vidaAtual + quantidade, vidaMaxima);
VidaMudou?.Invoke(vidaAtual, vidaMaxima);
}
}
Por que evento e não a barra lendo vida.VidaAtual no Update? Porque a vida muda poucas vezes por minuto e a barra não precisa trabalhar nos outros milhares de frames. E porque o mesmo evento alimenta outras coisas: som de dano, tremida de câmera, flash vermelho na tela. Se você quer o sistema completo, com invencibilidade depois do golpe e morte, o post de sistema de vida e dano na Unity aprofunda esse script. A lógica é a mesma que o sistema de vida e dano no Godot usa com sinais, então dá para comparar as duas engines lado a lado.
Barra de dano atrasada que desce suave
Agora a barra. Ela escuta o evento, pula o vermelho direto para o valor novo e deixa o amarelo descer devagar depois de uma pausa curta.
Arquivo BarraDeVida.cs:
using UnityEngine;
using UnityEngine.UI;
public class BarraDeVida : MonoBehaviour
{
[SerializeField] private Vida vida;
[SerializeField] private Image preenchimento;
[SerializeField] private Image preenchimentoAtrasado;
[SerializeField] private float esperaAntesDeDescer = 0.4f;
[SerializeField] private float velocidadeDaAtrasada = 0.8f;
private float fracaoAlvo = 1f;
private float tempoAteDescer;
private void OnEnable()
{
vida.VidaMudou += AoMudarVida;
}
private void OnDisable()
{
vida.VidaMudou -= AoMudarVida;
}
private void Start()
{
fracaoAlvo = (float)vida.VidaAtual / vida.VidaMaxima;
preenchimento.fillAmount = fracaoAlvo;
preenchimentoAtrasado.fillAmount = fracaoAlvo;
}
private void AoMudarVida(int atual, int maxima)
{
float novaFracao = (float)atual / maxima;
if (novaFracao < fracaoAlvo)
{
tempoAteDescer = esperaAntesDeDescer;
}
else
{
preenchimentoAtrasado.fillAmount = novaFracao;
}
fracaoAlvo = novaFracao;
preenchimento.fillAmount = fracaoAlvo;
}
private void Update()
{
if (preenchimentoAtrasado.fillAmount <= fracaoAlvo)
{
return;
}
if (tempoAteDescer > 0f)
{
tempoAteDescer -= Time.deltaTime;
return;
}
preenchimentoAtrasado.fillAmount = Mathf.MoveTowards(
preenchimentoAtrasado.fillAmount,
fracaoAlvo,
velocidadeDaAtrasada * Time.deltaTime);
}
}
Coloque o BarraDeVida no objeto BarraVida, arraste o jogador (com o Vida) e as duas Images. Por que Mathf.MoveTowards e não Mathf.Lerp? O MoveTowards desce a uma velocidade constante (0.8 da barra por segundo, no exemplo), então um golpe de 10% e um de 50% demoram tempos proporcionais, e o jogador lê o tamanho do dano. O Lerp com Time.deltaTime desacelera no fim e nunca chega exatamente ao alvo, o que deixa uma lasquinha amarela parada. A espera de 0.4 s antes de descer é o que dá a sensação de impacto: o jogador vê o tamanho do estrago antes dele sumir. Esse tipo de detalhe é o que o post de game feel e juice chama de feedback, e faz o golpe parecer mais pesado sem mudar um número do combate.
Na cura, a barra atrasada pula junto com a principal. Se preferir, inverta a lógica para cura: amarelo pula, vermelho sobe devagar.
Até aqui são dois scripts e meia dúzia de Images. O trabalho de verdade aparece quando isso entra num jogo: vinte inimigos com barra, o chefe com barra grande no topo, o jogador com HUD, tudo atualizando por evento sem vazar referência quando o inimigo morre. É juntar as peças num projeto inteiro, sozinho, que costuma travar.
Barra de vida em cima do inimigo (World Space)
Para o inimigo, a barra fica no mundo, presa a ele.
- Clique com o botão direito no prefab do inimigo e crie UI > Canvas como filho.
- No Canvas, mude Render Mode para World Space.
- Ajuste o RectTransform para 100 x 12 e a Scale para 0.01 nos três eixos. Assim a barra tem 1 unidade de largura no mundo, que é a escala de um sprite de 100 pixels por unidade ou de um personagem 3D de 1 metro.
- Posicione o Canvas acima da cabeça (Pos Y em torno de 1.2 a 2, dependendo do modelo).
- Copie para dentro dele a mesma hierarquia
Fundo/Atrasada/Preenchimentoe adicione oBarraDeVidaapontando para oVidado próprio inimigo.
Como o script é o mesmo, não há código novo para o inimigo. Num jogo com muitos inimigos, vale esconder a barra enquanto a vida está cheia (desligue o componente Canvas com canvas.enabled = false no Start e ligue no primeiro dano; não desative o GameObject, senão o OnDisable do BarraDeVida cancela a inscrição e a barra para de ouvir o evento) para a tela não virar um mar de barras.
Funciona em 2D e 3D: o que muda
Em 3D, a barra precisa olhar para a câmera, ou o jogador vê de lado (uma linha fina) quando o inimigo gira.
Arquivo OlharParaCamera.cs:
using UnityEngine;
public class OlharParaCamera : MonoBehaviour
{
private Camera cameraPrincipal;
private void Start()
{
cameraPrincipal = Camera.main;
}
private void LateUpdate()
{
if (cameraPrincipal == null)
{
return;
}
transform.rotation = cameraPrincipal.transform.rotation;
}
}
Coloque no Canvas da barra. O LateUpdate roda depois que o inimigo e a câmera já se moveram no frame, então a barra não treme. Copiar a rotação da câmera deixa a barra paralela à tela, que é o que você quer em UI.
Em 2D, a câmera ortográfica olha sempre na mesma direção, então não precisa do script. O cuidado é outro: o Canvas em World Space precisa aparecer na frente dos sprites. No Canvas, ajuste Sorting Layer e Order in Layer para ficar acima da camada dos personagens.
Erros comuns: barra invertida, olhando para trás e não atualizando
Barra que esvazia para o lado errado. No Image Filled, troque o Fill Origin de Left para Right. No Slider, é o campo Direction (Left To Right ou Right To Left).
Barra olhando para trás ou espelhada. Você usou transform.LookAt(camera.transform). O LookAt aponta o eixo Z do Canvas para a câmera, e a frente do Canvas é o lado contrário, então você vê a barra de costas. Use a cópia de rotação do script acima.
Barra do inimigo 2D invertendo junto com ele. Se o inimigo vira trocando o Scale X para -1, todo filho vira também, e a barra esvazia para o lado oposto. Vire o inimigo pelo Flip X do SpriteRenderer, ou deixe a barra fora do objeto que inverte.
Barra que não atualiza. Três suspeitos: divisão inteira (atual / maxima com dois int dá 0 ou 1, por isso o (float) no código), o campo vida do BarraDeVida apontando para outro objeto (um prefab no Project em vez da instância da cena) ou o Image Type que voltou para Simple, que ignora o Fill Amount.
Próximo passo: transformar a barra em game feel
Com a barra funcionando, pratique variações no mesmo código: barra radial para cooldown de habilidade (Fill Method Radial 360), barra de escudo sobreposta à de vida e um número de dano saltando do inimigo a cada golpe. Para mostrar o número da vida ao lado da barra, use um TMP_Text e o SetText com formato, como SetText("{0}/{1}", atual, maxima), dentro do mesmo AoMudarVida: o texto só muda quando a vida muda e não gera lixo de memória.
Perguntas frequentes
É melhor fazer barra de vida com Slider ou com Image Fill na Unity?
Image com Image Type Filled é mais simples e mais limpa para barra de vida: não tem handle, não reage a clique e chega a zero sem sobrar pedaço. O Slider serve quando você já tem um prefab pronto ou quer a barra arrastável num menu de opções.
Por que minha barra de vida fica cheia ou vazia e não muda no meio?
Quase sempre é divisão inteira. vidaAtual / vidaMaxima com dois int dá 0 ou 1. Converta para float antes de dividir: (float)vidaAtual / vidaMaxima.
Como fazer a barra de vida do inimigo sempre olhar para a câmera em 3D?
Use um Canvas em World Space como filho do inimigo e, no LateUpdate, copie a rotação da câmera para o Canvas: transform.rotation = camera.transform.rotation. Usar LookAt na câmera deixa a barra espelhada.
Como fazer a barra de vida descer devagar depois do dano?
Use duas Images sobrepostas. A da frente pula direto para o valor novo e a de trás, de outra cor, espera uma fração de segundo e desce com Mathf.MoveTowards até alcançar a da frente.



