Project Anarchy : Entrées utilisateur

Partie 1- souris, clavier et abstraction des contrôles

Mike Fleischauer

Traducteur

Alexandre Laurent

Ce tutoriel présente la gestion des entrées utilisateur.

Commentez

- Titre : Project Anarchy : Entrées utilisateur

- Auteur : Mike Fleischauer

- Traducteur : Alexandre Laurent

- Parution : 1 octobre 2013

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2013 Mike Fleischauer. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- Navigation

- II. Introduction

- III. Gérer les entrées clavier

- IV. Un point très important !

- V. Gérer les entrées souris

- VI. Créer une table de correspondance des entrées

- VII. Remerciements

- Navigation

Navigation

Tutoriel précédent : entités, mesh statiques et composants

Sommaire

Tutoriel suivant : Entrées utilisateur - deuxième partie

II - Introduction

Dans ce tutoriel, nous allons voir la gestion des entrées utilisateur avec des scripts Lua (nous verrons le C++ plus tard). Je suppose que vous avez lu les tutoriels précédents et donc, que vous savez créer un nouveau projet et le peupler d'une nouvelle entité en utilisant le modèle de caisse que nous avons réalisé précédemment. Vous n'avez pas besoin de l'exporter une nouvelle fois, vous pouvez simplement copier les fichiers du modèle et de la texture dans la racine de votre nouveau projet. Faites-le donc tout de suite : créez un nouveau projet, créez une scène par défaut et ajoutez le modèle de caisse dans votre scène. Ensuite, ajoutez un composant script à votre nouvelle entité de caisse. Si vous avez un quelconque problème avec ces instructions, veuillez vous référer aux tutoriels précédents.

Dans ce tutoriel, nous explorons :

- la récupération des entrées clavier ;

- la récupération des entrées souris ;

- la création d'une table de correspondance pour gérer les entrées de façon abstraite.

Ce tutoriel commence en présentant une méthode que vous ne devriez pas utiliser ! Assurez-vous de lire l'intégralité du tutoriel avant de commencer quoi que ce soit.

III - Gérer les entrées clavier

Le script suivant montre comment gérer les entrées basiques du clavier avec Lua. J'ai joint le script à notre entité et implémenté son callback OnThink.

function OnThink(self)
if Input:IsKeyPressed(Vision.KEY_UP) then
self:IncPosition(0,1,0)
elseif Input:IsKeyPressed(Vision.KEY_DOWN) then
self:IncPosition(0,-1,0)
elseif Input:IsKeyPressed(Vision.KEY_LEFT) then
self:IncPosition(-1,0,0)
elseif Input:IsKeyPressed(Vision.KEY_RIGHT) then
self:IncPosition(1,0,0)
end
end

La clé est l'objet source « Input », qui est une instance de VScriptInput_wrapper, qui, comme son nom l'indique, recouvre les fonctionnalités des entrées et les rend disponibles dans Lua. Une autre chose à remarquer est l'objet Vision (ou… table, si j'utilise la bonne terminologie) qui contient un certain nombre de constantes. Dans notre cas, nous accédons aux valeurs des codes de plusieurs touches, telles que Vision.KEY_LEFT. Lors de l'appui d'une touche fléchée, nous déplaçons notre entité en appelant la fonction IncPosition(), nous déplaçant suivant les axes X et Y selon la touche appuyée.

Le point principal à comprendre sur la fonction IsKeyPressed est qu'elle retournera true à chaque fois qu'une touche sera pressée. Donc, avec le code ci-dessus, si vous maintenez la touche enfoncée, la fonction sera déclenchée à chaque fois que OnThink() sera appelée. Que faites-vous si vous souhaitez ne réagir que lorsque la touche est appuyée la première fois ? Dans ce cas, vous utilisez SetKeyOnHit comme ceci :

function OnCreate(self)
Input:SetKeyAsSingleHit(Vision.KEY_UP,true)
end

function OnThink(self)
if Input:IsKeyPressed(Vision.KEY_UP) then
self:IncPosition(0,100,0)
end
end

Avec le code ci-dessus, grâce à l'appel supplémentaire à SetKeyAsSignleHit, l'appel à IsKeyPressed pour KEY_UP ne retournera true que lorsque la flèche HAUT sera initialement appuyée et ne retournera plus true tant que la touche ne sera pas relâchée puis réappuyée. Vous pouvez annuler ce comportement en appelant une nouvelle fois SetKeyAsSingleHit mais en lui passant false.

IV - Un point très important !

Lors du test d'un code d'entrée, il est très important que vous exécutiez votre jeu dans le mode « Jouer le jeu » (« Play the Game »), sinon vForge continuera de capturer et de gérer les entrées utilisateur. Vous pouvez activer ce mode en cliquant sur la flèche vers le bas à côté de l'icône de lecture et en sélectionnant « Jouer le jeu » :

Lorsque vous avez fini votre test, appuyez sur la touche Échap.

V - Gérer les entrées souris

Maintenant, voyons comment gérer les événements de la souris. J'ai créé une nouvelle scène dans le même projet, ajouté le modèle de caisse et encore une fois, j'ai attaché un script Lua à la caisse. Le moteur Vision possède la faculté de sonder les activités de la souris, ce qui est exactement ce que nous allons montrer.

-- nouveau fichier de script

function OnThink(self)
local mouseX,mouseY = Input:GetMousePosition()
local mouseDeltaX,mouseDeltaY = Input:GetMouseDelta()
local mouseWheelDelta = Input:GetMouseWheelDelta()
local isLeftButtonDown = Input:IsMouseButtonPressed(Vision.BUTTON_LEFT)
local isRightButtonDown = Input:IsMouseButtonPressed(Vision.BUTTON_RIGHT)

local outString = "Mouse Position:" .. mouseX .. "," .. mouseY ..
" Mouse Delta:" .. mouseDeltaX .. "," .. mouseDeltaY .. " Mouse Wheel Delta:" .. mouseWheelDelta

if isLeftButtonDown then
outString = outString .. " Left Button Down"
end

if isRightButtonDown then
outString = outString .. " Right Button Down"
end

Debug:PrintLine(outString)
end

Le code est plutôt simple. Pour chaque appel à OnThink(), nous sondons la position de la souris avec la fonction GetMousePosition(), le mouvement de la souris depuis la dernière image avec la fonction GetMouseDelta(), le mouvement de la molette de la souris avec la fonction GetMouseWheelDelta() et finalement, nous vérifions si l'un des boutons de la souris est appuyé avec la fonction IsMouseButtonPressed(). Une fois encore, les boutons de la souris sont des constantes définies dans Vision.

Si vous exécutez ce code, vous allez voir :

Une fois encore, tout comme pour la démonstration du clavier, vous devez exécuter le projet dans le mode « Jouer le jeu » afin que la vue Moteur reçoive les entrées.

VI - Créer une table de correspondance des entrées

Maintenant que nous savons sonder l'activité du clavier et de la souris, nous allons voir la gestion des entrées avec une InputMap. L'utilisation d'une table de correspondance des entrées vous permet de rajouter un niveau d'abstraction entre vous et les périphériques d'entrée, vous permettant de faire correspondre plusieurs schémas de contrôle à une simple action. Voyons comment cela fonctionne. Encore une fois, j'ai créé une nouvelle scène, ajouté une caisse et appliqué un script sur celle-ci.

-- Démonstration table de correspondance d'entrées

function OnAfterSceneLoaded(self)
self.map = Input:CreateMap("crateInputMap")
self.map:MapTrigger("Up", "KEYBOARD", "CT_KB_SPACE")
self.map:MapTrigger("Up", "MOUSE", "CT_MOUSE_RIGHT_BUTTON")
self.map:MapTrigger("Up", {0,0,100,100}, "CT_TOUCH_ANY", {once=true})

-- Correspondance des touches fléchées pour gauche/droite
self.map:MapTriggerAxis("MoveX", "KEYBOARD", "CT_KB_LEFT", "CT_KB_RIGHT")
-- Correspondance WASD pour gauche/droite
self.map:MapTriggerAxis("MoveX", "KEYBOARD", "CT_KB_A", "CT_KB_D")
-- Correspondance de la manette pour gauche/droite
self.map:MapTriggerAxis("MoveX", "PAD1", "CT_PAD_RIGHT_THUMB_STICK_LEFT", "CT_PAD_RIGHT_THUMB_STICK_RIGHT")

end

function OnThink(self)
if self.map:GetTrigger("Up") == 1 then
self:IncPosition(0,0,10)
end

if self.map:GetTrigger("MoveX") < 0 then
self:IncPosition(10,0,0)
elseif self.map:GetTrigger("MoveX") > 0 then
self:IncPosition(-10,0,0)
end
end

Cette fois, nous créons la table pour gérer les entrées avec la fonction Input:CreateMap(). Si vous créez une autre table avec le même nom, la table précédente sera détruite. Une fois que vous avez la table, il ne reste plus qu'à définir des interrupteurs. Fondamentalement, cela signifie que nous faisons correspondre une chaîne de caractères définissant une touche à une entrée. Par exemple MapTrigger("Up", "KEYBOARD", "CT_KB_SPACE") indique que lorsque la barre d'espacement est appuyée, un interrupteur appelé « Up » est déclenché. Comme vous pouvez le voir, il est possible de faire correspondre plusieurs périphériques/touches au même interrupteur. Dans cet exemple, nous faisons correspondre la barre d'espacement, le clic droit de la souris et un appui sur l'écran entre les coordonnées (0,0) et (100,100) à l'interrupteur « Up ». Donc, maintenant, au lieu de gérer tous les schémas de contrôle par le code, nous avons simplement à gérer l'interrupteur « Up ». Si vous regardez à l'interrupteur CT_TOUCH_ANY (que nous détaillerons dans un prochain tutoriel), vous pouvez aussi bien passer un certain nombre de paramètres optionnels, dans le cas où nous voulons indiquer que l'événement ne doit être déclenché qu'une fois par contact.

Quelquefois, vous souhaitez lier un axe à un événement, comme lors de l'appui sur les flèches gauche ou droite, ou l'utilisation d'un joystick sur une manette. Dans ce cas, vous pouvez utiliser MapTriggerAxis(). Vous passez alors le nom de l'interrupteur, le périphérique à utiliser et les valeurs représentant les échelles négatives et positives de l'axe. Dans le premier exemple, nous faisions correspondre les touches fléchées GAUCHE et DROITE à l'axe des X, avec CT_KB_LEFT pour les valeurs négatives et CT_KB_RIGHT pour les valeurs positives. Nous avons aussi lié les touches A et D de la classique disposition WASD à l'axe des X et aussi le joystick droit de la manette, si elle est disponible. Le code vous avertira qu'un tel périphérique n'est pas présent, mais s'exécutera.

Maintenant, vous pouvez voir la facilité de la gestion des contrôles. Vous n'avez qu'à appeler la méthode GetTrigger en passant le nom de l'interrupteur que vous avez créé. Donc, si n'importe quel interrupteur défini est vrai (le bouton droit de la souris, la barre d'espacement ou une certaine partie de l'écran touchée), elle retournera 1 lors de la vérification de l'interrupteur « Up ». La gestion d'un axe est virtuellement identique, sauf que la valeur de retour varie entre -1 et 1, selon la direction appuyée. L'utilisation d'une table de correspondance vous permet de gérer facilement plusieurs schémas de contrôle en utilisant un simple code de base.

Dans ce tutoriel, nous avons principalement vu les contrôles traditionnels aux PC… pas très utilisés dans le développement mobile. Dans le prochain tutoriel, nous allons voir les entrées des mobiles.

VII - Remerciements

Cet article est une traduction autorisée de l'article paru sur Game From Scratch. Je remercie chaleureusement Mike de nous avoir permis de le traduire.

Merci à Winjerome pour sa relecture lors de la traduction de cet article, ainsi qu'à ClaudeLELOUP pour sa relecture orthographique.

Navigation

Tutoriel précédent : entités, mesh statiques et composants

Sommaire

Tutoriel suivant : Entrées utilisateur - deuxième partie