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
- No Project, clique com o botão direito em
Assets/UIe escolha Create > UI Toolkit > UI Document. NomeieMainMenu.uxml. - Dê dois cliques no arquivo. Abre o UI Builder (também acessível por Window > UI Toolkit > UI Builder).
- O UI Builder tem: Library (elementos disponíveis), Hierarchy (a árvore da tela), Viewport (o preview), StyleSheets e o Inspector à direita.
- 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
titlecom 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
- 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.
- Arraste
MainMenu.uxmlpara o campo Source Asset do UI Document. - 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.
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.



