TDM n°4 – Gérer les entrées
Posté le 01/08/2018 18:00
Le Tuto Du Mercredi [TDM] est une idée qui fut proposée par Ne0tux. Un mercredi sur deux, nous postons un petit tutoriel sur l'Utilisation de la calculatrice, le Transfert, la Programmation, ou encore la Conception de jeu. En voici la quatrième édition !
Gérer les données entrées par l'utilisateur
Niveau : ★ ☆ ☆ ☆ ☆
Dans ce TDM, je vous expliquerai les techniques utilisables en Basic Casio pour que le joueur interagisse avec votre jeu via des commandes : c'est ce qu'on appelle les entrées. En fonction des besoins, il vous faudra adapter les méthodes que vous allez utiliser. Je vous expliquerai aussi les erreurs et les fautes que l'on commet souvent avec les entrées, et comment faire pour les éviter.
Partie I – La commande «?»
La manière la plus simple de demander quelque chose au joueur est d'utiliser la commande
?. Cette commande met le programme en "pause" jusqu'à ce que l'utilisateur ait fini d'entrer quelque chose et qu'il ait appuyé sur [EXE]. Le code suivant demande au joueur d'entrer un nombre.
"Entrez un nombre" ? → A
Jusque là, rien de bien dur. L'exemple suivant montre exactement la même chose, mais le résultat est cette fois-ci stocké dans une chaîne de caractère.
"Quel est votre nom" ? → Str 1
Attention toutefois, les erreurs arrivent vite :
"Entrez un nombre" ? → A //Cas n°1
"Quel est votre nom" ? → Str 1 //Cas n°2
Voici ce que j'obtiens dans chaque cas quand j'entre...
–> 5
A = 5 //Cas n°1
Str 1 = "5" //Cas n°2
–> BONJOUR
A = ??? //Généralement, A égale 0 car il fait le produit des variables B, O, N, J, U, et R. Toutefois, vous pouvez avoir à peu près tout et n'importe quoi.
Str 1 = "BONJOUR" //Cas 2, tout va bien
–> 2X² + log (3)
A = //Le résultat numérique de 2X² + log 3
Str 1 = "2X² + log (3)"
Voici un autre cas typique d'erreur :
"Entrez un nombre entre 1 et 128"? → A
//Le joueur, taquin, s'amuse à rentrer la valeur -30
Text 40,A, "Hello World! Whheeeeee!
Dans ce cas c'est l'erreur d'argument. La fonction
Text renvoie une erreur d'argument lorsqu'on rentre une valeur qui n'est pas comprise dans les intervalles [1,64] et [1, 128]. Cette valeur doit aussi être un entier ! Ainsi, il faudra s'assurer que la valeur soit correcte.
Do
"Entrez un nombre entre 1 et 128"? → A
LpWhile A<1 Or A>128 //disque rayé !
Text 40,Int A, "Hello World!"
Vous commencerez peut-être à comprendre qu'on ne peut JAMAIS être sûr de ce que le joueur entre. Ne faites pas confiance au joueur. Il peut entrer une valeur fausse par étourderie, ou même sournoisement pour voir comment le programme réagit. Soyez malins et anticipez les erreurs possibles !
Dans le cas des chaînes de caractère, Rien n'empêche le joueur d'entrer un nom à rallonge qui fait plus de 100 caractères. Il faut prévoir cette éventualité. Sans doute n'aurez-vous jamais assez de place pour afficher son nom s'il est trop large. Voici comment faire.
"Votre nom" ? → Str 1
If StrLen(Str 1) > 8
Then StrLeft(Str 1, 8) → Str 1
"Votre nom a été réduit à 8 caractères."◢
IfEnd
Maintenant, dans des cas de figures un peu plus particuliers, un joueur peut s'amuser à faire de la
merde et à taper
VRAIMENT n'importe quoi :
"Votre nom" ? → Str 1
//Je rentre "sin log ln cos tan "
If StrLen(Str 1) > 8 //... et ça passe.
Then StrLeft(Str 1, 8) → Str 1
"Votre nom a été réduit à 8 caractères."◢
IfEnd
Les fonctions sin , cos, etc. (écrites directement sur les boutons correspondants du clavier de la calto) sont traitées comme un caractère chacun dans la chaîne. On peut contrer cela sans en écrire beaucoup :
Do
"Votre nom" ? → Str 1
If StrLen(Str 1) > 8
Then StrLeft(Str 1, 8) → Str 1
"Votre nom a été réduit à 8 caractères."◢
IfEnd
For 1→A To 6
StrSrc(Str 1, StrMid("log ln sin cos tan ", A, 1)) ⇒ Break //Si StrSrc renvoie une valeur non nulle, alors on sort prématurément de la boucle For pour recommencer au début
Next
LpWhile A < 6
Partie II – Les GetKeys
Pour commencer, ayez toujours bien en tête cette image :Merci Zezombye pour cette image.
Cette image décrit le code numérique correspondant à chacune des touches du clavier de la calculatrice. Ce code numérique, vous pouvez le récupérer via la fonction GetKey. Dans la plupart (la totalité ?) des cas, on utilise cette fonction dans une boucle :
Do
GetKey → G
LpWhile Not G //Tant que la valeur de G est nulle, on tourne en boucle.
Retenez bien que, par défaut, le GetKey renvoie la valeur 0 : Autrement dit, si aucune touche n'est appuyée, il égale toujours 0. Quand une touche est appuyée et tant que celle-ci reste appuyée, GetKey renvoie la valeur correspondante de la touche, qui est indiquée sur l'image ci-dessus. Le code suivant continue de tourner tant que le joueur n'appuie pas sur la touche "0" :
Do
GetKey → G
"Appuie sur 0, bougre d'idiot !"
LpWhile G≠71
Voici alors comment on peut fabriquer un petit moteur de jeu qui fait avancer votre personnage. On prend en compte que ce dernier a I et J comme coordonnées.
While 1
Do
//On redessine le joueur si besoin
Do
Getkey → W
LpWhile Not W
W = 28 ⇒ Dsz J //Touche du haut, on monte
W = 27 ⇒ Isz I //Touche de droite, on va à droite
W = 38 ⇒ Dsz I //Touche de gauche...
W = 37 ⇒ Isz J //Bas
W = 48 ⇒ Break // Menu, on fait autre chose
LpWhile1
// Le menu, autres truc, etc.
[...]
WhileEnd // On revient au début.
Un problème qui arrive souvent est celui-ci :
//Début du programme
"MEGA-JEU"
"Appuie sur [EXE] pour commencer !"
While GetKey ≠ 31
WhileEnd
Cls
Stop
Lorsque le joueur lancera le programme, il n'en verra même pas la couleur : un «
done» s'affichera, indiquant que le programme a fini de tourner. Pourquoi ? Tout simplement parce que le joueur n'aura pas eu le temps de relever le doigt de la touche [EXE] (Sauf s'il est trèèès rapide) et que donc, le GetKey restera à la valeur 31. C'est comme si la boucle n'existait pas. Pour éviter cet inconvénient, voici la parade :
//Début du programme
"MEGA-JEU"
"Appuie sur [EXE] pour commencer !"
While GetKey
WhileEnd //Tant que le joueur a un doigt sur une touche, il ne sort pas de cette boucle. Donc, il n'arrivera à la suite qu'au moment où il aura relevé le doigt de la touche EXE.
While GetKey ≠ 31
WhileEnd
Cls
Stop
Cette solution est à placer devant chacun de vos GetKey qui sont susceptibles de rencontrer ce problème. Elle ne prend que 3 octets et permet d'éviter beaucoup de désagréments.
Partie III – Choses à ne PAS FAIRE !
Il y a un certain nombre d'erreurs de débutant que l'on commet facilement quand on découvre tout cela. Ces choses sont à ne pas faire sous peine de se faire foudroyer pas la colère des Dieux.
Lbl 1
GetKey → G
G = 31 ⇒ Goto A
Goto 1
Les raisons pour lesquelles il est déconseillé (voire implicitement interdit) de faire ceci sont nombreuses. En voici une liste, sans doute non exhaustive :
– Les Labels ne sont pas conçus pour faire des boucles. Ils ne sont pas adaptés pour cela.
– Une boucle avec un Label est plus lente à l'exécution qu'une boucle While ou Do LpWhile.
– Les Labels sont souvent sources d'erreur, parfois très difficile à comprendre pour quelqu'un qui découvre le Basic Casio
– L'utilisation des Labels de manière générale n'est pas recommandée. Il a été prouvé qu'un code avec Label peut toujours avoir son équivalent à l'exécution sans Label.
– L'utilisation des Labels désorganise facilement la structure de votre code et le rend moche. Affreux. Désastreux.
Alors, retenez bien :
On ne fait pas de boucles avec les Labels.
Faites plutôt ceci. C'est plus simple, plus sain, plus propre :
Do
GetKey → G
LpWhile G≠31
De même, ne faites plus ceci :
Do
Getkey → W
W = 28 ⇒ Dsz J
W = 27 ⇒ Isz I
W = 38 ⇒ Dsz I
W = 37 ⇒ Isz J
W = 48 ⇒ Break
Text 1, 1, "Joueur : " + Str 1
Prog"INFO"
//Etc.
LpWhile Not W
Ce code a un énorme défaut : la boucle du GetKey cohabite avec plein d'autres commandes : des conditions, des fonctions graphiques, un appelle d'un sous-programme... Tout ce que vous demandez à la calculatrice demande un certain temps à être effectué. Donc il se peut que le joueur appuie sur la touche quand la calculatrice est en train d'afficher Text ou encore quand elle est dans le sous-programme INFO. Autrement dit, au moment où la boucle en revient à la ligne avec le GetKey, le joueur aura déjà relevé le doigt de sa touche. Et c'est comme si le joueur n'avait jamais appuyé sur un bouton. C'est ce qu'on appelle la réactivité : pour que votre GetKey soit réactif et réponde le plus efficacement possible, il faut l
'ISOLER LE PLUS POSSIBLE du reste. Dans notre cas, faites en sorte d'avoir plutôt quelque chose comme ceci :
While 1
Do
Getkey → W //Le GetKey est seul dans sa boucle, et c'est très bien.
LpWhile Not W
W = 28 ⇒ Dsz J
W = 27 ⇒ Isz I
W = 38 ⇒ Dsz I
W = 37 ⇒ Isz J
W = 48 ⇒ Break
Text 1, 1, "Joueur : " + Str 1
Prog"INFO"
//Etc.
WhileEnd
De même, je déconseille fortement de faire ceci :
//Du code...
Prog"GETKEY"
If G = 71
Then //[...]
IfEnd
//Et encore du code, etc.
//___________________
PROGRAMME : "GETKEY"
Do
GetKey → G
LpWhile Not G
//Fin du sous-programme
Dans ce code, le programmeur a décidé d'éditer un sous-programme qui fait le GetKey, pour l'appeler plus facilement avec
Prog"GETKEY" dès qu'il en a besoin. Cela pourrait être intelligent... Mais en fait ça ne l'est pas. Pourquoi ? En voici les raisons :
– L'appel d'un sous-programme est un poil plus lent que si on écrivait directement la boucle GetKey à la place du
Prog"GETKEY".
– Un sous-programme vide prend 32 octets. L'appel du sous-programme prend toujours 3 octets (
Prog " ") PLUS un octet pour chaque lettre du nom. Autrement dit, dans notre cas, on gaspille 9 octets à chaque fois PLUS 39 octets avec le sous-programme GETKEY, alors que la boucle GetKey toute seule n'en prend que 7.
– Si vous avez déjà 10 sous-programmes d'ouverts, ce sera l'erreur lorsque vous appellerez votre sous-programme GETKEY. Donc, potentiellement, un souci supplémentaire.
Bref, maintenant, je vous recommande d'arrêter de mettre un simple GetKey dans un sous-programme. Le code à effectuer n'est pas assez long pour que ça devienne rentable de faire cela.
Et ce sera tout pour cette fois ! Si vous avez des
petites suggestions ou remarques pour compléter ce tutoriel, vous êtes la bienvenue. Si vous avez des questions, n'hésitez pas non plus. Si vous avez des envies particulières pour le TDM suivant, là encore, je suis ouvert à vos propositions. Bien sûr, n'hésitez pas à visiter les liens utiles que je vous donne ci-dessous !
Liens utiles :
Citer : Posté le 01/08/2018 18:50 | #
Pas mal ! Le style est un peu moins explicatif/pédagogique que les précédents je dirais, mais blâmons le peu de temps que tu as eu pour le rédiger.
Une autre tutoriel à ajouter à la liste quand j'aurais réussi à créer la nouvelle catégorie que je t'ai promise !
Citer : Posté le 01/08/2018 19:16 | #
Je me pose une question : peut-on résoudre le ghosting du getkey en Basic ?
Citer : Posté le 01/08/2018 19:46 | #
malheureusement non... le gosthing est une erreur de gestion des touches dues à l'algorithme basé sur une matrice...
@Drak : Ce TDM est fondamental
Citer : Posté le 01/08/2018 20:23 | #
Je dirai même plus : le ghosting est un problème matériel. Malgré tous mes efforts niveau gint je n'ai jamais réussi à m'en défaire.
Citer : Posté le 01/08/2018 20:53 | #
Fuuuuuuuck !
Adieu les jeux multijoueurs en local !
Citer : Posté le 01/08/2018 21:00 | #
Mais heu,perso je n'ai jamais eu de problème de ghosting dans un jeu (surtout dans multitask), c'est si courant que ça ?
Ajouté le 01/08/2018 à 21:00 :
Mais heu,perso je n'ai jamais eu de problème de ghosting dans un jeu (surtout dans multitask), c'est si courant que ça ?
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 01/08/2018 21:05 | #
Très courant mais rarement un problème. Tu as déja entendu parler de SHIFT + Down + Left qui déclenche ALPHA ? C'est ça.
Évidemment dans les jeux à beaucoup de boutons ça dégénère vite. Tous ceux qui utilisent les flèches pour diriger doivent choisir entre SHIFT et ALPHA pour les actions ; pas les deux à la fois.
Citer : Posté le 01/08/2018 21:19 | #
Ben justement : j'en ai jamais entendu parler et avec la plupart des jeux qui utilisent flèches, shift et alpha je m'étais dit qu'on en entendrait parler plus souvent.
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 01/08/2018 23:25 | #
Pas mal ! Le style est un peu moins explicatif/pédagogique que les précédents je dirais, mais blâmons le peu de temps que tu as eu pour le rédiger.
Une autre tutoriel à ajouter à la liste quand j'aurais réussi à créer la nouvelle catégorie que je t'ai promise !
Merci ! Il est vrai que je me suis moins investi dans ce tuto que dans les précédents, et que donc le ton pédagogique est moins présent. Mais, à mes yeux, ce tuto permet d'asseoir les fondamentaux, et est une occasion d'éviter des erreurs très courantes avec les entrées.
Citer : Posté le 02/08/2018 20:25 | #
Pour le coup de la vérification des inputs de strings, il est sûrement préférable de fonctionner sur le principe d'une whilelist plutôt que d'une blacklist
En gros tu autorises certains caractères, et tous les autres tu les refuse. Dans ton cas par exemple, je peux toujours ajouter tan⁻¹ à ton nom, ça passera
Citer : Posté le 02/08/2018 21:37 | #
Tout dépend de ton niveau d'exigence. Si tu veux juste empêcher les Cos et autres, une black list est mieux car elle prend moins de place que toutes les lettres de l'alphabet et les chiffres. Dans mon cas, effectivement, j'ai oublié les Atan, Acos, Asin. Mais il n'empêche que je préfèrerai toujours écrire une chaîne de 9 caractères plutôt que de 36 caractères. Après, effectivement, si tu veux faire preuve de beaucoup de rigueur et couvrir tous les cas possibles, c'est peut-être mieux. Il n'empêche qu'il y aura toujours des cas qu'on ne pourra pas couvrir : On fait quoi si le joueur est si malin qu'il tape une phrase de plus de 255 caractères ?
Citer : Posté le 02/08/2018 22:06 | #
Rien, mais pour le coup ce ne sera pas de ta faute. Ta solution fonctionnera, mais d'un point de vue sécurité ça reste beaucoup moins safe que la whitelist. Ça va qu'en Basic osef un peu
Ajouté le 02/08/2018 à 22:08 :
Sachant qu'en plus ta blacklist doit contenir tous les opcodes du catalogue (puisqu'on peut y accéder avec le prompt ?)