Unity Input Manager

Input Manager Pression des touches dans Unity

Dans cet article, nous allons explorer les différentes méthodes pour détecter la pression des touches dans Unity avec Input Manager.
La détection de touches est essentielle pour interagir avec les éléments du jeu, comme la gestion des mouvements, les sauts, les tirs et bien d’autres actions.

Notre approche s’appuie sur Input Manager le gestionnaire d’entrée par défaut de Unity, affectueusement appelé « l’ancien système Input ».
Bien qu’il manque certaines fonctionnalités innées par rapport au nouveau système (Input System), il reste complet et particulièrement utile pendant le prototypage.

InputManager vs InputSystem

La principale différence entre les deux réside dans leur implémentation. L’ancien système s’appuie sur une approche d’écoute « persistante » (Code dans les boucles Update), tandis que le nouveau système relie directement les pressions sur les touches aux actions à l’aide d’événements.

En tout franchise, si votre jeu implique une personnalisation des touches, je vous recommande vivement d’opter pour Input System. Vous découvrirez à quel point le changement de touches en jeu peut être fastidieux dans la section Modifier les touches en jeu.

Sommaire

Détection des touches

Détecter quand une touche est enfoncée (Input.GetKeyDown)

La fonction Input.GetKeyDown renvoie true dès que l’utilisateur commence à presser une touche. Cependant, elle ne renverra pas true tant que l’utilisateur n’aura pas relâché la touche et appuyé à nouveau.


using UnityEngine;

public class InputMonobehaviour : MonoBehaviour
{
    private void Update()
    {
        // Avec KeyCode
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // Faites un saut, par exemple
        }

        // Ou avec une chaîne
        if (Input.GetKeyDown("space"))
        {
            // Faites un saut, par exemple
        }
    }
}
            

Détecter quand une touche est relâchée (Input.GetKeyUp)

La fonction Input.GetKeyUp renvoie true pendant la frame où l’utilisateur relâche la touche. Elle ne renverra pas true


private void Update()
{
    if (Input.GetKeyUp(KeyCode.Space))
    {
        // La touche est relâchée
    }
}
            

Détecter quand une touche est maintenue (Input.GetKey)

La fonction Input.GetKey renvoie true tant que l’utilisateur maintient enfoncée la touche spécifiée.


private void Update()
{
    if (Input.GetKey(KeyCode.A))
    {
        // La touche A est maintenue, effectuez une action comme tirer
    }
}
            

KeyCode

Comme illustré dans le premier exemple, vous avez la possibilité d’identifier une touche en utilisant soit une chaîne, soit l’énumération KeyCode. Il est fortement recommandé d’utiliser l’énumération pour la réutilisabilité de votre code, ce qui vous permettra également d’éviter les erreurs de frappe. Vous pouvez consulter la liste complète des touches disponibles dans la documentation Unity.

Attention si vous utilisez un clavier AZERTY, à partir de Unity 2022.3, par défaut l’option « Physical keys » est activée.
Elle permet de mapper directement vos touches AZERTY en QWERTY. Par exemple, sur un clavier AZERTY, si vous appuyez sur Z, la valeur true sera renvoyée lorsque vous utilisez « Input.GetKey(KeyCode.W) ».

Vous pouvez désactiver ce paramètre dans « Edit »« Project Settings »« Input Manager ».

Short évoquant ce changement

Axe virtuel

Dans l’Input Manager, nous pouvons créer des axes virtuels. Ils nous permettent de définir des touches pour le clavier/souris ou un contrôleur sur le même axe virtuel. La particularité des axes est leur possibilité de définir un bouton positif et un bouton négatif. Lorsque le bouton positif est pressé, la valeur augmente et tend vers 1.0, tandis que lorsque le bouton négatif est pressé, la valeur tend vers -1.0. Lorsque aucune des touches n’est pressée, la valeur tend vers 0.0.

De plus, nous pouvons utiliser ces axes comme des boutons virtuels, sans attendre qu’ils nous renvoient une valeur.

Vous pouvez retrouver l’ensemble des axes dans « Edit » → « Project Settings » → « Input Manager ».

InputManager Axis Unity Editor

Les propriétés principales de l’axe sont :

  • Negative Button, Positive Button : Ces contrôles changent la valeur de l’axe selon leur direction respective.
  • Gravity : Quand aucune entrée n’est détectée, la valeur tend vers 0 en utilisant cette vitesse (unité par seconde).
  • Sensitivity : Vitesse à laquelle la valeur se déplacera quand une entrée est saisie.
  • Snap : Lors d’un changement de direction de la valeur, la valeur passera instantanément à 0 avant de changer de direction.
  • Axis : L’axe d’un appareil connecté. (L’axe choisi dépendra du contrôleur, par exemple pour des manettes Xbox et PlayStation, les axes sont différents.)

Pour le type, l’évolution de la valeur dépend du type d’entrée utilisé. Par exemple :

  • Pour un joystick horizontal, une valeur de 1 signifie que le manche est poussé à fond vers la droite et une valeur de -1 signifie qu’il est poussé à fond vers la gauche ; une valeur de 0 signifie que le joystick est en position neutre.
  • Pour une souris, la valeur est différente et n’est pas comprise entre -1 et 1. Elle correspond au delta actuel de la souris multiplié par la sensibilité de l’axe.
  • Pour une touche/bouton de souris, la sensibilité sera utilisée pour modifier la valeur.

Axis vertical Joystick Unity

Joystick par OpenClipart-Vectors de Pixabay

Pour mapper un axe virtuel sur plusieurs types de contrôleurs, vous pouvez créer plusieurs axes avec le même nom.

Les axes sont parfaits pour le déplacement de personnages, leur utilité est également recommandée par Unity (par rapport à l’utilisation de touches directement) en raison de leur polyvalence vis-à-vis des différents types de contrôleurs. Voyons maintenant comment les utiliser dans le code.

Récupérer l’axe (Input.GetAxis)

Pour récupérer la valeur de l’axe, il faut appeler Input.GetAxis et passer le nom de l’axe en paramètre. Le float retourné est la valeur de l’axe.


private void Update()
{
    float speed = 3f;
    // Déplacez-vous vers la droite ou la gauche, en fonction de l'axe
    transform.Translate(Input.GetAxis("Horizontal") * Time.deltaTime * speed, 0, 0);
}
        

Remarque : Si vous préférez récupérer une valeur d’axe brute (sans lissage), vous pouvez utiliser Input.GetAxisRaw. La valeur retournée sera soit -1.0, 0.0 ou 1.0, en fonction des pressions.

Détecter quand un bouton virtuel est cliqué (Input.GetButtonDown)

La fonction Input.GetButtonDown renvoie true dès que l’utilisateur commence à presser le bouton virtuel. Cependant, elle ne renverra pas true tant que l’utilisateur n’aura pas relâché le bouton virtuel et appuyé à nouveau.


using UnityEngine;

public class InputMonobehaviour : MonoBehaviour
{
    private void Update()
    {
        if (Input.GetButtonDown("Fire2"))
        {
            // Faites un deuxième tir, par exemple
        }
    }
}
        

Détecter quand un bouton virtuel est relâché (Input.GetButtonUp)

La fonction Input.GetButtonUp renvoie true pendant la frame où l’utilisateur relâche le bouton virtuel. Elle ne renverra pas true tant que l’utilisateur n’aura pas appuyé sur le bouton virtuel et l’aura relâché à nouveau.


using UnityEngine;

public class InputMonobehaviour : MonoBehaviour
{
    private void Update()
    {
        if (Input.GetButtonUp("Fire2"))
        {
            // La touche associée au bouton virtuel Fire2 est relâchée
        }
    }
}
        

Détecter quand un bouton virtuel est maintenu (Input.GetButton)

La fonction Input.GetButton retourne true tant que l’utilisateur maintient enfoncé le bouton virtuel.


private void Update()
{
    if (Input.GetButton("Fire2"))
    {
        // Le bouton virtuel Fire2 est maintenu, effectuez une action comme tirer
    }
}
        

Modifier les touches en jeu

Pour ce dernier point, je ne vous conseille pas d’utiliser ces méthodes, le nouveau Input System répondra plus facilement à vos attentes. Malheureusement, l’écran de modification des touches utilisable sur Unity a été déprécié et supprimé, on doit donc créer nos propres méthodes.

Touche dynamique avec Input.GetKey()

Pour utiliser des touches définies par l’utilisateur dans les Input.GetKey, nous devons créer un système de stockage des touches de l’utilisateur. Voici comment on peut récupérer une pression de touche sur Unity :


using System;
using System.Collections;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;

public class GetKeySample : MonoBehaviour
{
    public void GetKeyPress(UnityAction keyCodeAction)
    {
        // Vous pouvez utiliser async si vous préférez
        StartCoroutine(CheckKeyPress(keyCodeAction));
    }

    private IEnumerator CheckKeyPress(UnityAction keyCodeAction)
    {
        // Obtenez toutes les valeurs de KeyCode
        var values = Enum.GetValues(typeof(KeyCode)).Cast().ToList();

        while (true)
        {
            foreach (var keyCode in values)
            {
                // Vérifiez une par une si une touche est pressée
                if (Input.GetKeyDown(keyCode))
                {
                    keyCodeAction.Invoke(keyCode);
                    yield break;
                }
            }

            yield return null;
        }
    }

    private void Start()
    {
        // Comment utiliser
        GetKeyPress((KeyCode keyCode) =>
        {
            // Nous pouvons lier keyCode à une action
        });
    }
}
            

Ceci n’est qu’un exemple, je pense que cette classe serait plus utilisable en tant que classe statique sans étendre de MonoBehaviour.

2. Stocker et récupérer les touches par actions

Ici, nous utilisons PlayerPrefs pour stocker les préférences des touches. Elles seront donc stockées localement par poste. Rien ne vous empêche de faire évoluer cette classe pour les stocker sur un serveur web :


using System.Collections.Generic;
using UnityEngine;

public class KeyBinder
{
    // Définissez toutes vos actions
    public enum Control
    {
        ACTION,
        PARAMETERS,
        MAP
    }

    // Les valeurs par défaut peuvent être créées de différentes manières...
    private static Dictionary<Control, Keycode> _defaultValues = new Dictionary<Control, Keycode>()
    {
        { Control.ACTION, KeyCode.A },
        { Control.PARAMETERS, KeyCode.Escape },
        { Control.MAP, KeyCode.M }
    };

    // Cache pour les performances
    private static Dictionary<Control, Keycode> _keysStored = new Dictionary<Control, Keycode>();

    public static KeyCode GetKeyCode(Control control)
    {
        // Valeur mise en cache
        if (_keysStored.ContainsKey(control))
        {
            return _keysStored[control];
        }

        if (PlayerPrefs.HasKey(control.ToString()))
        {
            var value = (KeyCode)PlayerPrefs.GetInt(control.ToString());
            _keysStored.Add(control, value);
            return _keysStored[control];
        }

        // Pas encore configuré, retourne la valeur par défaut
        return _defaultValues[control];
    }

    public static void UpdateKeyControl(Control control, KeyCode keyCode)
    {
        // Supprimer le cache
        if (_keysStored.ContainsKey(control))
        {
            _keysStored.Remove(control);
        }

        // Met à jour la valeur
        PlayerPrefs.SetInt(control.ToString(), (int)keyCode);
        PlayerPrefs.Save();
    }
}

3. Utilisez ces touches dans le jeu

Enfin, utilisez ces touches dans le jeu :


using UnityEngine;

public class UseKeyBinder : MonoBehaviour
{
    private void Update()
    {
        // Utilisez les touches configurées
        if (Input.GetKeyDown(KeyBinder.GetKeyCode(KeyBinder.Control.ACTION)))
        {
            // Faites quelque chose quand la touche ACTION est enfoncée
        }
    }
}
            

Ce système est très souple et vous permet de gérer de manière dynamique les touches du jeu.

Touche dynamique avec Axis

Concernant les axes dynamiques, c’est une autre histoire, nous n’avons pas accès aux axes virtuels stockées dans l’éditeur, nous devons alors créer nos propres axes ainsi que répliquer leur fonctionnement dans une nouvelle classe.

Mais malheureusement il n’est pas possible de détecter directement la pression appliqué sur un joystick.
De ce fait toutes les valeurs retournés par notre fonction customisée reprendront le fonctionnement d’une touche classique et utiliseront « sensitivity » pour faire varier la valeur.

Encore une fois je vous conseille de switcher sur Input System au lieu d’utiliser ce code.


using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;


public class InputMonobehaviour : MonoBehaviour
{
    // enum, for detect wich button are pressed
    // Bit field but we dont need FlagsAttribute, because we only use POSITIVE (10) AND NEGATIVE (01)
    public enum PressButton
    {
        NONE,
        POSITIVE,
        NEGATIVE,
        BOTH
    }

    // Recreate a class similar to Virtual Axis Configuration (It will be better as ScriptableObject) 
    public class InputAxis
    {
        public string name;

        public float sensitivy;

        public float gravity;

        public KeyCode negativeKeyCode;

        public KeyCode positiveKeyCode;
    }

    // let's singleton
    public static InputMonobehaviour Instance { get; private set; }

    private void Awake()
    {
        Instance = this;
    }

    private void Start()
    {
        // Init for sample, ScriptableObject will be better
		// I use static keys for sample but all keys can be change at runtime.
        _inputAxes = new List()
        {
             {
                new InputAxis()
                {
                    name = "Vertical",
                    gravity = 3f,
                    sensitivy = 3f,
                    negativeKeyCode = KeyCode.DownArrow,
                    positiveKeyCode = KeyCode.UpArrow
                }
            }
        };
    }

    // store current axis Values
    private static Dictionary<string, float> _axisValues = new Dictionary<string, float>();

    // store InputAxis in List
    private List _inputAxes = new List();

    // This function will replace Input.GetAxis
    public static float GetAxis(string name)
    {
        var axisInput = LoadInputAxis(name);

        // add key
        if (!_axisValues.ContainsKey(name))
        {
            _axisValues.Add(name, 0f);
        }

        var value = _axisValues[name];

        // check wich button are pressed
        var pressValue = IsPressPositive(axisInput, value);

        // Postive or negative value ?
        var sign = Mathf.Sign(value);

        // nothing is pressed gravity is applied
        if (pressValue == PressButton.NONE)
        {
            _axisValues[name] = Mathf.Clamp01(Mathf.Abs(value) - Time.deltaTime * axisInput.gravity) * sign;
            return _axisValues[name];
        }

        // same value are applied in each direction nothing move
        if (pressValue == PressButton.BOTH)
        {
            return value;
        }

        var signToFocus = pressValue == PressButton.POSITIVE ? 1f : -1f;

        // it always snap values, miss a configuration (condition check if direction has changed)
        if (value != 0 && sign != signToFocus)
        {
            _axisValues[name] = 0f;
            return _axisValues[name];
        }

        _axisValues[name] = Mathf.Clamp01(Mathf.Abs(value) + Time.deltaTime * axisInput.sensitivy) * signToFocus;     
        return _axisValues[name];
    }

    private static PressButton IsPressPositive(InputAxis inputAxis, float value)
    {
        // We use PressButon as a field Bit, so we can use bit operations
        PressButton pressPositiveValue = Input.GetKey(inputAxis.positiveKeyCode) ? PressButton.POSITIVE : PressButton.NONE;
        PressButton pressNegativeValue = Input.GetKey(inputAxis.negativeKeyCode) ? PressButton.NEGATIVE : PressButton.NONE;

        return pressPositiveValue | pressNegativeValue;
    }

    private static InputAxis LoadInputAxis(string name)
    {
        return Instance._inputAxes.Single(x => x.name == name);
    }
}

3. Utilisez ces axes dans le jeu


private void Update()
{
    float speed = 3f;
    // use it like that
    transform.Translate(GetAxis("Vertical") * Time.deltaTime * speed, 0, 0);
}

Cette classe est incomplète et présente quelques lacunes au niveau des fonctionnalités et des performances.

Pour expliquer brièvement le code, qui peut sembler complexe au premier abord : Mathf.Clamp01(Mathf.Abs(value) + Time.deltaTime * axisInput.sensitivity) * signToFocus;

L’objectif est de travailler avec des valeurs comprises entre 0 et 1, indépendamment de la positivité ou de la négativité de notre valeur. Cela nous permet d’effectuer d’abord des calculs, pour ensuite effectuer une multiplication qui déterminera si la valeur est positive ou négative.

J’espère que cet article aura pu vous faire découvrir de nouvelles choses.

Illustration principale par sergeitokmakov

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *