Controle e Gamepad na Unity: Xbox, PlayStation e Genéricos
Suportar Unity gamepad com o Input System é, na maior parte, configuração: você adiciona bindings de <Gamepad> nas mesmas actions do teclado e controles de Xbox e PlayStation passam a funcionar com o mesmo código. O trabalho de verdade está nos detalhes que o jogador percebe: analógico com drift, ícone de botão errado na tela, controle genérico que não responde e vibração que não para.
Este guia cobre esses detalhes com código para Unity 6.3 LTS e o Input System 1.20: o que o pacote já resolve sozinho, como montar os bindings, as diferenças entre Xbox, PlayStation e genéricos, deadzone, detecção de troca entre teclado e controle para trocar os ícones, vibração e como testar tudo isso sem ter uma gaveta cheia de controles.
Unity gamepad: o que o Input System já resolve
O Input System trata todo controle reconhecido como um Gamepad: dois analógicos, D-pad, quatro botões de face, ombros e gatilhos. Os botões de face têm nome por posição, não por letra: buttonSouth é o A do Xbox e o X do PlayStation, buttonEast é o B e o círculo, buttonWest é o X e o quadrado, buttonNorth é o Y e o triângulo.
Isso quer dizer que um binding <Gamepad>/buttonSouth para pular funciona em controle de Xbox, de PlayStation e de Switch Pro sem você escrever um if. O asset padrão InputSystem_Actions da Unity 6 já traz Move no analógico esquerdo, Look no direito e Jump no botão sul. Se você usa esse asset, o seu jogo provavelmente já responde a controle e você nem testou.
A leitura direta, para debug, fica assim:
Arquivo GamepadDebug.cs:
using UnityEngine;
using UnityEngine.InputSystem;
public class GamepadDebug : MonoBehaviour
{
private void Update()
{
Gamepad pad = Gamepad.current;
if (pad == null)
{
return;
}
Vector2 leftStick = pad.leftStick.ReadValue();
float rightTrigger = pad.rightTrigger.ReadValue();
if (pad.buttonSouth.wasPressedThisFrame)
{
Debug.Log("Botão sul em " + pad.displayName + " | analógico " + leftStick + " | gatilho " + rightTrigger);
}
}
}
Gamepad.current é o último controle usado ou conectado. Para multiplayer local você vai querer Gamepad.all ou o componente Player Input, que pareia um controle por jogador.
Adicionar bindings de gamepad
No asset de actions, para cada action que o jogador usa:
- Selecione a action (por exemplo, Attack) e clique no + ao lado dela, Add Binding.
- Em Path, clique em Listen e aperte o botão no controle, ou navegue em Gamepad > Button West.
- Em Use in control scheme, marque Gamepad (se o asset tem esquemas definidos).
Para movimento, use Gamepad > Left Stick numa action do tipo Value com Control Type Vector2. Se o jogo é de plataforma e você quer o D-pad também, adicione um segundo binding Gamepad > D-Pad na mesma action. Os dois funcionam ao mesmo tempo, e a action entrega o que estiver mais acionado.
Quer o gatilho para acelerar um carro? Action do tipo Value com Control Type Axis, binding Gamepad > Right Trigger. O valor vai de 0 a 1, contínuo. Se o gatilho é só para atirar, deixe a action como Button: ela conta como pressionada a partir do press point padrão (0,5).
Xbox, PlayStation e genéricos
Nem todo controle chega igual. O que o manual do Input System descreve, por família:
- Xbox: no Windows, controles XInput aparecem como
XInputController, que é umGamepad. Vibração funciona. No macOS, alguns modelos com fio precisam de driver de terceiros. - PlayStation: DualShock 4 com suporte em Windows, macOS e Linux; DualSense em Windows e macOS. Os dois são
DualShockGamepad. O manual avisa que o adaptador sem fio USB do DualShock 4 não é suportado: use cabo ou Bluetooth. - Switch Pro: suportado no desktop só por Bluetooth. Joy-Con no desktop não.
- Genéricos HID: controles que a Unity não reconhece não viram
Gamepad, viram Joystick genérico. Bindings de<Gamepad>não pegam nada neles.
Para não deixar o jogador com controle genérico na mão, adicione bindings de <Joystick> (por exemplo <Joystick>/stick para mover e <Joystick>/trigger para a ação principal) nas actions principais. O mapeamento dos botões extras varia por fabricante, então não dá para garantir o layout; o que dá é garantir que ele anda e pula, e deixar o resto para a tela de remapear controles.
Tem um caso que confunde muita gente no PC: jogo distribuído pela Steam com Steam Input ligado. A Steam pode traduzir o controle de PlayStation e entregar ao jogo como um controle Xbox. O jogo funciona, mas o seu código de ícones mostra A/B/X/Y para quem segura um DualSense. Se o ícone importa, confira no Input Debugger rodando o build pela Steam, e decida nas configurações da Steam do seu jogo se Steam Input fica ligado para controles PlayStation. Para quem pensa em Steam Deck, o post sobre compatibilidade com Steam Deck mostra o que a Valve cobra de controle e de texto na tela.
Deadzone e drift do analógico
Todo analógico tem folga mecânica, e analógico velho tem drift: em repouso ele informa (0,08; -0,05) em vez de zero, e o personagem anda sozinho. A defesa é a deadzone.
No Input System ela é um processor. Selecione o binding do Left Stick, em Processors clique em + e escolha Stick Deadzone. Com Default marcado, ele usa InputSettings.defaultDeadzoneMin (0,125) e defaultDeadzoneMax (0,925): abaixo do mínimo vira zero, acima do máximo vira 1, e o meio é reescalado para não dar o "pulo" de 0 para 0,125.
Dois cuidados:
- Stick Deadzone é radial: olha o comprimento do vetor, não cada eixo. É o certo para movimento em 8 direções. Axis Deadzone (por eixo) serve para gatilho.
- Drift varia por controle. Um menu com "zona morta do analógico" que altera o mínimo resolve o problema do jogador sem patch. Você aplica o valor com
action.ApplyParameterOverride("stickDeadzone:min", valor).
O drift resolvido pelo processor deixa o código limpo: o seu script de movimento lê a action e nunca precisa de if (move.magnitude < 0.1f).
Até aqui é tudo peça de input. Num jogo publicado, controle ainda conversa com menu navegável sem mouse, com ícones que trocam na hora, com pausa que corta a vibração e com uma tela de opções que salva tudo. Quando o projeto cresce, é essa integração que quebra em quem estuda por vídeos soltos, e é ela que o curso de Unity monta passo a passo dentro de jogos completos.
Detectar troca entre teclado e controle
O jogador pega o controle no meio da fase e a dica na tela continua dizendo "aperte E". Para trocar os ícones na hora, observe qual dispositivo acionou a última action.
Arquivo InputDeviceWatcher.cs:
using System;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.DualShock;
using UnityEngine.InputSystem.XInput;
public enum InputFamily
{
KeyboardMouse,
Xbox,
PlayStation,
GenericGamepad
}
public class InputDeviceWatcher : MonoBehaviour
{
public static event Action<InputFamily> FamilyChanged;
public static InputFamily Current { get; private set; } = InputFamily.KeyboardMouse;
private void OnEnable()
{
InputSystem.onActionChange += OnActionChange;
}
private void OnDisable()
{
InputSystem.onActionChange -= OnActionChange;
}
private void OnActionChange(object obj, InputActionChange change)
{
if (change != InputActionChange.ActionPerformed)
{
return;
}
InputAction action = obj as InputAction;
if (action == null || action.activeControl == null)
{
return;
}
InputFamily family = Classify(action.activeControl.device);
if (family != Current)
{
Current = family;
FamilyChanged?.Invoke(family);
}
}
private static InputFamily Classify(InputDevice device)
{
if (device is DualShockGamepad)
{
return InputFamily.PlayStation;
}
if (device is XInputController)
{
return InputFamily.Xbox;
}
if (device is Gamepad || device is Joystick)
{
return InputFamily.GenericGamepad;
}
return InputFamily.KeyboardMouse;
}
}
O componente de UI que mostra a dica assina InputDeviceWatcher.FamilyChanged e troca o sprite. A ordem dos if importa: DualShockGamepad e XInputController também são Gamepad, então os específicos vêm primeiro. Ouvir só ActionPerformed evita que o ícone pisque quando um analógico com drift manda sinal sem o jogador encostar.
Se você usa o componente Player Input, ele já faz isso: o evento Controls Changed dispara quando o esquema troca, e playerInput.currentControlScheme diz qual é. Para mostrar o nome da tecla atual (útil depois de remapear), action.GetBindingDisplayString() devolve o texto do binding ativo.
Vibração com SetMotorSpeeds
Gamepad.SetMotorSpeeds(low, high) liga os dois motores: o de baixa frequência (pesado, para explosão) e o de alta (fino, para tiro e passos). Valores de 0 a 1. Pelo manual, vibração funciona em DualShock 4 no Windows e Mac e em controles Xbox no Windows; em WebGL, os controles PlayStation funcionam só como gamepad básico, sem vibração.
Arquivo GamepadRumble.cs:
using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;
public class GamepadRumble : MonoBehaviour
{
private Coroutine rumbleRoutine;
public void Pulse(float low, float high, float duration)
{
Gamepad pad = Gamepad.current;
if (pad == null)
{
return;
}
if (rumbleRoutine != null)
{
StopCoroutine(rumbleRoutine);
}
rumbleRoutine = StartCoroutine(PulseRoutine(pad, low, high, duration));
}
private IEnumerator PulseRoutine(Gamepad pad, float low, float high, float duration)
{
pad.SetMotorSpeeds(low, high);
yield return new WaitForSecondsRealtime(duration);
pad.SetMotorSpeeds(0f, 0f);
rumbleRoutine = null;
}
public void SetPaused(bool paused)
{
if (paused)
{
InputSystem.PauseHaptics();
}
else
{
InputSystem.ResumeHaptics();
}
}
private void OnDisable()
{
InputSystem.ResetHaptics();
}
}
WaitForSecondsRealtime garante que a vibração para mesmo se o jogo estiver com Time.timeScale = 0 no pause. ResetHaptics no OnDisable é o seguro contra o bug mais irritante: trocar de cena no meio de uma explosão e o controle ficar vibrando no menu. Para o efeito ficar bom, pense em vibração como som: curta, com intensidade proporcional ao impacto e nunca constante.
Testar sem ter todos os controles
Ninguém tem todos os controles em casa. O que dá para fazer:
- Input Debugger (Window > Analysis > Input Debugger): mostra cada dispositivo conectado, com o layout que a Unity escolheu. Se o seu DualSense aparece como XInputController, tem alguém traduzindo (Steam Input ou um driver).
- Dispositivo virtual:
InputSystem.AddDevice<DualShock4GamepadHID>()cria um controle de PlayStation falso. Ele não manda input sozinho, mas faz oClassifydo código acima devolver PlayStation e permite testar a troca de ícones. Remova comInputSystem.RemoveDevice(device). - Um controle real de cada família que você promete: se a página da loja diz "suporte a controle", teste pelo menos um Xbox e um PlayStation antes de publicar.
Erros comuns
Controle funciona no editor e não no build. O Game view do editor precisa de foco para receber input; no build, confira em Project Settings > Input System Package se Background Behavior não está descartando input quando a janela perde foco.
Personagem anda sozinho. Drift sem deadzone. Adicione Stick Deadzone no binding.
Gatilho que não conta como botão. Action do tipo Value com binding de gatilho nunca dispara WasPressedThisFrame do jeito que você espera. Para atirar, use Button; para acelerar, use Value e leia o float.
Vibração que não para. Falta SetMotorSpeeds(0, 0) ou ResetHaptics no fim da cena.
Próximo passo
Com controle funcionando no gameplay, o próximo gargalo é o menu: jogador de controle não tem mouse para clicar em "Jogar". Configure o EventSystem com First Selected e navegação explícita. Se você também trabalha em Godot, compare com o suporte a controle e joystick na Godot, que resolve o mesmo problema pelo Input Map e pela API de joypad.
Perguntas frequentes
A Unity reconhece controle de PS4 e PS5 no PC?
Sim, com o Input System. O manual do pacote lista o DualShock 4 com suporte em Windows, macOS e Linux, e o DualSense em Windows e macOS. Os dois aparecem como Gamepad, então os mesmos bindings de <Gamepad> funcionam sem código extra. Se o jogo roda pela Steam com Steam Input ligado, o controle pode chegar como um controle Xbox.
Por que meu controle genérico não funciona na Unity?
Controles HID genéricos que a Unity não reconhece não viram Gamepad, viram Joystick. Bindings como <Gamepad>/buttonSouth não pegam nada neles. Adicione bindings de <Joystick> nas actions ou confira no Input Debugger como o aparelho foi identificado.
Como fazer o controle vibrar na Unity?
Chame Gamepad.current.SetMotorSpeeds(low, high) com valores de 0 a 1 para o motor de baixa e de alta frequência, e depois SetMotorSpeeds(0, 0) para parar. Use InputSystem.PauseHaptics no menu de pausa e ResetHaptics ao sair da cena, para o controle não ficar vibrando.
Como corrigir drift do analógico na Unity?
Use o processor Stick Deadzone na action de movimento. Por padrão ele ignora valores abaixo de 0,125 e trata como máximo tudo acima de 0,925. Se o controle do jogador tem drift maior, aumente o mínimo, de preferência numa opção do menu.



