Voltar para o Blog
Quest Log

New Input System da Unity: Tutorial do Zero em C#

Personagem 2D andando numa plataforma enquanto teclado e controle aparecem ligados a ele por linhas de luz

Unity New Input System do zero: actions do Unity 6, InputSystem.actions, Player Input e classe gerada em C#, com código pronto e a correção do erro do Input.

Ferramenta:UnityC#

New Input System da Unity: Tutorial do Zero em C#

O Unity New Input System é o pacote oficial de entrada da Unity, e na Unity 6 ele já vem instalado e ativo em todo projeto novo, com um asset de actions pronto para usar. Você declara ações com nome (Move, Jump, Attack), liga teclas, botões de controle e toques a elas, e o seu código lê a ação. Aqui você sai com um personagem 2D andando e pulando, lido de três jeitos diferentes, e sabendo qual escolher para o seu jogo.

Todo o código foi escrito para a Unity 6.3 LTS com o pacote Input System 1.20 (a versão que a documentação da Unity lista como liberada para o editor 6000.3 em outubro de 2026). Se você caiu aqui por causa do erro "You are trying to read Input using the UnityEngine.Input class", a seção dele está lá embaixo, depois dos três jeitos.

Unity new input system: por que usar

O Input Manager clássico, aquele do Input.GetAxis("Horizontal"), funciona bem enquanto o jogo tem só teclado. O problema aparece quando você quer controle de Xbox, depois controle de PlayStation, depois toque no celular, depois deixar o jogador trocar a tecla de pular. Cada dispositivo vira um if a mais espalhado pelos scripts, e a tela de remapear controles fica praticamente impossível.

O Input System resolve isso com uma camada no meio. O script do jogador pergunta "a ação Jump foi pressionada neste frame?" e não sabe se foi espaço, botão A ou toque. Quem decide isso são os bindings, configurados num asset. Na prática você ganha três coisas:

  • Um código para todos os dispositivos. Teclado, gamepad e touch disparam a mesma action.
  • Remapeamento pronto. O pacote tem API de rebinding e salva as trocas em JSON.
  • Eventos além de polling. Você pode ler o valor todo frame ou receber um callback quando a ação acontece.

O próprio manual da Unity 6.3 chama o Input Manager de sistema legado e diz que, por padrão, quem fornece input na Unity é o Input System Package. Os exemplos novos da documentação, os templates e os samples partem dele.

Instalar e ativar o pacote

Em projeto criado na Unity 6 você não precisa fazer nada: o pacote com.unity.inputsystem já está lá. Para conferir, abra Window > Package Management > Package Manager, procure "Input System" em In Project e veja a versão.

Em projeto que veio de uma versão anterior da Unity, faça assim:

  1. No Package Manager, aba Unity Registry, procure Input System e clique em Install.
  2. A Unity pergunta se quer ativar o novo backend e reiniciar o editor. Responda Yes.
  3. Confira em Edit > Project Settings > Player > Other Settings > Configuration o campo Active Input Handling. As opções são Input Manager (Old), Input System Package (New) e Both.
  4. Em Edit > Project Settings > Input System Package, se o projeto ainda não tem actions de projeto, use o botão que cria o asset padrão. Ele nasce com o nome InputSystem_Actions.

A partir da versão 1.8 do pacote existe o conceito de project-wide actions: um asset de actions registrado no projeto inteiro, acessível de qualquer script por InputSystem.actions, e que já vem habilitado. É isso que deixa o jeito 1 deste tutorial tão curto.

Input Actions: maps, actions e bindings

Dê dois cliques em InputSystem_Actions na pasta Assets. Abre a janela do editor de actions com três colunas:

  • Action Maps: grupos de ações que ligam e desligam juntos. O asset padrão tem um map Player (jogo) e um map UI (menus). Quando o jogo pausa, você desliga Player e o personagem para de responder, sem nenhum if (paused) no script dele.
  • Actions: as ações em si. No map Player do asset padrão você encontra Move, Look, Attack, Interact, Crouch, Jump, Previous, Next e Sprint.
  • Bindings: os controles ligados a cada action. Move, por exemplo, tem um composite WASD/setas e o analógico esquerdo do gamepad, entre outros.

Cada action tem um Action Type. Value é para coisas contínuas (Move devolve um Vector2), Button é para apertar e soltar (Jump), e Pass Through entrega todo valor que chega sem filtrar qual controle está ativo. Para movimento, Value com Control Type Vector2. Para pulo, Button.

Uma regra que evita metade dos bugs: o nome da action é o contrato entre o asset e o código. Se você renomear "Jump" para "Pular" no asset, todo FindAction("Jump") passa a devolver null. Escolha os nomes cedo e não mexa mais.

Jeito 1: ler direto pelo código

É o jeito que eu uso em jogo de um jogador só. Crie um objeto com Rigidbody2D e BoxCollider2D, ponha um chão embaixo e adicione o script.

Arquivo PlayerMovement.cs:

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerMovement : MonoBehaviour
{
    [SerializeField] private Rigidbody2D rb;
    [SerializeField] private float speed = 5f;
    [SerializeField] private float jumpForce = 8f;

    private InputAction moveAction;
    private InputAction jumpAction;
    private Vector2 moveInput;
    private bool jumpRequested;

    private void Awake()
    {
        moveAction = InputSystem.actions.FindAction("Player/Move", true);
        jumpAction = InputSystem.actions.FindAction("Player/Jump", true);
    }

    private void Update()
    {
        moveInput = moveAction.ReadValue<Vector2>();

        if (jumpAction.WasPressedThisFrame())
        {
            jumpRequested = true;
        }
    }

    private void FixedUpdate()
    {
        rb.linearVelocity = new Vector2(moveInput.x * speed, rb.linearVelocity.y);

        if (jumpRequested)
        {
            rb.linearVelocity = new Vector2(rb.linearVelocity.x, jumpForce);
            jumpRequested = false;
        }
    }
}

Três decisões aí dentro que valem explicar:

  • FindAction("Player/Move", true): o caminho com o nome do map evita pegar uma action homônima de outro map, e o true faz a Unity lançar exceção na hora se o nome estiver errado, em vez de te deixar com um null misterioso mais tarde.
  • A leitura acontece no Update e a física no FixedUpdate. O WasPressedThisFrame vale para o frame de renderização; se você ler no FixedUpdate, pode perder o aperto num frame em que a física não roda. Por isso o pulo vira uma flag.
  • rb.linearVelocity: na Unity 6 a propriedade se chama assim. O velocity dos tutoriais antigos não existe mais no Rigidbody2D.

Se você prefere arrastar a action no Inspector em vez de usar string, troque o campo por InputActionReference:

Arquivo PlayerMovementRef.cs:

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerMovementRef : MonoBehaviour
{
    [SerializeField] private Rigidbody2D rb;
    [SerializeField] private InputActionReference moveAction;
    [SerializeField] private float speed = 5f;

    private void OnEnable()
    {
        moveAction.action.Enable();
    }

    private void FixedUpdate()
    {
        Vector2 move = moveAction.action.ReadValue<Vector2>();
        rb.linearVelocity = new Vector2(move.x * speed, rb.linearVelocity.y);
    }
}

No Inspector, o campo mostra um seletor com as actions do asset. A vantagem é que renomear a action não quebra a referência. O Enable() é inofensivo quando a action já está ligada (caso das project-wide actions) e obrigatório quando ela vem de outro asset.

Jeito 2: componente Player Input

O componente Player Input faz a ponte entre o asset e os seus scripts sem você buscar action nenhuma. Selecione o personagem, Add Component > Player Input, arraste o InputSystem_Actions para o campo Actions, escolha Default Map = Player e defina o Behavior:

  • Send Messages: chama métodos com o nome On + nome da action nos componentes do mesmo objeto (OnMove, OnJump).
  • Invoke Unity Events: mostra uma lista de eventos no Inspector, um por action, e você liga cada um a um método público.
  • Invoke C# Events: dispara onActionTriggered para você assinar por código.

Com Send Messages, o script fica assim. Arquivo PlayerMessages.cs:

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerMessages : MonoBehaviour
{
    [SerializeField] private Rigidbody2D rb;
    [SerializeField] private float speed = 5f;
    [SerializeField] private float jumpForce = 8f;

    private Vector2 moveInput;

    private void OnMove(InputValue value)
    {
        moveInput = value.Get<Vector2>();
    }

    private void OnJump(InputValue value)
    {
        if (value.isPressed)
        {
            rb.linearVelocity = new Vector2(rb.linearVelocity.x, jumpForce);
        }
    }

    private void FixedUpdate()
    {
        rb.linearVelocity = new Vector2(moveInput.x * speed, rb.linearVelocity.y);
    }
}

O ponto forte do Player Input é multiplayer local. Com um Player Input Manager na cena, cada controle que aperta um botão ganha uma cópia do prefab do jogador, com o próprio dispositivo pareado. Ele também expõe o esquema de controle atual (teclado ou gamepad), útil para trocar ícones de botão na tela. O ponto fraco é a mágica por nome: errou OnJunp e nada acontece, sem erro nenhum no Console.

O Input System é uma peça. Num jogo de verdade ela convive com física, animação, câmera, UI e salvamento, e é nessa costura que quem estuda sozinho costuma travar: cada tutorial ensina a peça isolada e nenhum mostra como elas conversam num projeto que vai até o fim. Se é aí que você está, o curso de Unity monta um jogo completo com essas peças encaixadas, do input ao build.

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

Jeito 3: classe C# gerada

Selecione o asset InputSystem_Actions no Project, marque Generate C# Class no Inspector e clique em Apply. A Unity gera um arquivo .cs com uma classe do mesmo nome do asset, com propriedades tipadas para cada map e cada action.

Arquivo PlayerGenerated.cs:

using UnityEngine;

public class PlayerGenerated : MonoBehaviour
{
    [SerializeField] private Rigidbody2D rb;
    [SerializeField] private float speed = 5f;
    [SerializeField] private float jumpForce = 8f;

    private InputSystem_Actions controls;
    private Vector2 moveInput;
    private bool jumpRequested;

    private void Awake()
    {
        controls = new InputSystem_Actions();
    }

    private void OnEnable()
    {
        controls.Player.Enable();
    }

    private void OnDisable()
    {
        controls.Player.Disable();
    }

    private void OnDestroy()
    {
        controls.Dispose();
    }

    private void Update()
    {
        moveInput = controls.Player.Move.ReadValue<Vector2>();

        if (controls.Player.Jump.WasPressedThisFrame())
        {
            jumpRequested = true;
        }
    }

    private void FixedUpdate()
    {
        rb.linearVelocity = new Vector2(moveInput.x * speed, rb.linearVelocity.y);

        if (jumpRequested)
        {
            rb.linearVelocity = new Vector2(rb.linearVelocity.x, jumpForce);
            jumpRequested = false;
        }
    }
}

Aqui não existe string: controls.Player.Jump é uma propriedade, então um erro de digitação vira erro de compilação. O detalhe que pega muita gente é que new InputSystem_Actions() cria uma cópia nova das actions. Ela não é a mesma instância de InputSystem.actions. Se a sua tela de opções remapeia as project-wide actions e o personagem lê pela classe gerada, o remapeamento não chega no personagem. Escolha uma fonte e use só ela no projeto.

Qual escolher

Depois de fazer alguns jogos com os três, a regra que uso é esta:

SituaçãoJeito
Jogo de um jogador, protótipo, game jam1: InputSystem.actions
Quer arrastar a action no Inspector1 com InputActionReference
Multiplayer local, um jogador por controle2: Player Input + Player Input Manager
Projeto grande, time, quer checagem do compilador3: classe gerada

Misturar é possível, mas cobra caro em depuração. O erro clássico é o Player Input ligado num objeto e um script lendo InputSystem.actions em outro, os dois achando que controlam o mesmo personagem.

Corrigir "You are trying to read input using the UnityEngine.Input class"

A mensagem completa é parecida com esta:

InvalidOperationException: You are trying to read Input using the UnityEngine.Input class, but you have switched active Input handling to Input System package in Player Settings.

A causa é a configuração Active Input Handling. Com ela em Input System Package (New), a classe UnityEngine.Input fica desligada, e qualquer chamada a Input.GetAxis, Input.GetKey, Input.GetMouseButton ou Input.mousePosition lança essa exceção. Como projeto novo na Unity 6 nasce com o Input System ativo, basta colar um script de tutorial antigo para ver o erro.

Existem duas correções:

  1. Migrar o trecho para o Input System. É a correção certa. Input.GetAxis("Horizontal") vira a leitura da action Move, Input.GetKeyDown(KeyCode.Space) vira Keyboard.current.spaceKey.wasPressedThisFrame ou a action Jump, e Input.mousePosition vira Mouse.current.position.ReadValue().
  2. Trocar Active Input Handling para Both. Em Edit > Project Settings > Player > Other Settings > Configuration, escolha Both e deixe o editor reiniciar. Os dois sistemas passam a funcionar ao mesmo tempo.

Quando usar Both: quando o erro vem de um asset da Asset Store ou de código de terceiros que você não quer reescrever agora, como um pacote de câmera antigo ou um EventSystem com StandaloneInputModule. No caso específico do EventSystem, nem precisa de Both: selecione o objeto EventSystem, e o Inspector mostra um botão para substituir o módulo antigo pelo InputSystemUIInputModule. Para o resto, Both destrava o projeto enquanto você migra aos poucos. Só não trate Both como estado final, porque dois sistemas lendo o mesmo teclado é receita para input duplicado.

Erros comuns

A action devolve sempre zero. A action está desabilitada. Project-wide actions já começam ligadas, mas actions de outro asset, de InputActionReference ou da classe gerada precisam de Enable(). Também confira se você não desligou o map Player num menu de pausa e esqueceu de religar.

FindAction devolve null. Nome diferente do asset (maiúsculas contam) ou o asset que você editou não é o que está registrado como project-wide. Confira em Project Settings > Input System Package.

O pulo às vezes não sai. Leitura de botão dentro do FixedUpdate. Leia no Update, guarde numa flag e consuma na física, como no jeito 1.

Teclado funciona e o controle não. Falta binding de gamepad na action, ou o Player Input está com um Default Scheme fixo em Keyboard&Mouse.

Funciona no editor e não no build. Geralmente é a classe gerada desatualizada depois de editar o asset sem clicar em Apply, ou um script lendo um asset diferente do que foi para o build.

Próximo passo

Com o personagem andando, pratique o que mais aparece em jogo de plataforma: ler o botão no frame certo e segurar para carregar. O post sobre como checar se o botão foi pressionado no Input System cobre WasPressedThisFrame, IsPressed e a interação Hold. Se você ainda está decidindo se vale aprender a engine, leia qual linguagem a Unity usa. E se quer comparar com outra engine, o Input Map da Godot resolve o mesmo problema com outra filosofia, e entender os dois deixa claro por que separar ação de tecla é a decisão certa.

Perguntas frequentes

O New Input System já vem instalado na Unity 6?

Sim. Projetos novos criados na Unity 6 já vêm com o pacote Input System instalado, com ele ativo em Active Input Handling e com um asset de actions padrão chamado InputSystem_Actions configurado como project-wide actions. Em projeto antigo atualizado você instala pelo Package Manager e troca a configuração no Player Settings.

O que são Input Actions na Unity?

Input Actions são ações com nome (Move, Jump, Attack) desacopladas das teclas. Cada action tem bindings, que dizem quais controles disparam aquela ação: espaço no teclado, botão sul no gamepad, toque na tela. O seu código lê a action, não a tecla, e por isso o mesmo script funciona em teclado, controle e celular.

Devo usar PlayerInput ou ler as actions direto no código?

Para jogo de um jogador só, ler pelo código com InputSystem.actions é o caminho mais curto e mais fácil de depurar. O componente Player Input brilha em multiplayer local, porque cria um jogador por controle conectado e cuida da troca de esquema de controle. A classe C# gerada serve quando você quer autocomplete e erro de compilação em vez de string.

Como corrigir "You are trying to read Input using the UnityEngine.Input class"?

O erro aparece quando algum script usa Input.GetAxis, Input.GetKey ou similar e o projeto está configurado só com o Input System. Ou você reescreve aquele trecho com o Input System, ou muda Active Input Handling para Both em Project Settings, Player, Other Settings. Both resolve rápido, mas o certo a longo prazo é migrar o código.