MericKarabulut/unity-ui-toolkit

Crie e depure interfaces de jogo no Unity usando UI Toolkit (UXML + USS + controladores C#) para qualquer gênero e plataforma - HUDs, barras de vida/energia/munição, prompts de interação, vinhetas de dano, menus, telas de pausa, contadores de pontuação, telas de cutscene. Abrange o fluxo mais recente do PanelRenderer (sem UIDocument) e o clássico UIDocument, escrevendo um guia de estilo de UI por jogo, layout sem adivinhação de pixels e correção de problemas no UI Builder / USS / UXML. Use sempre que o usuário mencionar Unity UI, UI Toolkit, UXML, USS, PanelRenderer, UIDocument, UI Builder, HUD, barra de vida, contador de munição ou menu, ou quiser reestilizar, corrigir ou adicionar qualquer interface no Unity, mesmo que nunca diga "UI Toolkit".

O que é unity-ui-toolkit?

Este skill fornece uma metodologia completa para construir e depurar interfaces de jogo no Unity usando UI Toolkit, com foco em HUDs, menus e outros elementos de UI. Ele descreve uma arquitetura onde a estrutura é definida em UXML, a aparência em USS e o comportamento em pequenos controladores C# que reagem a eventos do jogo, promovendo consistência visual através de um guia de estilo. O skill detalha o workflow do `PanelRenderer` (sem `UIDocument`) e o clássico `UIDocument`, explicando como encontrar e se inscrever em eventos de scripts do jogo, evitar erros comuns como `NullReferenceException` e configurar o layout flexível sem adivinhação de pixels. Inclui snippets reutilizáveis para elementos comuns (segmentos, pulsos, flashes, vinhetas), diretrizes específicas para gêneros (arcade, retrô) e plataformas (mobile, console, VR), e uma tabela de solução de problemas que aborda desde elementos não estilizados até scripts que não podem ser adicionados. O skill funciona com qualquer assistente de codificação ou agente IA que edite código, e é adequado para projetos Unity de qualquer gênero e plataforma, incluindo HUDs, telas de menu e cutscenes.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/MericKarabulut/claude-skills/tree/HEAD/skills/unity-ui-toolkit

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

UI Toolkit Unity para UI de jogo

Estrutura em UXML, aparência em USS, comportamento em pequenos controladores C# que reagem a eventos do jogo. Destilado de um projeto real (HUD de terror-tiro); a lista de solução de problemas é a parte que mais provavelmente economizará tempo.

1. Antes de escrever código

Pergunte apenas o que está faltando, em um lote curto:

  • API de UI. [RequireComponent(typeof(PanelRenderer))] + RegisterUIReloadCallback = workflow PanelRenderer (sem UIDocument, sem rootVisualElement). UIDocument + rootVisualElement = clássico. Infira a partir do código colado; se não estiver claro, peça um controlador funcional e seu UXML. Nunca misture os dois (causa NullReferenceException). Um arquivo funcional do usuário é melhor que sua memória da API.
  • Gênero/tema (com capturas de tela ou paleta), plataforma e entrada, resolução/aspecto de referência.
  • Os scripts do jogo que a UI escuta. Peça por eles, leia os eventos/propriedades públicas reais, vincule-se a eles. Se um evento estiver faltando, proponha a menor adição ao script do jogo.
  • Responda no idioma do usuário; escreva comentários de código no idioma que os scripts existentes usam.

2. Guia de estilo primeiro

Consistência é o que os usuários notam primeiro. Antes de construir muitas partes: proponha um guia curto, construa UMA parte pequena, deixe o usuário reagir e transforme o feedback deles em regras ("sem caixa de fundo", "mais compacto"). Salve-o no projeto (por exemplo, Assets/UI/UI_STYLE_GUIDE.md) e diga ao usuário para colá-lo em futuras solicitações de UI. Atualize-o quando partes ou regras mudarem. Conteúdo: linguagem de design (tom, contêineres, densidade, movimento), tabela de paleta (texto, suave, aviso, crítico, sombra), tipografia (tamanhos por função, regra de sombra), layout (zonas, margens, Configurações de Painel), nomes de classes e estados no USS, regras de arquitetura de código, pegadinhas do UXML, lista de partes existentes, checklist para uma nova parte.

3. Arquitetura

  • Uma parte = um controlador + um UXML + um USS, com uma fonte de dados. Sem mega-controlador.
  • Partes que devem ficar juntas compartilham um contêiner flex UXML (dois controladores podem compartilhar um GameObject/PanelRenderer; cada um encontra seus próprios elementos por nome).
  • Controladores apenas encontram elementos, definem texto/valores e alternam classes USS. Cores, tamanhos e animações ficam no USS.
  • PanelRenderer: a raiz chega via RegisterUIReloadCallback(OnUIReload) (registre em OnEnable, cancele em OnDisable). Ele é acionado quando a UI está pronta e a cada recarregamento, então TODAS as buscas Q<T>("nome"), cache, vinculação de eventos e redefinição de estado vão lá. Faça proteção nula em Update() até que os elementos existam.
  • Eventos do jogo podem disparar antes da UI existir: armazene em cache os valores mais recentes em campos, reaplique no final de OnUIReload.
  • Objetos do jogador/jogo podem surgir tarde: referência serializada, senão FindFirstObjectByType limitada (~0,5 s), então vincule; cancele inscrição em OnDisable.
  • Empurre, não pergunte. Inscreva-se em eventos e escreva apenas na mudança. Consulte apenas valores sem evento (energia contínua, um bool de recarga), alternando classes na mudança. Sem construção de string por quadro.
  • Construa elementos repetidos (pips, segmentos) no código uma vez; reconstrua apenas quando sua fonte mudar; limpe listas em cache no recarregamento.
  • Temporizadores/pulsos: element.schedule.Execute(...).Every(ms) criado pausado, retomado apenas enquanto necessário. Flashes de um disparo: .StartingIn(ms).
  • Elementos de HUD: picking-mode="Ignore" em todos os lugares, sem controles focalizáveis. Menus são a exceção.
  • Nome do arquivo = nome da classe MonoBehaviour.

4. Layout (fonte da maioria dos bugs visuais)

  • Nunca coloque partes relacionadas com deslocamentos de pixel adivinhados separadamente: dois painéis absolutos sobrepostos porque bottom foi estimado às cegas. Coloque-os em um contêiner flex e posicione apenas esse contêiner.
  • Um contêiner absolutamente posicionado por zona da tela; filhos fluem com flex e margens.
  • Esconda com opacity + transição quando o layout não deve pular; display:none apenas quando o espaço deve colapsar.
  • Largura fixa em rótulos numéricos para que 9 para 10 para 100 não desloque vizinhos.
  • Caixas são border-box: largura da linha de segmento = contagem x (largura + margem) - margem. Alinhe widgets relacionados pela borda esquerda idêntica e largura calculada.
  • Configurações de Painel: Scale With Screen Size (ex: 1920x1080, match 0.5); para HUDs desligue Clear Color e Clear Depth Stencil. Use o Sort Order do PanelRenderer para camadas de partes (vinheta em tela cheia abaixo do texto).

5. USS e estado

  • Estado = classe alternada do C# (EnableInClassList); animação = transição USS. Nunca defina cores ou anime no código.
  • Nomeie estados por significado: --low, --critical, --hidden, --hit, --pulse, --unavailable. Prefira BEM para novas partes.
  • Regras de sobreposição (segmentos gastos, flash) vêm DEPOIS das regras que elas substituem; especificidade igual, a última vence.
  • Uma transição usa a duração/atraso do estado de destino: .x--hidden { opacity:0; transition-duration:.5s; transition-delay:.9s; } desaparece tarde e aparece instantaneamente; .x--flash { transition-duration:.05s; } liga rápido, desaparece na velocidade base.
  • Texto flutuante sem fundo precisa de text-shadow. Sem text-transform (maiúsculas no C#); sem gradientes radiais (veja vinheta).
  • Para UI re-skinable, coloque cores/tamanhos em variáveis USS em uma classe raiz (--c-text, usado como var(--c-text)); verifique se a versão do Unity do usuário suporta.

6. Esqueleto do controlador (PanelRenderer)

[RequireComponent(typeof(PanelRenderer))]
public class PartController : MonoBehaviour
{
    [SerializeField] private SourceComponent source;      // vazio = encontrar em tempo de execução
    private PanelRenderer _pr; private VisualElement _block; private Label _value;
    private bool _bound; private float _v = -1f; private int _shown = -1; private float _nextFind;

    private void Awake() => _pr = GetComponent<PanelRenderer>();
    private void OnEnable()  { _pr.RegisterUIReloadCallback(OnUIReload); TryBind(); }
    private void OnDisable() { _pr.UnregisterUIReloadCallback(OnUIReload); Unbind(); _block = null; _value = null; }

    private void Update()
    {
        if (_bound || Time.unscaledTime < _nextFind) return;
        _nextFind = Time.unscaledTime + 0.5f; TryBind();
    }
    private void TryBind()
    {
        if (_bound) return;
        if (source == null) source = FindFirstObjectByType<SourceComponent>();
        if (source == null) return;
        source.OnChanged += OnChanged; _bound = true;
        _v = source.Current; Apply();                      // puxa o estado atual uma vez
    }
    private void Unbind() { if (_bound && source != null) source.OnChanged -= OnChanged; _bound = false; }

    private void OnUIReload(PanelRenderer r, VisualElement root)
    {
        _block = root.Q<VisualElement>("part"); _value = root.Q<Label>("part-value");
        if (_block == null || _value == null) { Debug.LogError("Part: elements not found in UXML (check Source Asset).", this); _block = null; return; }
        _shown = -1; Apply();                              // nova árvore: força reescrita do cache
    }
    private void OnChanged(float v) { _v = v; Apply(); }
    private void Apply()
    {
        if (_block == null || _v < 0f) return;
        int n = Mathf.CeilToInt(_v);
        if (n != _shown) { _shown = n; _value.text = n.ToString(); }   // escreve apenas na mudança
        _block.EnableInClassList("part--low", _v <= 0.3f);
    }
}

7. Snippets reutilizáveis

  • Segmentos/pips: em OnUIReload, row.Clear(); list.Clear(); então adicione N VisualElement{pickingMode=Ignore} com classe seg; na mudança list[i].EnableInClassList("seg--spent", i >= filled). Mostre pelo menos 1 preenchido enquanto valor > 0: filled = v<=0 ? 0 : Clamp(CeilToInt(v*N-0.001f),1,N).
  • Pulso: _pulse = _block.schedule.Execute(() => _block.ToggleInClassList("part--pulse")).Every(400); _pulse.Pause(); Retome ao entrar no estado, Pause + remova classe ao sair; rastreie um bool _pulsing e redefina no recarregamento.
  • Flash no evento: var b=_block; b.AddToClassList("part--hit"); b.schedule.Execute(()=>b.RemoveFromClassList("part--hit")).StartingIn(150);
  • Vinheta: gere uma vez em Awake uma Texture2D 128x128 (RGBA32, Clamp, Bilinear, HideFlags.HideAndDontSave) onde alpha = SmoothStep(0,1,InverseLerp(inner,outer,dist/1.4142)) a partir do centro, RGB branco; defina _el.style.backgroundImage = new StyleBackground(tex) em OnUIReload; destrua em OnDestroy. USS: elemento absoluto em tela cheia, opacity:0, -unity-background-scale-mode: stretch-to-fill, -unity-background-image-tint-color para a cor, classes --low/--critical/--pulse/--flash definindo opacidade (flash por último, duração rápida). Sort Order menor que partes de texto.

8. Gêneros e plataformas

A arquitetura é idêntica entre gêneros; o guia de estilo e o modelo de entrada mudam. Arcade: números grandes e encorpados, contagem crescente, pop de escala, brilho de combo, transições rápidas de 0,08-0,25 s, contêineres bem-vindos. Retrô/pixel: fonte pixel, escala inteira, texturas com filtro Point, bordas de sprite 9-slice. Mobile: alvos de toque de cerca de 44-48 dp, área segura do dispositivo como padding da raiz (verifique a API atual para a versão do Unity do usuário), sem estados hover, fonte mínima maior, teste em dispositivo real. Console/gamepad: elementos focalizáveis com um estilo :focus forte, foco inicial explícito, margens TV-safe (3-5 por cento), texto maior, glifos que trocam com o dispositivo ativo. VR/espaço-mundial: depende da versão do Unity, pesquise primeiro. Esses pontos de plataforma são práticas gerais sólidas, mas não testadas em um projeto real: diga isso e peça ao usuário para verificar no dispositivo. Mantenha rótulos de entrada (nome da tecla ou glifo) em dados que o controlador lê, não codificados em UXML.

9. Entregando uma parte

Em ordem: UXML completo, USS completo, controlador completo, etapas de configuração (GameObject, PanelRenderer + Panel Settings compartilhado + Source Asset, Sort Order, referências a preencher, o que ver em Play), botões de ajuste (qual valor USS altera tamanho/posição/cor/limites) e uma entrada curta no guia de estilo. Ao alterar arquivos existentes, forneça arquivos inteiros se a maior parte do arquivo mudar (colagens parciais causaram erros reais).

10. Solução de problemas

SintomaCausa / correção
NullReference em rootVisualElement / GetComponent<UIDocument>()O código usa UIDocument, mas o objeto tem PanelRenderer. Use RegisterUIReloadCallback, vincule em OnUIReload
Controlador registra "element not found"Source Asset errado, incompatibilidade de nome com Q<>(), ou UXML inválido
UI Builder "Failed to open asset"UXML inválido (duplicata </ui:UXML>, <Style> ruim, GUID placeholder, caracteres ocultos). Corrija, então clique com o botão direito no arquivo > Reimport. Leia a mensagem do console acima do erro genérico
Elementos sem estilo<Style> ausente ou caminho errado. No UI Builder: StyleSheets > + > Add Existing USS (escreve project://database/...guid=... completo)
Tudo desapareceu após uma ediçãoUXML quebrou, ou a referência Source Asset ficou obsoleta após renomear. Corrija, Reimport, reatribua. Teste com background-color: rgba(255,0,0,.3) na raiz
Parte mal posicionada ou sobrepondo outraGameObject obsoleto ainda desenhando UXML antigo, ou deslocamentos absolutos separados. Exclua objeto antigo, mescle vizinhos em um contêiner
Texto placeholder na visualização GameO modo Edição desenha o UXML; pressione Play
Script não pode ser adicionadoNome do arquivo != nome da classe. Renomeie dentro da janela Project do Unity para que o .meta siga

Correções não válidas: a tag de stylesheet é <Style src="..."/> SEM o prefixo ui: (<ui:Style> é silenciosamente ignorado); nunca invente ou deixe um GUID placeholder; use o cabeçalho padrão <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">.

11. Trabalhando sem ver o editor

Peça uma captura de tela em reclamações de layout e leia atentamente; resolveu bugs que a adivinhação não conseguia. Admita erros brevemente e corrija com arquivos completos. Para erros de compilação, peça o texto exato e a versão do Unity em vez de adivinhar novamente. Não prometa comportamento do dispositivo ou números de desempenho; construa a estrutura e peça ao usuário para testar.

Habilidades Relacionadas