Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
We use cookies to ensure that we provide you with the best possible experience on our site.
Votre source incontournable sur Unity et le développement web
Votre source incontournable sur Unity et le développement web
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.
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.
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
}
}
}
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
}
}
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
}
}
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
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 ».
Les propriétés principales de l’axe sont :
Pour le type, l’évolution de la valeur dépend du type d’entrée utilisé. Par exemple :
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.
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.
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
}
}
}
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
}
}
}
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
}
}
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.
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.
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