Voltar para o Blog
Quest Log

UI Toolkit na Unity: Tutorial com UI Builder e USS

Menu de jogo com botões arredondados e barra de vida sendo montado num editor visual de interface

Unity UI Toolkit do zero: monte HUD e menu no UI Builder, estilize com USS, configure UIDocument e Panel Settings e responda a cliques com C# no Unity 6.3.

Ferramenta:UnityC#

UI Toolkit na Unity: Tutorial com UI Builder e USS

O UI Toolkit da Unity é o sistema de interface baseado em documentos: você descreve a estrutura da tela em UXML, o visual em USS (uma folha de estilo parecida com CSS) e monta tudo de forma visual no UI Builder. Na cena, um componente UIDocument mostra a tela, e o seu script C# busca os elementos com Q<Button>() para reagir a cliques e atualizar textos e barras.

Aqui você vai construir um menu principal e um HUD com barra de vida no Unity 6.3: criar o UXML no UI Builder, estilizar em USS, colocar o UIDocument na cena e escrever os scripts que conectam tudo.

O que é UI Toolkit na Unity

A Unity tem dois sistemas de UI para jogos. O uGUI usa GameObjects num Canvas, cada botão é um objeto com componentes. O UI Toolkit trabalha com uma árvore de elementos (VisualElements) definida em arquivos, sem um GameObject por botão, com layout flexível no estilo flexbox e estilos globais.

No Unity 6.3, o UI Toolkit é o sistema recomendado para a interface do editor e uma alternativa para UI de jogo, principalmente telas sobrepostas (menus, HUD, inventário). A tabela de comparação do manual lista como recursos dele em runtime: data binding, animações de transição de UI, layout flexível avançado, gerenciamento global de estilos, suporte a SVG e renderização em screen space e world space. O que ele ainda não tem e o uGUI tem: edição dentro da cena, eventos serializados no Inspector e integração com Animation Clips e Timeline. Se você ainda está decidindo entre os dois, veja o comparativo de UI Toolkit vs uGUI.

UI Builder: montando a tela visualmente

  1. No Project, clique com o botão direito em Assets/UI e escolha Create > UI Toolkit > UI Document. Nomeie MainMenu.uxml.
  2. Dê dois cliques no arquivo. Abre o UI Builder (também acessível por Window > UI Toolkit > UI Builder).
  3. O UI Builder tem: Library (elementos disponíveis), Hierarchy (a árvore da tela), Viewport (o preview), StyleSheets e o Inspector à direita.
  4. Selecione o documento raiz na Hierarchy e, no Inspector, marque Match Game View para o preview usar a resolução do Game view.

Agora monte o menu arrastando da Library para a Hierarchy:

  • Um VisualElement chamado menu-root.
  • Dentro dele, um Label chamado title com o texto do nome do jogo.
  • Três Button: play-button, options-button, quit-button, com os textos "Jogar", "Opções" e "Sair".

O campo Name no Inspector de cada elemento é o que o script vai usar para encontrá-lo. Use nomes em minúsculas com hífen, sem espaço. Salve com File > Save no topo do Viewport.

UXML e USS: estrutura e estilo

O UXML que o UI Builder salvou fica parecido com isto:

<ui:UXML xmlns:ui="UnityEngine.UIElements">
    <Style src="MainMenu.uss" />
    <ui:VisualElement name="menu-root" class="menu-root">
        <ui:Label name="title" text="Meu Jogo" class="title" />
        <ui:Button name="play-button" text="Jogar" class="menu-button" />
        <ui:Button name="options-button" text="Opções" class="menu-button" />
        <ui:Button name="quit-button" text="Sair" class="menu-button" />
    </ui:VisualElement>
</ui:UXML>

Para o estilo, no painel StyleSheets clique em + > Create New USS e salve MainMenu.uss. Você pode criar seletores pelo UI Builder ou editar o arquivo num editor de texto:

.menu-root {
    flex-grow: 1;
    align-items: center;
    justify-content: center;
    background-color: rgba(10, 10, 20, 0.85);
}

.title {
    font-size: 64px;
    color: rgb(255, 255, 255);
    margin-bottom: 40px;
    -unity-font-style: bold;
}

.menu-button {
    width: 320px;
    height: 64px;
    margin: 8px;
    font-size: 28px;
    border-radius: 12px;
    background-color: rgb(237, 26, 59);
    color: rgb(255, 255, 255);
    border-width: 0;
    transition-duration: 0.15s;
}

.menu-button:hover {
    scale: 1.05 1.05;
    background-color: rgb(255, 60, 90);
}

A diferença prática em relação a configurar cada botão no Inspector: mudar .menu-button muda todos os botões do jogo de uma vez. O transition-duration anima o hover sem nenhuma linha de C#. Propriedades com prefixo -unity- são específicas da Unity, como estilo e fonte do texto. Para escolher fontes legíveis para o jogo, veja o guia de tipografia e fontes para jogos.

UIDocument e Panel Settings na cena

  1. No menu, GameObject > UI Toolkit > UI Document. A Unity cria um GameObject com o componente UI Document e, na primeira vez, uma pasta com um asset Panel Settings e um tema de runtime padrão.
  2. Arraste MainMenu.uxml para o campo Source Asset do UI Document.
  3. Selecione o asset Panel Settings. Em Scale Mode, escolha Scale With Screen Size e defina a Reference Resolution (1920x1080 para jogo de PC em paisagem). Assim a UI cresce e diminui com a tela.

Dê Play. O menu aparece por cima de tudo. Cada UIDocument que usa o mesmo Panel Settings vai para o mesmo painel. O campo Sort Order do UI Document define quem fica na frente quando há mais de um documento.

Um menu bonito sozinho é a parte fácil. O difícil é ligar menu, pausa, HUD, troca de cena e save num jogo que funciona do início ao fim, com cada tela falando com a lógica certa. No curso de Unity você monta esse fluxo completo, entendendo por que cada peça fica onde fica.

Curso de Unity
Quer fazer seu primeiro jogo na Unity?

No curso de Unity 6 da CursoGame.Dev você vai do zero ao primeiro jogo, com um desafio prático em cada level e suporte individual no WhatsApp quando travar.

Conhecer o curso de Unity
Suporte no WhatsAppAcesso vitalícioGarantia de 7 dias

Buscando elementos com Q e reagindo a cliques

Adicione este script no mesmo GameObject do UI Document. Arquivo MainMenuController.cs:

using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;

[RequireComponent(typeof(UIDocument))]
public class MainMenuController : MonoBehaviour
{
    [SerializeField] private string gameSceneName = "Game";

    private Button playButton;
    private Button optionsButton;
    private Button quitButton;

    private void OnEnable()
    {
        VisualElement root = GetComponent<UIDocument>().rootVisualElement;

        playButton = root.Q<Button>("play-button");
        optionsButton = root.Q<Button>("options-button");
        quitButton = root.Q<Button>("quit-button");

        playButton.clicked += OnPlayClicked;
        optionsButton.clicked += OnOptionsClicked;
        quitButton.clicked += OnQuitClicked;
    }

    private void OnDisable()
    {
        playButton.clicked -= OnPlayClicked;
        optionsButton.clicked -= OnOptionsClicked;
        quitButton.clicked -= OnQuitClicked;
    }

    private void OnPlayClicked()
    {
        SceneManager.LoadScene(gameSceneName);
    }

    private void OnOptionsClicked()
    {
        Debug.Log("Abrir opções");
    }

    private void OnQuitClicked()
    {
        Application.Quit();
    }
}

O Q<T>(nome) busca o primeiro elemento daquele tipo com aquele nome na árvore. Também dá para buscar por classe: root.Q<Button>(className: "menu-button"), ou pegar todos com root.Query<Button>(className: "menu-button").ToList().

Por que OnEnable e não Start: o UIDocument recria a árvore de elementos quando é desativado e reativado. Se você registra os cliques no Start, depois de desligar e ligar o menu os botões ficam mortos. Registrando no OnEnable, a cada reativação os elementos novos são buscados de novo.

O clicked serve para botões. Para qualquer outro elemento, use RegisterCallback<ClickEvent>(handler), que é o caminho que o manual usa no exemplo de UI de runtime, e lembre de UnregisterCallback no OnDisable.

Atualizando texto e barra de vida por script

Crie Hud.uxml com um Label chamado score-label e um ProgressBar chamado health-bar (ele está na Library, em Controls). Num segundo UI Document, use este script. Arquivo HudController.cs:

using UnityEngine;
using UnityEngine.UIElements;

[RequireComponent(typeof(UIDocument))]
public class HudController : MonoBehaviour
{
    [SerializeField] private float maxHealth = 100f;

    private Label scoreLabel;
    private ProgressBar healthBar;

    private void OnEnable()
    {
        VisualElement root = GetComponent<UIDocument>().rootVisualElement;
        scoreLabel = root.Q<Label>("score-label");
        healthBar = root.Q<ProgressBar>("health-bar");

        healthBar.lowValue = 0f;
        healthBar.highValue = maxHealth;
        SetHealth(maxHealth);
        SetScore(0);
    }

    public void SetHealth(float value)
    {
        healthBar.value = Mathf.Clamp(value, 0f, maxHealth);
        healthBar.title = $"{Mathf.CeilToInt(healthBar.value)} / {maxHealth}";
    }

    public void SetScore(int score)
    {
        scoreLabel.text = $"Pontos: {score}";
    }
}

O script de vida do jogador chama hud.SetHealth(vidaAtual) quando leva dano. Atualize a UI quando o valor muda, e não todo frame: o UI Toolkit só redesenha o que mudou, e textos alterados a cada frame geram trabalho à toa. Para telas grandes com muitos campos, o data binding de runtime liga propriedades de um objeto direto aos elementos, sem esse código de cola.

Trocando de tela: mostrar e esconder sem recriar

Um jogo tem várias telas: menu, opções, pausa, fim de fase. Existem duas formas de alternar entre elas, e a escolha muda o comportamento dos seus scripts.

Desativar o GameObject do UI Document. Simples, mas a árvore é destruída e recriada a cada vez. Funciona bem para telas que aparecem raramente, desde que os registros de clique estejam no OnEnable, como nos scripts acima.

Esconder um elemento dentro do mesmo documento. Coloque o menu principal e o painel de opções no mesmo UXML, como irmãos, e alterne pelo estilo: optionsPanel.style.display = DisplayStyle.None; esconde, DisplayStyle.Flex mostra. A árvore continua existindo, os callbacks continuam registrados e o valor dos sliders não se perde. É o caminho mais leve para alternar painéis que o jogador abre e fecha toda hora.

Para esconder sem tirar do layout (o espaço continua reservado), use style.visibility = Visibility.Hidden. E para uma transição suave, mude a opacidade numa classe USS com transition-duration e adicione ou remova a classe por script com AddToClassList e RemoveFromClassList. O UI Toolkit anima a mudança sozinho, sem corrotina.

Erros comuns: UI que não aparece e clique que não dispara

UI não aparece. Confira: Source Asset preenchido no UI Document; Panel Settings atribuído; o elemento raiz com flex-grow: 1 (sem isso, ele tem altura zero); nenhum display: none herdado; Sort Order atrás de outro documento opaco.

NullReferenceException no Q. O nome no script não bate com o Name do elemento (não confunda com o texto do botão nem com a classe). Ou o script está num GameObject diferente do UI Document.

Clique não dispara. Um elemento transparente por cima captura o ponteiro. No UI Builder, selecione o elemento de cima e mude Picking Mode para Ignore. Também acontece quando o registro foi no Start e o documento foi reativado.

UI gigante ou minúscula em outra resolução. Scale Mode em Constant Pixel Size. Troque para Scale With Screen Size no Panel Settings.

Estilo não aplica. O USS não está ligado ao UXML (falta o <Style src>), ou um estilo inline definido no Inspector do UI Builder está sobrescrevendo a classe. Estilos inline aparecem em negrito no Inspector.

Próximo passo

Monte o menu deste tutorial, depois o HUD, e faça o botão Jogar carregar a cena com o HUD. Em seguida, crie uma tela de pausa reaproveitando o mesmo USS. Para decidir o que mostrar no HUD e como organizar menus que o jogador entende de primeira, leia o guia de UI/UX design para jogos.

Perguntas frequentes

UI Toolkit funciona para UI dentro do jogo ou só para o editor?

Funciona para os dois. Para UI de jogo você usa um UIDocument na cena com um Panel Settings. A tabela de comparação do manual do Unity 6.3 lista renderização em screen space e world space, data binding e materiais personalizados como recursos de runtime do UI Toolkit.

O que são UXML e USS?

UXML é o arquivo que descreve a estrutura da tela, quais elementos existem e como estão aninhados, parecido com HTML. USS é o arquivo de estilo, com seletores e propriedades parecidas com CSS. O UI Builder edita os dois visualmente.

Como pegar um botão do UI Toolkit no script?

Pegue o rootVisualElement do UIDocument e use Q<Button>("nome-do-botao"), em que o nome é o campo Name do elemento no UI Builder. Depois registre o clique com button.clicked += Metodo ou com RegisterCallback<ClickEvent>.

Preciso de EventSystem para o UI Toolkit receber cliques?

Não é obrigatório: o UI Toolkit processa o input dos painéis de runtime por conta própria. Se a cena já tiver um EventSystem por causa de uGUI, os dois convivem. Clique que não dispara costuma ser outro elemento por cima capturando o ponteiro.