Nous continuons notre série de tutoriels Inform 6 ; cette semaine, voici un article sur les variables locales et globales.

Les variables sont des petits bouts de choses qui peuvent stocker une information, une valeur, un état qui est amené à potentiellement évoluer au cours de la partie. Oui oui, une variable est variable, et je viens de faire une grande phrase pour dire ça.

Un exemple de variable est le score. Au début, on démarre à 0, et si le joueur fait quelque chose de chouette, on lui donne un point. Comment donner un point au joueur ? On prend le nombre de points, on ajoute 1, et on dit que c’est le nouveau score. Ça s’exprime comme ça :

score = score + 1;

Le signe égal veut dire que la variable de gauche va maintenant prendre la valeur qui est à droite ; on met donc le nom d’une variable à gauche, et sa nouvelle valeur à droite. C’est une construction standard dans beaucoup de langages de programmation ; dans certains langages, on écrit même à la place du égal, ce qui est encore plus explicite (ce qui est à droite est mis dans la variable de gauche).

Un exemple plus complet : si nous avons une variable qui contient le nombre de pièces d’or du joueur, nous pouvons dire :

Global argent = 100;

Et ça veut dire que l’on commence avec 100 pièces. Si le joueur en dépense 10, nous écriront

argent = argent - 10;

Et quand nous voulons l’afficher, nous écrivons tout simplement print argent. Ce n’est pas très difficile et vous prendrez vite l’habitude.

Ce bout de code définit une variable globale, qui est définie une fois pour toute dans le code : il n’y en a qu’une avec ce nom, et c’est celle-là. Maintenant, qu’est-ce qu’une variable locale ? En Inform 6, c’est simplement une variable qui est rattachée à un objet. La façon de la définir est un peu différente. Prenons un exemple, tiré d’Ekphrasis, de FibreTigre :

Object simonetta "femme" plage
	with parled 0,
	...
	life [; Answer,Ask,Tell: if (self.parled==0) {SimonettaTalks();self.parled=1;return 2;} else {print "..."} ], ...

Nous avons ici Simonetta, qui possède une variable locale qui démarre à 0, et si on essaie de lui parler, le jeu regarde la variable locale : si on ne lui a pas déjà parlé, on lance la routine SimonettaTalks (une cinématique ou cutscene en fait) et on met la variable à 1, pour indiquer qu’on vient de lui parler ; si on lui a déjà parlé, on affiche autre chose. Donc on lance la cutscene la première fois qu’on lui parle, et sinon on affiche un autre message. Et oui oui, la variable s’appelle parled, comme il y en a d’autres dans le code nommées tremped.

Et en fait, parled et tremped sont des bons noms ! Vous avez tout à fait le choix dans le nom des variables, à part que vous ne pouvez pas mettre d’accent dans le nom ; mieux vaut choisir un nom explicite pour se souvenir de ce que modélise la variable, comme « combien de fois a-t-on parlé à cette femme » ou « est-ce que le drap est trempé ou non ».

Remarquez bien la façon dont on y fait référence : self.parled, « self » voulant dire « l’objet dans lequel on se trouve au moment du code » (ici, simonetta). Si on avait besoin de tester à un autre moment du code si on a déjà parlé à Simonetta, on ferait if (simonetta.parled == 0) (plutôt que self.parled == 0). La variable locale est rattachée à l’objet : il y a beaucoup d’autres personnages dans Ekphrasis auxquels on peut parler (Pazzi, Saint George, etc.) et qui ont tous une variable locale nommée parled ; pazzi.parled est une variable différente de simonetta.parled, même si dans les deux cas la variable qui nous intéresse s’appelle parled.

Vous allez me dire : « Pourquoi utiliser des variables locales si on a des variables globales ? » On pourrait juste dire que c’est plus propre : mieux vaut garder les propriétés et l’état d’un objet à l’intérieur de l’objet (c’est un grand principe de la programmation orientée objet, dont Inform 6 s’inspire). Mais en fait, la raison la plus intéressante à mon sens est le fait qu’on puisse justement définir des variables avec le même nom, et du coup faire du code un peu plus générique.

Voici maintenant un exemple plus complexe pour illustrer une différence entre variables globales et variables locales (ce qui suit est plus avancé, donc n’hésitez pas à y revenir plus tard !). Il s’agit d’un jeu avec des combats : chaque ennemi sera un objet, et aura des points de vie. Nous pouvons coder ceci avec une variable locale pv (points de vie) pour chacun des ennemis :

Object garde "garde"
   with pv 20,
        description
        ...
Object paladin "paladin"
   with pv 40,
        description
        ...
Object ecuyer "écuyer"
   with pv 12,
        description
        ...

C’est plus logique que d’avoir des variables globales très similaires garde_pv, paladin_pv, etc. : tout le monde a des pv, y’a les pv du garde, ceux de l’écuyer, et ainsi de suite.

On peut ensuite implanter des verbes de combat ; on va dire par exemple qu’on aura le verbe « estocade X » qui fait perdre 7 points de vie à la cible. Si nous avions des variables globales, il faudrait faire un gros test : si c’est le garde qu’on attaque, enlever 7 à garde_pv, si c’est l’écuyer, enlever 7 à ecuyer_pv, etc. Si nous avons 20 ennemis, nous allons avoir un if avec 20 cas et, à chaque nouvel ennemi, il ne faudra pas oublier d’ajouter le test correspondant. Bon courage… Avec des variables locales, c’est tout facile car elles ont toutes le même nom ! Il suffit de faire référence à la variable pv de l’objet concerné, qui sera ici noun (le nom reconnu durant l’analyse de la commande tapée). Nous pouvons même ajouter du code pour faire mourir les ennemis. Cela donne :

[ EstocadeSub ;
  noun.pv = noun.pv - 7;
  if (noun.pv <= 0) { remove noun; print "Le ", noun, " pousse un râle et meurt."; }
  return true;
];

Ce code est court et vous permet d’avoir du code flexible, car vous pouvez ajouter autant d’ennemis que vous voulez sans toucher à ce code. Bref, merci les variables locales !