Voltar para o Blog
Quest Log

Navegar no Menu com Controle na Unity (EventSystem)

Menu principal de jogo com quatro botões empilhados, o segundo destacado por uma moldura brilhante, e um controle de videogame à frente

Unity menu navigation with controller: EventSystem com InputSystemUIInputModule, First Selected, navegação explícita, troca de painel e destaque do botão.

Ferramenta:UnityC#

Navegar no Menu com Controle na Unity (EventSystem)

Para um menu feito com uGUI responder ao controle (a busca "unity menu navigation with controller"), você precisa de três coisas: um EventSystem com o InputSystemUIInputModule, um botão selecionado desde o início e uma navegação entre botões que faça sentido. Com isso, o direcional e o analógico movem a seleção e o botão sul confirma.

Parece pouco, e é aí que mora o problema: o menu funciona na primeira tela e morre na segunda, o mouse rouba a seleção, o jogador volta do painel de opções e nada está selecionado. Este guia monta um menu que funciona no controle do começo ao fim, com código C# para Unity 6.3 LTS e o Input System.

Unity menu navigation with controller: o que precisa

O uGUI (Canvas, Button, Slider, Toggle) tem navegação embutida. Cada Selectable sabe quem são os vizinhos acima, abaixo e dos lados. O EventSystem guarda qual objeto está selecionado agora e, quando chega um evento de mover (direcional, analógico, setas do teclado), passa a seleção para o vizinho naquela direção. Quando chega Submit, ele "clica" no selecionado.

Daí vem a regra que resolve metade dos problemas: sem objeto selecionado, não existe navegação. O mouse não precisa de seleção (ele aponta), o controle precisa.

EventSystem e InputSystemUIInputModule

Ao criar um Canvas (GameObject > UI > Canvas), a Unity cria também um objeto EventSystem. Com o Input System ativo, ele deve ter o componente Input System UI Input Module. Se aparecer o Standalone Input Module antigo, o Inspector mostra um botão para substituir pelo módulo do Input System; clique nele.

O módulo traz um conjunto de actions de UI: Point, Left Click, Middle Click, Right Click, Scroll Wheel, Move, Submit, Cancel e duas de dispositivo rastreado (para VR). As que importam para controle são Move (direcional e analógico; no map UI do asset padrão ela se chama Navigate), Submit (botão sul) e Cancel (botão leste).

No campo Actions Asset do módulo, aponte para o seu InputSystem_Actions, que tem um map UI com essas actions. A documentação do módulo indica as project-wide actions como o jeito de ter actions de UI editáveis desde o início. A vantagem prática: se o jogador remapear Submit na tela de opções, o menu respeita.

Dois campos do módulo que mudam a sensação do menu:

  • Move Repeat Delay e Move Repeat Rate: segurar o direcional espera o delay e depois repete a cada intervalo. Ajuste se a lista é longa e a rolagem parece lenta.
  • Deselect On Background Click: ligado por padrão. Clicar com o mouse fora de qualquer elemento limpa a seleção. Guarde isso, porque é a causa de um dos bugs da seção de erros.

First Selected e SetSelectedGameObject

Para o menu da primeira cena, selecione o objeto EventSystem e arraste o botão "Jogar" no campo First Selected. Ao carregar a cena, ele já começa selecionado e o controle funciona sem uma linha de código.

Para tudo que abre depois (pausa, opções, inventário, diálogo de confirmação), selecione por código:

Arquivo MenuPanel.cs:

using UnityEngine;
using UnityEngine.EventSystems;

public class MenuPanel : MonoBehaviour
{
    [SerializeField] private GameObject firstSelected;

    private void OnEnable()
    {
        SelectFirst();
    }

    public void SelectFirst()
    {
        if (EventSystem.current == null || firstSelected == null)
        {
            return;
        }

        EventSystem.current.SetSelectedGameObject(null);
        EventSystem.current.SetSelectedGameObject(firstSelected);
    }
}

O SetSelectedGameObject(null) antes do objeto real não é enfeite. Se o botão já era o selecionado de uma abertura anterior, chamar de novo com o mesmo objeto não dispara os eventos de seleção, e o destaque visual pode não aparecer. Limpar primeiro garante a transição.

Coloque um MenuPanel em cada painel e preencha o firstSelected com o botão mais provável: "Continuar" na pausa, "Voltar" num diálogo destrutivo (para o jogador não apagar o save apertando A sem querer).

Selecione um botão e veja o campo Navigation no Inspector. As opções:

  • Automatic: a Unity escolhe o vizinho pela posição na tela. Funciona bem em lista vertical simples.
  • Horizontal / Vertical: automática, mas só em um eixo.
  • Explicit: você arrasta os vizinhos em Select On Up, Down, Left e Right.
  • None: o elemento não participa da navegação (bom para um texto clicável decorativo).

O botão Visualize desenha setas amarelas na Scene view mostrando para onde cada elemento leva. Ligue sempre ao montar menu; erros de navegação aparecem na hora.

Quando usar Explicit: para dar a volta (de "Sair", embaixo, para "Jogar", em cima), para grades irregulares (inventário com slots de tamanhos diferentes) e quando a automática pula um elemento por estar um pouco desalinhado. Sliders merecem atenção: em navegação automática, esquerda e direita mudam o valor do slider em vez de sair dele, o que é o esperado para volume. Mantenha os sliders numa coluna e use cima e baixo para entrar e sair deles.

Menu jogável no controle é uma daquelas partes que separam protótipo de jogo lançável: ninguém elogia quando funciona, todo mundo reclama quando não funciona. Junto com pausa, salvamento, opções e troca de cena, é a camada que transforma mecânicas soltas num produto, e é ela que o curso de Unity monta passo a passo dentro de projetos completos.

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

Trocar de painel sem perder a seleção

Fluxo típico: menu principal, jogador seleciona "Opções", ajusta algo, aperta Cancel para voltar. O certo é voltar com "Opções" selecionado, não com "Jogar". Para isso, guarde quem estava selecionado ao sair e restaure ao voltar.

Arquivo MenuNavigator.cs:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;

public class MenuNavigator : MonoBehaviour
{
    [SerializeField] private MenuPanel rootPanel;

    private readonly Stack<MenuPanel> panelStack = new Stack<MenuPanel>();
    private readonly Stack<GameObject> returnSelection = new Stack<GameObject>();
    private InputAction cancelAction;

    private void Awake()
    {
        cancelAction = InputSystem.actions.FindAction("UI/Cancel", true);
    }

    private void Start()
    {
        rootPanel.gameObject.SetActive(true);
        panelStack.Push(rootPanel);
    }

    private void Update()
    {
        if (cancelAction.WasPressedThisFrame())
        {
            Back();
        }
    }

    public void Open(MenuPanel panel)
    {
        GameObject current = EventSystem.current.currentSelectedGameObject;
        returnSelection.Push(current);

        panelStack.Peek().gameObject.SetActive(false);
        panel.gameObject.SetActive(true);
        panelStack.Push(panel);
    }

    public void Back()
    {
        if (panelStack.Count <= 1)
        {
            return;
        }

        MenuPanel closing = panelStack.Pop();
        closing.gameObject.SetActive(false);

        MenuPanel previous = panelStack.Peek();
        previous.gameObject.SetActive(true);

        GameObject restore = returnSelection.Pop();
        EventSystem.current.SetSelectedGameObject(null);
        if (restore != null && restore.activeInHierarchy)
        {
            EventSystem.current.SetSelectedGameObject(restore);
        }
        else
        {
            previous.SelectFirst();
        }
    }
}

No botão "Opções", ligue On Click () a MenuNavigator.Open passando o painel de opções. A pilha permite submenus dentro de submenus (Opções > Controles > Remapear) e o Cancel volta um nível por vez. O previous.gameObject.SetActive(true) dispara o OnEnable do MenuPanel, que seleciona o primeiro botão; logo em seguida o navegador sobrescreve com o botão de onde o jogador saiu. Se esse botão sumiu (foi desativado), cai no primeiro.

Destaque visual do botão selecionado

No Button, em Transition: Color Tint, existem cores separadas para Highlighted (mouse em cima) e Selected (selecionado pelo EventSystem). Muita gente só ajusta a Highlighted, testa com mouse e lança. No controle, o Selected é o único sinal de onde o jogador está; dê a ele contraste forte, não um cinza 10% mais claro.

Para destaque mais rico (escala, moldura, som), use os eventos de seleção:

Arquivo SelectedHighlight.cs:

using UnityEngine;
using UnityEngine.EventSystems;

public class SelectedHighlight : MonoBehaviour, ISelectHandler, IDeselectHandler, IPointerEnterHandler
{
    [SerializeField] private GameObject frame;
    [SerializeField] private float selectedScale = 1.08f;

    private Vector3 baseScale;

    private void Awake()
    {
        baseScale = transform.localScale;
        frame.SetActive(false);
    }

    public void OnSelect(BaseEventData eventData)
    {
        frame.SetActive(true);
        transform.localScale = baseScale * selectedScale;
    }

    public void OnDeselect(BaseEventData eventData)
    {
        frame.SetActive(false);
        transform.localScale = baseScale;
    }

    public void OnPointerEnter(PointerEventData eventData)
    {
        if (EventSystem.current != null)
        {
            EventSystem.current.SetSelectedGameObject(gameObject);
        }
    }
}

O OnPointerEnter resolve o "dois botões acesos": com ele, passar o mouse sobre um botão também o seleciona, então mouse e controle compartilham um único destaque. Alterne entre os dois no meio do menu e o jogador nunca fica em dúvida de onde está.

Nunca deixar a seleção vazia

Mesmo com tudo acima, a seleção pode ficar nula: um botão é destruído, um painel some por animação, um clique cai no fundo. Um guarda pequeno no EventSystem resolve todos esses casos de uma vez.

Arquivo SelectionKeeper.cs:

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;

public class SelectionKeeper : MonoBehaviour
{
    private GameObject lastSelected;
    private InputAction navigateAction;

    private void Awake()
    {
        navigateAction = InputSystem.actions.FindAction("UI/Navigate", true);
    }

    private void Update()
    {
        EventSystem eventSystem = EventSystem.current;
        if (eventSystem == null)
        {
            return;
        }

        GameObject current = eventSystem.currentSelectedGameObject;
        if (current != null && current.activeInHierarchy)
        {
            lastSelected = current;
            return;
        }

        bool navigating = navigateAction.ReadValue<Vector2>().sqrMagnitude > 0.25f;
        if (navigating && lastSelected != null && lastSelected.activeInHierarchy)
        {
            eventSystem.SetSelectedGameObject(lastSelected);
        }
    }
}

Coloque no mesmo objeto do EventSystem. Ele lembra o último selecionado válido e, quando a seleção some e o jogador mexe o direcional, devolve a seleção para lá. O jogador de mouse não percebe nada; o de controle nunca fica com o menu morto. No asset padrão da Unity 6, a action de mover do map UI se chama Navigate; se o seu asset usa outro nome, ajuste a string.

Erros comuns

O controle para de funcionar depois de clicar com o mouse. É o Deselect On Background Click: o clique fora dos botões limpou a seleção. Desligue a opção no módulo ou adicione um script que, ao detectar input de navegação com seleção nula, reseleciona o último botão.

Menu de pausa não navega. O jogo desligou o action map UI junto com o Player ao pausar, ou nenhum botão foi selecionado ao abrir. Desligue só o Player e use o MenuPanel.

Menu pausado não anda. Se o seu código de pausa zera Time.timeScale e algum script de animação do menu usa Time.deltaTime, o destaque ou a transição congela, e parece que a navegação travou. Use Time.unscaledDeltaTime nas animações de UI e, no Animator dos botões, Update Mode em Unscaled Time.

A seleção vai parar num botão escondido. Navegação automática enxerga botões ativos de outros painéis sobrepostos. Desative os painéis que não estão abertos, em vez de só deixá-los transparentes.

UI Toolkit não respeita a Navigation do Inspector. Ela é do uGUI. UI Toolkit tem foco e navegação próprios (elementos focáveis e eventos de navegação), configurados de outra forma.

Próximo passo

Monte o menu principal com Jogar, Opções e Sair, um painel de Opções com dois sliders e um toggle, e jogue só com o controle, sem encostar no mouse. Para a parte visual de menus e HUD, o post de UI e UX em jogos traz os princípios de legibilidade. A montagem do menu inicial com troca de cena está em menu principal na Unity, e quem usa Godot encontra o mesmo tema em suporte a controle e joystick na Godot.

Perguntas frequentes

Por que o controle não navega no meu menu da Unity?

Quase sempre porque nenhum botão está selecionado. A navegação do uGUI parte do objeto selecionado no EventSystem; se ele é nulo, o direcional não tem de onde sair. Preencha First Selected no EventSystem ou chame SetSelectedGameObject ao abrir o menu.

O que é o First Selected do EventSystem?

É o campo do componente EventSystem que define qual objeto começa selecionado quando a cena carrega. Funciona para o primeiro menu; para painéis que abrem depois, como opções ou pausa, você precisa selecionar por código com EventSystem.current.SetSelectedGameObject.

Qual a diferença entre navegação automática e explícita na Unity?

Na automática, a Unity escolhe o vizinho pela posição dos elementos na tela. Na explícita, você arrasta no Inspector qual botão fica acima, abaixo, à esquerda e à direita. A automática resolve listas simples; a explícita é necessária para dar a volta no fim da lista e para layouts em grade irregular.

UI Toolkit usa EventSystem para navegação?

UI Toolkit tem sistema próprio de foco e navegação, baseado em elementos focáveis e eventos de navegação, e não usa o Navigation dos botões do uGUI. Este guia trata do uGUI (Canvas, Button, EventSystem), que é o caminho mais usado em menus de jogo.