Como usar o novo Input System da Unity

Controles são essenciais, já que são com eles que o jogador interage com o mundo do jogo. Mas até a alguns anos atrás a Unity não oferecia um sistema dinâmico e robusto suficiente, fazendo que muitas vezes fosse necessário usar ferramentas de terceiros ou criar suas próprias soluções. Sabendo disso, a Unity desenvolveu um novo Input System com o objetivo de resolver esses e outros problemas.

Apesar do novo Input System ser melhor, muitos desenvolvedores ainda não migraram para ele, pois à primeira vista ele parece mais complicado. Mas ao final desse tutorial você verá que isso não é verdade e terá o conhecimento necessário para começar a criar seus próprios controles usando o Input System.

Como a melhor forma de aprender é botando a mão na massa, após entendermos como o Input System funciona, vamos colocar esse conhecimento em prática ao criar o controle para uma espaçonave. Ao final do exemplo nossa espaçonave deverá conseguir se mover e atirar.

Antes de começarmos

Para esse tutorial será necessário que você tenha conhecimento básico sobre a Unity e C#, já que não vamos ver como criar um novo projeto ou como criar scripts novos por exemplo.

O novo sistema de Input está disponível para versões da Unity a partir de 2019.1 através do Package Manager. Porém para esse tutorial irei usar a Unity 2021.3.16f1 e o Input System 1.4.4. Recomendo fazer o mesmo uma vez que é que existem diferenças entre as diferentes versões do pacote e não falarei sobre elas aqui.

Para facilitar, criei um projeto como ponto de partida, neste projeto você irá encontrar uma cena como tudo o que é necessário para começar esse tutorial. Você pode baixar o projeto aqui. Para importar o projeto basta arrastar o arquivo que foi baixado até o editor já aberto. Ou se preferir você pode criar a sua própria cena já o que for importante faremos durante o tutorial.

O projeto final e todo o código fonte está disponível no GitHub.

Vamos lá?

Por que usar o novo Input System?

Permite a separação entre controle e códigos: No antigo Input System precisamos checar no nosso código qual tecla foi pressionada, isso fazia com que tivéssemos código espalhado por todo o projeto. Por si só não tem nada de errado com isso, porém caso queiramos mudar um botão precisamos modificar o código diretamente em vários lugares diferentes. Isso é resolvido no novo Input System, já que criamos um asset no projeto que mapeia em uma lista todas as ações que o jogador pode realizar e quais os botões que as acionam, dessa forma podemos adicionar ou modificar os botões sem tocar no código que escrevemos.

É baseado em eventos: No antigo Input System precisamos checar todos os quadros se uma tecla foi pressionada, pode parecer pouco, mas são preciosos recursos que gastamos quando queremos que nossos jogos rodem a 60fps. Com o novo Input System a engine fica responsável por checar se uma tecla foi pressionada e dispara um evento quando isso ocorre, dessa forma a Unity pode otimizar da melhor forma possível. Do nosso lado, não precisamos nos preocupar com qual botão é pressionado e sim com qual ação o jogador realizou e inscrever os métodos que deverão ser chamados quando esse evento acontecer.

Mais fácil utilizar o mesmo código em diferentes plataformas: Outra vantagem é que o novo Input System permite que adicionamos suporte a novas plataformas sem trabalho extra. Já que ele tem suporte a uma grande quantidade de plataforma por padrão. Para adicionarmos mais plataforma ao nosso jogo, basta adicionar um novo botão a lista de botões de uma ação.

Suporte para teclas modificadoras: O novo sistema dá suporte a teclas modificadoras por padrão, isso é, podemos acionar uma ação apenas se dois botões forem pressionados ao mesmo tempo, por exemplo.

Permite mudar as teclas durante o jogo: O antigo Input System não permitia que mudássemos as teclas durante o jogo, como isso é essencial para acessibilidade, precisamos criar nossa própria solução ou usar uma de terceiro. Esse problema está resolvido, já que agora podemos fazer isso no novo Input System.

Ferramentas para depuração: Por último, mas não menos importante, o novo Input System vem com uma série de ferramentas para auxiliar na criação e depuração dos controles.

Como instalar o novo Input System?

Antes de podermos usar o novo sistema, precisamos instalar ele em nosso projeto através do Package Manager. Para isso, clique em Window > Package Manager.

Na janela que se abriu, selecione Unity Registry no dropdown.

Agora procure pelo pacote Input System, você pode rolar a lista até encontrar o pacote ou usar a barra de pesquisa.

Selecione o pacote na lista e clique em Install.

Após a instalação, a Unity irá perguntar se você deseja ativar ele. Ao clicar em Yes o editor será reiniciado e o Input System padrão deixará de funcionar. Caso ainda não esteja pronto clique em No.

Você pode mudar essa configuração na janela Project Settings. Para isso, clique em Edit > Project Settings.

Na janela que se abriu clique na opção Player. Abra a aba Other Settings e role a lista até encontrar a opção Active Input Handling. 

Clique no dropdown e selecione se você quer usar o novo ou o antigo Input System ou até mesmo os dois ao mesmo tempo. Não se esqueça de salvar o projeto.

Como o novo Input System funciona?

Antes de começarmos a usar o Input System precisamos entender duas partes importantes do sistema, o Input Action Asset e o Player Input. 

O Input Action Asset é um arquivo que criamos em nosso projeto, seu papel é guardar todas as ações que o jogador pode realizar, quais os botões que ativam essas ações e outras configurações. 

Já o Player Input é um componente que adicionamos ao Game Object que representa o jogador, ele é responsável por gerenciar os eventos que ocorrem devido no Input System. É através dele que iremos inscrever os métodos que queremos que sejam chamados quando um evento ocorre.

Como usar  o Input Action Asset?

Antes de mais nada precisamos criar um Input Action Asset. Para isso clique com o botão direito do mouse na janela Project e no menu que aparecerá clique em Create > Input Action, com isso um novo arquivo será criado no projeto.

Dê um nome de sua preferência para ele, eu irei nomeá-lo PlayerInputActions. Abra o arquivo clicando duas vezes sobre o ele ou selecione-o e clique no botão Edit asset na janela Inspector.

Como dito anteriormente, esse arquivo será usado para configurar e guardar as ações que o jogador pode realizar durante o jogo.

Quando abrirmos essa janela pela primeira vez ela estará vazia, mas podemos ver que ela é divida em três seções. Leve um tempo para se familiarizar com a interface.

Action Maps: Action Maps são grupos de ações que o jogador pode realizar durante o jogo. Isso nos permite agrupar ações que servem para diferentes propósitos e ativar/desativar dependendo do que o jogador estiver fazendo naquele momento. Por exemplo, podemos criar diferentes Action Maps para quando o jogador estiver andando, nadando, em um veículo, ou usando algum menu.

Actions: Esta é a lista de ações que o jogador pode realizar em um determinado mapa de ações. Cada ação mantém uma lista de que botão irá acionar essa ação.

Properties: Aqui podemos editar as propriedades de uma ação ou botão. Aqui podemos definir se uma ação é um valor ou um botão, ou qual botão será usado para acionar essa ação, também podemos definir como uma ação será executada, ou adicionar Processors para modificar o resultado da ação.

Como usar o componente Player Input?

Como dito antes, o Player Input é responsável por gerenciar os eventos que ocorrem no Input System e é através dele que iremos adicionar os métodos que queremos que sejam executados quando uma ação ocorre. Como o Player Input é um componente, precisamos adicionar ele a um Game Object na cena para que possamos usá-lo.

Para isso, crie um novo Game Object e com ele selecionado clique no botão Add Componente na janela Inspector e procure por Player Input. Agora podemos aprender sobre as propriedades mais importantes.

Na propriedade Actions podemos definir qual Input Action Asset o Player Input irá usar. Não precisamos nos preocupar com isso agora, já que faremos isso quando colocarmos tudo isso em prática. Caso ainda não tenhamos criado o arquivo podemos clicar no botão Create Actions e a Unity irá criar um arquivo com algumas ações já definidas.

Na propriedade Default Action Map podemos definir qual Action Map será ativado por padrão, caso você selecione None, o jogador irá começar sem poder realizar nenhuma ação e você terá que definir um mais tarde através do código.

As propriedades UI Input Module e Camera, são especialmente úteis para jogos multiplayer. Como não irei falar sobre esse assunto nesse tutorial, podemos ignorá-las por enquanto. Caso exista interesse falarei sobre como usar o Input System para jogos multiplayer em outro momento.

Na propriedade Behavior podemos definir como o componente enviará os eventos do Input System para o nosso código. Aqui temos 4 opções:

Primeiro temos Send Messages e Broadcast Messages. Eles funcionam de maneira similar, isso é, o Player Input irá procurar métodos com certos nomes em todos os componentes do nosso jogador. A diferença é que o Send Messages irá procurar apenas no game object em que o Player Input é adicionado. Já o Broadcast Messages irá procurar em todos os game objects que são filhos dele na hierarquia. Como você pode imaginar, essas duas maneiras de enviar eventos podem se tornar lentos dependendo da quantidade de componentes e game objects que precisarem procurar.

Em seguida temos a Invoke Unity Events. Ao selecionar esse Behavior, uma nova propriedade chamada Events irá aparecer. Se você já usou Unity Events com a UI, por exemplo o evento OnClick no componente Button, você não terá dificuldade alguma para usar esse método.

Basicamente temos uma lista com todos os Action Maps que criamos no Input Action Asset e ao clicar em um deles uma lista com todas as Actions que pertencem a esse Action Map. E ao clicar no sinal de mais podemos selecionar um Game Object que está na hierarquia e selecionar um método em um de seus componentes. Quando o jogador executar a ação esse método será chamado. Podemos definir quantos métodos quisermos para cada evento.

Por último, Invoke C Sharp Events, aqui o Player Input irá chamar os eventos das ações através de eventos do C#. Esse método é o que nos dá mais controle sobre os eventos de cada ação em nosso código. Porém, em contrapartida temos que fazer muito mais manualmente e tudo deve ser feito no código, dificultando o trabalho de designer e artistas.

Para mim, tanto Invoke Unity Events e Invoke C Sharp Events são as melhores opções. Eu recomendaria usar Send Messages apenas quando você estiver fazendo algo mais simples, já que é o jeito mais fácil de usar o sistema. Enquanto Broadcast Messages você pode ignorar, já que as outras opções são melhores.

Para esse tutorial vamos usar Invoke Unity Events já que é um bom meio termo entre controle do código e facilidade de uso, tanto para quem está programando quanto para designer e artistas.

Como usar o novo sistema?

Com toda essa informação fora do caminho, podemos finalmente criar o controle para a nossa espaçonave.

Primeiramente, crie um Input Action Asset caso ainda não tenha feito. Para isso clique com o botão direito do mouse na janela Project e no menu que aparecerá clique em Create > Input Action.

Dê o nome de sua preferência para ele, eu irei nomeá-lo PlayerInputActions. Em seguida, abra o arquivo clicando duas vezes sobre o ele.

Como controlar o jogador usando o novo Input System?

Agora podemos criar nosso Action Map. Para isso clique no sinal de mais seção Action Maps. Podemos nomear o Action Map como Gameplay, já que esse mapa será usado para guardar as ações que o jogador poderá fazer durante o jogo normal.

Agora clique no sinal de mais na seção Actions. Isso irá criar uma nova ação. Iremos nomeá-la como Move. Essa ação será responsável por fazer a espaçonave se mover.

Com essa Action selecionada, olhe para o painel Action Properties. Clique no dropdown Action Type. Podemos ver que temos três opções.

Value: É usado quando uma ação requer que o valor seja acompanhado continuamente. Por exemplo no movimento. Aqui o valor passa por um processo de desambiguação, isso é, o sistema irá escolher qual o controle mais adequado para executar a ação.

Button: É usado quando precisamos que uma ação seja executada apenas uma vez.

Pass Through: É o mesmo que value, porém o valor não passa pelo processo de desambiguação, fazendo com que a ação possa ser executada por qualquer controle que esteja conectado.

Como queremos que nossa espaçonave se mova constantemente em uma direção e que mude de direção sempre que o jogador pressionar mais de um botão, devemos escolher o Action Type Value.

Ao fazer isso, a opção Control Type irá aparecer. Clique no dropdown e verá que temos várias opções, para o nosso caso queremos Vector 2, isso irá fazer com que o sistema passe um vetor para o evento, isto é, um valor que representa uma posição ou direção no mundo. Isso irá nos permitir que saibamos a direção que o jogador quer se mover.

De volta ao painel Actions. Vamos adicionar nosso primeiro Binding, isto é, qual botão irá acionar essa ação quando pressionado. Para isso clique no sinal de mais ao lado do nome da Action. As opções desse menu irão mudar conforme o Control Type que selecionamos. Como escolhemos Vector 2 temos a opção de adicionar um binding que irá nos permitir usar as teclas WASD do teclado para controlar a direção da espaçonave. Para isso, selecione a opção “add Up/Down/Left/Right composite”. Podemos nomear esse Binding como WASD para mais tarde sabermos do que ele se trata.

Agora selecione o Binding Up. E no painel Binding Properties, temos algumas opções diferentes. O que nos interessa no momento é a propriedade Path. Clique no dropdown e um menu irá se abrir com todos os botão que podemos usar para acionar essa ação.

Você pode escolher qual botão você deseja navegando pelas opções, usando a caixa de pesquisa, ou clicar no botão Listen, isso fará a Unity mostrar o próximo botão que você pressionar. Use o método que preferir e selecione qual botão será usado para fazer a espaçonave se mover para direção do topo da tela. Eu escolhi a tecla W no teclado.

Agora faremos o mesmo processo para as outras direções. Selecione a tecla S para Down. Selecione a tecla A para Left. E por fim selecione a tecla D para a Right.

Aliás, você pode clicar com o botão direito do mouse sobre os Action Maps, ações ou bindings para abrir um menu com opções para duplicar, renomear, apagar, entre outras. Caso queira, remova a Action e o Binding que ficaram vazios.

Após criar sua Action não se esqueça de salvar suas mudanças clicando no botão Save asset. Ou as mudanças não terão efeito durante o jogo. Você também pode clicar no checkbox Auto-save para salvar automaticamente cada vez que fizer uma alteração. Eu particularmente não gosto já que conforme a lista de mapas e ações e bindings cresce a Unity tem a tendência de ficar sem responder por alguns instantes, o que atrapalha o trabalho.

Com isso configuramos os controles para a movimentação da espaçonave. Agora vamos ver como podemos usar eles para fazer a nave se mover durante o jogo.

Para isso importe o projeto que disponibilizei no início do tutorial.

Após importar o projeto, abra a cena SinglePlayer, que está localizada na pasta Scenes. Em seguida, selecione na hierarquia o Game Object Player.

Em seguida adicione o componente Player Input. Para isso clique no botão Add Component na janela Inspector e procure pelo componente.

Na propriedade Actions do componente, clique na bolinha e selecione o Input Action Asset que criamos.

Agora, clique com o botão direito na pasta Scripts e no menu que seu abriu selecione Create > C# Script, nomeio como PlayerController. 

Abra o arquivo no seu editor de texto favorito, eu irei utilizar o Visual Studio.

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    [SerializeField]
    private float moveSpeed = 8.0f;

    private Vector2 moveDirection;

    public void OnMove(InputAction.CallbackContext context)
    {
        moveDirection = context.ReadValue<Vector2>();
    }

    private void Update()
    {
        transform.Translate(moveSpeed * Time.deltaTime * moveDirection);
    }
}

Antes de começarmos a usar o Input System no nosso script precisamos não podemos nos esquecer de declarar using UnityEngine.InputSystem; no início do arquivo. 

Em seguida declaramos a variável moveSpeed, ela é responsável por controlar a velocidade com que a nave se move, marcamos ela com SerializeField para que possamos módica-la diretamente no editor.

Já a variável moveDirection é responsável por guardar a direção em que o jogador está se movendo. Esse valor será modificado toda vez que a Action Move for executada, ou seja, quando o jogador pressionar umas das teclas WASD.

Em seguida declaramos o método OnMove. Ele receberá como parâmetro InputAction.CallbackContext, essa classe é fornecida pelo Input System, através dela temos acesso a uma série de informações sobre a Action que está sendo executada. Tanto o nome do método quanto o da variável não são importantes, podemos usar o nome que quisermos.

Porém para um método poder ser adicionado à lista de métodos que serão chamados quando uma ação acontecer é necessário que ele tenha um parâmetro InputAction.CallbackContext e somente ele. 

Em seguida usamos o método ReadValue, fornecido pela classe InputAction.CallbackContext através da variável context para recuperar o Vector2 que representa a direção que o jogador quer se mover.

Por fim, no método Update, que é um método especial da Unity que é chamado todo quadro. Usamos o método Translate para fazer a nave se mover. Para isso calculamos onde a nave deve estar no próximo quadro multiplicando a velocidade de movimento por Time.deltaTime, para garantir que a nave se mova de forma suave independente da quantidade de quadros, então multiplicamos novamente pela direção do movimento.

Salve o arquivo e volte a Unity.

Selecione o Game Object Player na hierarquia e adicione o script PlayerController como componente. Para isso clique no botão Add Component na janela Inspector e procure por Player Controller.

Em seguida no componente Player Input, clique em Events e em seguida em Gameplay. Como dito anteriormente, aqui está a lista de todas as Actions que podem acontecer.

Clique no sinal de mais localizado na parte de baixo da Action Move. Clique e arraste o Game Object Player para o campo com a bolinha. Em seguida clique no dropdown e selecione o método que criamos no script Player Controller (Player Controller > OnMove).

Agora clique no botão Enter Play Mode. Pressione as teclas WASD e a espaçonave deverá se mover na tela.

Porém, você verá que ela não aponta para direção que está se movendo. Podemos resolver esse problemas adicionando algumas linhas no método OnMove.

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    [SerializeField]
    private float moveSpeed = 8.0f;

    private Vector2 moveDirection;

    public void OnMove(InputAction.CallbackContext context)
    {
        moveDirection = context.ReadValue<Vector2>();

        if (moveDirection.magnitude > Mathf.Epsilon)
        {
            Quaternion lookRotation = Quaternion.LookRotation(Vector3.forward, moveDirection);
            transform.rotation = lookRotation;
        }
    }

    private void Update()
    {
        transform.Translate(moveSpeed * Time.deltaTime * moveDirection, Space.World);
    }
}

Primeiro checamos se a nave está se movendo. Para isso usamos a magnitude de moveDirection, caso o jogador estiver pressionando qualquer tecla para se mover esse valor será maior que zero. Então usamos o método LookRotation fornecido pela classe Quaternion para calcular a rotação necessária para a nave olhar para a direção correta e então modificamos a rotação da nave com esse valor.

Por último no método Update. Adicionamos o argumento Space.World ao método Translate para que o movimento ocorra em relação ao mundo, isso fará com a rotação da nave seja ignorada durante o movimento.

Volte ao editor e clique no botão Enter Play Mode. Mova a espaçonave e agora ela deverá apontar para a direção correta.

Com isso feito precisamos fazer com que nossa nave dispara lasers quando o botão esquerdo do mouse é pressionado. Para isso, vamos adicionar mais uma Action ao nosso Input Action Asset.

Caso você tenha fechado a janela de edição, abra novamente clicando duas vezes sobre o Input Action Asset no projeto. Em seguida, Selecione o Action Map Gameplay e no painel Actions clique no sinal de mais para adicionar mais uma Action. Nomeiei essa Action como Fire. 

No painel Action Properties selecione Button como Action Type. Isso fará com que a ação seja executada quando o botão for pressionado.

Agora selecione o Binding, e clique no dropdown da propriedade Path. Novamente, você será apresentado com uma lista com todos os botão que o Input System suporta. Use o método que preferir e selecione qual botão será usado para fazer a nave disparar o laser. Eu escolhi o botão esquerdo do mouse.

Após criar sua Action não se esqueça de salvar suas mudanças clicando no botão Save asset.

Agora podemos voltar ao script PlayerController. Faremos algumas modificações para fazer com que a nave possa disparar o laser.

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    [SerializeField]
    private float moveSpeed = 8.0f;
    [SerializeField]
    private GameObject laserPrefab;

    private Vector2 moveDirection;

    public void OnMove(InputAction.CallbackContext context)
    {
        moveDirection = context.ReadValue<Vector2>();

        if (moveDirection.magnitude > Mathf.Epsilon)
        {
            Quaternion lookRotation = Quaternion.LookRotation(Vector3.forward, moveDirection);
            transform.rotation = lookRotation;
        }
    }

    public void OnFire(InputAction.CallbackContext context)
    {
        Instantiate(laserPrefab, transform.position, Quaternion.identity);
    }

    private void Update()
    {
        transform.Translate(moveSpeed * Time.deltaTime * moveDirection, Space.World);
    }
}

Primeiramente adicionamos um novo campo chamado laserPrefab e marcamos ele com SerializeField para que possamos editar ele na Unity. Esse campo é responsável por guardar o Prefab que será instanciado quando o jogador pressionar para disparar o laser.

Em seguida declaramos um novo método chamado OnFire. E como o método OnMove, ele tem como parâmetro InputAction.CallbackContext, por enquanto não iremos precisar do valor desse parâmetro, mas como foi dito, todo método que for interagir com o Input System precisa desse parâmetro. Para então, instanciar o laserPrefab na posição atual da nave.

Salve o script e volte para a Unity.

Com a nave selecionada, repare que uma nova propriedade apareceu no componente Player Controller. Na pasta Prefabs você pode encontrar um Prefab chamado Laser, arraste ele até o campo no Player Controller.

Não podemos nos esquecer de adicionar o método OnFire a lista de métodos que serão chamados pelo Input System quando a Action Fire acontecer. Como anteriormente, Selecione o jogador e no componente Player Input e clique em Events e em seguida em Gameplay.

Clique no sinal de mais localizado na parte de baixo da Action Fire. Clique na bolinha e selecione o Game Object Player. Em seguida clique no dropdown e selecione o método que criamos no script Player Controller (Player Controller > OnFire).

Agora entre no Play Mode e aperte o botão esquerdo do mouse. Veremos que o laser irá aparecer na tela. Se movermos a nave podemos perceber que o laser aparece onde a nave estiver. Porém temos alguns problemas.

O primeiro e mais importante porque ocorre por causa de como o Input System funciona. Repare que cada vez que disparamos um laser, três lasers são criados. 

Isso acontece porque o Input System dispara o mesmo evento três vezes dependendo de qual fase a Action está. Uma ação pode estar em um das três fase a seguir:

Started: Aqui a ação começou. 

Performed: Aqui a ação foi concluída.

Canceled: Aqui a ação acabou.

No momento o método OnFire executa todo seu código em todas as fases. Porém queremos que ele seja executado uma vez quando a ação for concluída. Por sorte o Input System passa em qual fase uma ação está através do parâmetro context. Vamos voltar ao script Player Controller para corrigir esse problema.

public void OnFire(InputAction.CallbackContext context)
{
    if (context.phase == InputActionPhase.Performed)
    {
        Instantiate(laserPrefab, transform.position, Quaternion.identity);
    }
}

Podemos usar o campo phase do parâmetro context para sabermos em qual fase a Action está. Usamos isso para envolver a lógica de instanciamento em um bloco if. Dessa forma podemos checar se a fase atual é InputActionPhase.Performed, caso for executamos o código para instanciar o laser.

E o segundo problema é que o laser não se move. No projeto que forneci, verá que o prefab Laser tem um componente chamado Laser. Se você der uma olhada nele, poderá ver que ele é bem parecido com o Player Controller. Usamos basicamente a mesma lógica para fazer o laser se mover e apontar para a direção correta.

Para fazer o laser se mover precisamos fazer mais algumas modificações no script Player Controller.

No método OnFire, quando instanciamos o laserPrefab usamos o método GetComponent para recuperar o componente Laser que está no Game Obejct, e em seguida usamos o método Fire disponível no componente para definirmos a direção em que o laser deve se mover usando o valor do campo moveDirection.

Se testarmos o jogo agora você verá que o laser se move na direção correta, desde que a nave esteja se movendo. Isso acontece pelo mesmo motivo de quando a nave estava disparando três lasers. Porém aqui, queremos que o valor do campo moveDirection seja alterado caso o jogador pare de pressionar os botões de movimento, caso contrário a nave não irá parar de se mover.

Para resolvermos esse problema precisamos adicionar um novo campo chamado firingDirection. Esse campo será iniciado com o valor Vector2.up para que o laser se mova na direção do topo da tela caso o jogador ainda não tenha movido a nave.

Também precisamos modificar o método OnMove. Usaremos novamente o campo phase do parâmetro context para verificar em qual fase a ação está e modificar o valor do campo firingDirection apenas quando a fase for InputActionPhase.Performed, isso irá garantir que o laser seja disparado na direção correta mesmo quando o jogador para de mover a nave. O valor também será atualizado toda vez que o jogador mudar a direção da nave.

Por fim usamos o valor desse campo no método OnFire. A versão final do Player Controller deve ficar assim:

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    [SerializeField]
    private float moveSpeed = 8.0f;
    [SerializeField]
    private GameObject laserPrefab;

    private Vector2 moveDirection;
    private Vector2 firingDirection = Vector2.up;

    public void OnMove(InputAction.CallbackContext context)
    {
        moveDirection = context.ReadValue<Vector2>();

        if (moveDirection.magnitude > Mathf.Epsilon)
        {
            Quaternion lookRotation = Quaternion.LookRotation(Vector3.forward, moveDirection);
            transform.rotation = lookRotation;
        }

        if (context.phase == InputActionPhase.Performed)
        {
            firingDirection = moveDirection;
        }
    }

    public void OnFire(InputAction.CallbackContext context)
    {
        if (context.phase == InputActionPhase.Performed)
        {
            Laser laser = Instantiate(laserPrefab, transform.position, Quaternion.identity).GetComponent<Laser>();
            laser.Fire(firingDirection); 
        }
    }

    private void Update()
    {
        transform.Translate(moveSpeed * Time.deltaTime * moveDirection, Space.World);
    }
}

Volte a Unity e entre no Play Mode. Mova a nave e dispare alguns lasers, repare que agora apenas um laser é criado e que ele se move na direção correta.

Mas você deve estar se perguntando e se eu quiser adicionar suporte a controle para console por exemplo. Isso é muito simples com o novo Input System. Basta abrir o Input Action Asset e adicionar um novo Binding a Action.

Por exemplo, clique no sinal de mais da Action Move. E clique em Add Binding. No painel Binding Properties, clique no dropdown da propriedade Path e selecione a opção que você quiser. Por exemplo, eu selecione Gamepad > Left Stick.

Faça o mesmo para a Action Fire. Eu selecionei o Gamepad > Button South.

Entre no Play Mode e teste os novos controles.

Conclusão

Como podemos ver, o novo Input System é extremamente poderoso e nos permite criar controles simples ou complexos dependendo das necessidades do nosso jogo. Dessa forma, esse tutorial é apenas uma introdução de como começar a usar o sistema.

Com o que você aprendeu aqui você deverá ser capaz de começar a usufruir de um sistema mais moderno e robusto, e que conforme o seu jogo cresça irá permitir que você aprimore e adicione suporte a mais controles.

Como você pode imaginar existem muito mais coisas que gostaria de explorar em tutoriais futuros como interagir com a UI, fazer rebind dos controles, usar com plataformas diferentes ou com múltiplos jogadores.

Até a próxima!

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *