HUD de corações: do editor ao dano e cura
Monte três corações com Image, ligue à vida do jogador e confira dano, cura e zero vida.
É possível nesta versão?
Seção intitulada “É possível nesta versão?”Sim, pela composição Canvas UI + Image + Behavior C#. Image desenha o ícone e permite mudar Visible. A regra de vida é implementada no script PlayerHealth do exemplo. Não existe um componente pronto de “HUD de corações” ou binding automático de vida no snapshot. Este tutorial usa três corações inteiros; meio coração/preenchimento radial exigem outra composição, e Image não tem fillAmount.
O resultado e a árvore
Seção intitulada “O resultado e a árvore”Jogador HUD (objeto da cena)└─ PlayerHealth ├─ Canvas UI → UI/hud.aeui └─ HeartHud → Player = Jogador
UI/hud.aeui (documento)├─ Hearts (Panel, sem fundo)│ ├─ Empty1 (Image vazio) ← fica visível│ ├─ Heart1 (Image cheio) ← script liga/desliga│ ├─ Empty2 (Image vazio)│ ├─ Heart2 (Image cheio)│ ├─ Empty3 (Image vazio)│ └─ Heart3 (Image cheio)├─ Status (Text)├─ Damage (Button)└─ Heal (Button)Os seis Images ficam sobrepostos em pares. Ao esconder Heart2, Empty2 continua desenhado na mesma posição. Use Panel com layout manual aqui; esconder filhos diretos de HBox pode reorganizar a fileira. A ordem é relevante: vazio primeiro, cheio depois.



1. Prepare os recursos
Seção intitulada “1. Prepare os recursos”Adicione Images/heart-full.png e Images/heart-empty.png dentro do projeto. Use duas imagens 64 × 64 com alpha; ambas precisam existir e ser legíveis. Baixe o pacote do tutorial: inclui UI/hud.aeui, PNGs, scripts e instruções, para integrar num projeto existente. Abra e salve o .aeui pela área Interface para registrá-lo como recurso, ou siga a autoria manual abaixo. As duas imagens já aparecem no documento autoral, então o atlas conhece os dois recursos antes de esconder uma camada.
2. Crie o documento e preencha o layout
Seção intitulada “2. Crie o documento e preencha o layout”Siga o caminho de autoria e salve UI/hud.aeui. Crie Panel e renomeie para Hearts. Selecionar Panel antes de criar Image faz os filhos pertencerem a ele. Todos usam âncoras inicial e final (0,0).
| Elemento | Pai | Bordas: esquerda, topo, direita, base | Recurso / texto |
|---|---|---|---|
| Hearts, Panel | raiz | 24,24,184,72 | Desenhar fundo desligado |
| Empty1, Image | Hearts | 0,0,48,48 | Images/heart-empty.png |
| Heart1, Image | Hearts | 0,0,48,48 | Images/heart-full.png |
| Empty2, Image | Hearts | 56,0,104,48 | Images/heart-empty.png |
| Heart2, Image | Hearts | 56,0,104,48 | Images/heart-full.png |
| Empty3, Image | Hearts | 112,0,160,48 | Images/heart-empty.png |
| Heart3, Image | Hearts | 112,0,160,48 | Images/heart-full.png |
| Status, Text | raiz | 24,80,320,116 | Vida: 3/3 |
| Damage, Button | raiz | 24,124,124,168 | Dano |
| Heal, Button | raiz | 136,124,236,168 | Cura |
Para os Images, use Ajuste = Conter, Tinta = branco, Visível = ligado, Clicável = desligado e sem animação. Panel Hearts deve ter fundo transparente. Damage e Heal usam ação Notificar; não os configure para alterar Value do Image. Após criar os filhos, selecione a raiz/limpe a seleção antes de criar Status e os botões. Salve UI.
3. Configure a cena
Seção intitulada “3. Configure a cena”- Crie HUD, adicione Canvas UI, atribua UI/hud.aeui em Documento UI, use Tela e Habilitado.
- Crie ou escolha Jogador. Ele pode ser só um objeto vazio para a demonstração por botão; física não é necessária nessa etapa.
- Adicione os scripts abaixo na área de código, compile e anexe PlayerHealth a Jogador e HeartHud a HUD.
- No Inspector do HeartHud, campo Player, escolha o objeto Jogador. Não escolha um Image da árvore UI: o campo é uma referência a objeto da cena.
- Salve a cena e a UI. Receptor/câmera de entrada do Canvas podem ficar sem atribuição nesta demonstração visual.
4. Regra de vida: PlayerHealth.cs
Seção intitulada “4. Regra de vida: PlayerHealth.cs”using System;using Astra;
[ComponentId("docs.player-health")]public sealed class PlayerHealth : Behavior{ public const int Capacity = 3; public int Current { get; private set; } public bool IsDead => Current == 0;
public override void Awake() => Current = Capacity;
public void TakeDamage(int amount) { Current = Math.Max(0, Current - Math.Clamp(amount, 0, Capacity)); }
public void Heal(int amount) { Current = Math.Min(Capacity, Current + Math.Clamp(amount, 0, Capacity)); }}Awake começa com três corações. TakeDamage e Heal aceitam quantidades não negativas, limitadas à capacidade. Zero vida apenas muda o estado IsDead; este exemplo não inventa morte, respawn, animação ou persistência de save.
5. Ligar o estado às imagens: HeartHud.cs
Seção intitulada “5. Ligar o estado às imagens: HeartHud.cs”using System;using Astra;
[ComponentId("docs.heart-hud")]public sealed class HeartHud : Behavior{ [PropertyId("player")] public ObjectReference Player;
private GuiAccess hud = null!; private PlayerHealth health = null!; private readonly GuiElement[] hearts = new GuiElement[PlayerHealth.Capacity]; private GuiElement status; private int lastHealth = -1;
public override void Start() { var player = Resolve(Player) ?? throw new InvalidOperationException("Atribua Jogador ao campo Player do HeartHud."); health = player.GetBehavior<PlayerHealth>() ?? throw new InvalidOperationException("Jogador precisa de PlayerHealth."); hud = Gui.ForCanvas(Object); for (int i = 0; i < hearts.Length; ++i) { hearts[i] = hud.Find($"Heart{i + 1}"); if (hearts[i].Kind != GuiKind.Image) throw new InvalidOperationException($"Heart{i + 1} precisa ser Image."); } status = hud.Find("Status"); hud.Find("Damage"); hud.Find("Heal"); Refresh(); }
public override void Update(float deltaTime) { // Este script é o consumidor da fila deste Canvas. while (hud.Poll(out var message)) { if (message.Kind != GuiEventKind.Click) continue; if (message.Element.Name == "Damage") health.TakeDamage(1); else if (message.Element.Name == "Heal") health.Heal(1); } if (lastHealth != health.Current) Refresh(); }
private void Refresh() { lastHealth = health.Current; for (int i = 0; i < hearts.Length; ++i) hearts[i].Visible = i < lastHealth; status.Text = $"Vida: {lastHealth}/{PlayerHealth.Capacity}" + (health.IsDead ? " | Sem vida" : ""); }}Gui.ForCanvas(Object) escolhe o Canvas do objeto HUD. Start resolve nomes e conserva handles somente desta execução. O script verifica os tipos dos corações e falha com mensagem quando Player/PlayerHealth estão ausentes. Update consome os botões e só escreve as imagens quando a vida muda. Não precisa procurar Heart1–3 a cada frame.
6. Dano vindo do gameplay
Seção intitulada “6. Dano vindo do gameplay”Outro Behavior pode resolver o jogador e chamar player.GetBehavior<PlayerHealth>()?.TakeDamage(1) no momento real de um ataque. Para uma zona de dano, o arquivo abaixo usa TriggerEnter, uma vez por entrada, não TriggerStay a cada passo:
using Astra;
[ComponentId("docs.heart-damage-trigger")]public sealed class HeartDamageTrigger : Behavior{ public override void TriggerEnter(ObjectReference other) { Resolve(other)?.GetBehavior<PlayerHealth>()?.TakeDamage(1); }}Baixar HeartDamageTrigger.cs. Anexe à zona com corpo/colisor 3D em modo sensor. O outro objeto precisa participar da física 3D e carregar PlayerHealth. Consulte Corpo físico, Colisor 3D e eventos físicos. Eventos 2D têm contrato próprio; não substitua assinaturas por suposição. O script não define cooldown nem identifica filhos sem PlayerHealth: coloque o Behavior no objeto resolvido pelo evento ou adapte a busca explicitamente.
7. Conferir o resultado
Seção intitulada “7. Conferir o resultado”| Ação em Play | Vida esperada | HUD esperado |
|---|---|---|
| Iniciar | 3 | três cheios, texto Vida: 3/3 |
| Dano | 2 | dois cheios e um vazio |
| Dano duas vezes | 0 | três vazios, texto Sem vida |
| Dano em zero | 0 | continua zero |
| Cura | 1 | um cheio e dois vazios |
| Cura até o máximo | 3 | não ultrapassa três |
| Parar e reabrir Play | 3 | reinicia, documento autoral preservado |
Interagir não executa esses scripts. Use Play para validar a ligação com a vida. Se a fonte recarregar durante Play, resolva novos handles ou reinicie.
Os três scripts deste tutorial compilaram sem erros contra o SDK do APK publicado. O documento do pacote foi gravado e relido pelo serializador nativo: formato AEUI 5, dez elementos. Isso verifica contratos e formato; o roteiro acima ainda precisa de aceite no Play e no aparelho. Consulte o manifesto de exemplos para a evidência de compilação.
Responsividade, limite e roadmap
Seção intitulada “Responsividade, limite e roadmap”Os corações usam 160 × 48 px e margem 24 px no viewport Tela. Eles conservam tamanho e posição no canto superior esquerdo, sem assumir escala por resolução de referência. Confira landscape pequeno e tablet, notch/área segura, contraste e espaço da cena. Safe area automática e escala responsiva completa continuam em R5. Para fileira flexível, use HBox com slots Panel de tamanho preferido 48 × 48 e as duas Images dentro de cada slot.
Meios corações podem ser autorados com imagens adicionais e regra de meio ponto; nine-slice, fill radial, binding automático, timeline e templates completos continuam no roadmap. Vida persistente entre sessões pede SaveStore e regra explícita de save/load; não é consequência de salvar UI. Para erros, veja diagnóstico de Image e HUD.