Recherche

Recherche personnalisée

dimanche 29 mars 2009

Tutorial 6 Memory coloré (2/2)

Lors du dernier tutorial je vous ai présenté un jeu de memory. Nous allons maintenant en analyser le code. Commençons par observer les variables globales de ce script :



// Nombre de paires de couleurs
integer NombrePaires;
// Liste des etats des plots
list Visible = [];
// Liste melange des plots
list Melange;
// Liste des couleurs
list Couleurs = [];
// Premier plot touche
integer Premier;
// Second plot touche
integer Second;
// Constante de communication
integer GO = 10;
// Constante pour les plots
integer NON_TOUCHE = -1;



La première concerne le nombre de plots. L'exemple que j'ai réalisé sur SL en comporte 16, donc 8 paires. En programmation une règle important est d'aller du général au particulier. Il est toujours judicieux de prévoir un paramétrage complet pour ne pas avoir par la suite à modifier plusieurs lignes de code. Avec ce script il est facile de changer le nombre de plots, il suffit d'ajuster la valeur de la variable NombrePaires. Nous avons ensuite trois list déclarés. Les plots sont repérés simplement par leur numéro d'ordre dans la liaison des prims. Si vous réalisez le même ensemble que celui que j'ai présenté au dernier tuto (en respectant l'ordre pour le link) vous avez 18 prims en tout. Le root est numéroté 1, le bouton 2, les plots de 3 à 18. La liste Melange est destinée à conserver les numéros des plots dans un ordre aléatoire (pour avoir les couleurs mélangées), elle est donc composée de 16 valeurs de type integer. Les paires sont constituées pas les numéros d'ordre qui se suivent. Autrement dit les deux premiers dans la liste constituent la première paire, les deux suivants la paire suivante, et ainsi de suite. Cette liste peut donc être par exemple composée ainsi :


Plot

15

3

7

13

9

16

4

8

14

17

10

5

11

18

6

12
Paire

1

2

3

4

5

6

7

8



La liste Visible est destinée à mémoriser l'état d'un plot : couleur visible ou non visible. Elle contient donc autant de valeur que le nombre de plots, en l'occurrence 16. Au départ toutes ces valeurs sont initialisées à 0. La liste Couleurs sert à mémoriser la couleur des plots, elle est remplie de vector (une couleur est représentée par un vecteur avec ses trois composantes additives rouge, vert et bleu) et contient aussi autant de valeurs que de nombre de plots. Il aurait été évidemment possible de limiter le nombre de valeurs des listes Visible et Couleurs au nombre de paires, mais le code aurait été un peu plus complexe alors j'ai préféré privilégier la simplicité. Que les esprits chagrins ne m'en tiennent pas trop rigueur.

Les variables Premier et Second sont destinées à mémoriser à chaque instant les numéros des premiers et deuxièmes plots touchés. Au départ elles seront initialisées à la valeur -1 qui signifient qu'aucun plot n'a été touché. J'ai enfin prévu deux constantes destinées à améliorer la lisibilité du code mais j'y reviendrai en temps utile.

Voyons maintenant ce qui se passe au démarrage du script :



default
{
// Attente action du bouton
link_message(integer sender_number, integer number, string message, key id)
{
// Depart du jeu
if(number == GO)
{
initialisations();
state PremierPlot;
}
return;
}
}



Vous ne connaissez pas encore cet événement link_message. Il concerne la communication entre prims liées. Pour mieux comprendre allons voir le code du bouton :



integer GO = 10;

default
{
touch_start(integer total_number)
{
llMessageLinked(LINK_ROOT, GO, "", NULL_KEY);
}
}



Dès que celui-ci est touché il est mis en oeuvre la fonction llMessageLinked qui est destinée à envoyer un message aux prims liés. Le wiki vous parle un peu de cette fonction :



http://www.lslwiki.net/lslwiki/wakka.php?wakka=llMessageLinked



Le premier paramètre indique à qui s'adresse le message, ici nous visons le root, puisque c'est lui qui contient le script du jeu. Le second paramètre est un integer que nous allons utiliser pour distinguer les messages. ici nous déterminons que la valeur 10 (constante GO) signifie qu'on veut démarrer le jeu. C'est totalement arbitraire bien sûr. Les deux autres paramètres ne nous sont pas utiles.

Maintenant si nous en revenons au code d'entrée du script principal, l'événement link_message attend justement un message. Le premier paramètre indique le numéro du prim qui envoie le message, les trois autres paramètres correspond aux trois de la fonction, ce qui est rassurant ! Vous voyez donc que le test pour savoir si number est égal à GO signifie "est-ce qu'on doit démarrer le jeu ?". Si c'est le cas on procède aux initialisations nécessaires avec la procédure initialisations que nous allons voir plus loin et nous passons à l'état PremierPlot qui sert à attendre qu'on touche un premier plot. Si ce n'est pas GO (par exemple on a touché un plot ou le support) alors le return sort de l'événement et on se remet à l'écoute.

Voyons donc de plus près ces initialisations :



initialisations()
{
// Initialisation du nombre de paires
NombrePaires = 8;
// Initialisation des touches
Premier = NON_TOUCHE;
Second = NON_TOUCHE;
// Nombre total de plots
integer NombrePlots = NombrePaires * 2;
// Index du debut des plots
integer Debut = llGetNumberOfPrims() - NombrePlots + 1;
// Creation et remplissage de la liste des plots
integer x;
list Plots = [];
for(x = Debut; x < NombrePlots + Debut; x++)
{
Plots += x;
// Plots noirs au depart
llSetLinkColor(x, <0,0,0>, ALL_SIDES);
}
// Creation d'une liste melange des plots
Melange = llListRandomize(Plots, 1);
// Creation de la liste des couleurs des plots par paires
for(x = 0; x < NombrePaires; x++)
{
vector col = ;
Couleurs += col;
Couleurs += col;
}
// Remplissage de 0 de la liste Visible
for(x = 0; x < NombrePlots; x++)
Visible += [0];
// Avis de depart
llSay(PUBLIC_CHANNEL, "Jeu en cours");
}



Les deux variables Premier et Second sont initialisées à la valeur par défaut NON_TOUCHE qui signifie qu'aucun plot n'est encore touché. Ensuite on calcule le nombre de plots à partir du nombre de paires, ce qui ne nous est pas très difficile, cette valeur va nous servir pour les calculs ultérieurs. Ensuite on veut savoir à quel numéro commence le premier plot. Dans le cas où on a juste prévu un support et un bouton on sait que le premier plot est le 3. Mais il est toujours prudent de systématiser ce genre de chose. La fonction llGetNumberOfPrims permet de connaître le nombre total de prims liés. Il suffit alors d'enlever le nombre de plots et d'ajouter 1 pour avoir le numéro du premier.

Ensuite nous allons remplir la liste des plots. Dans une première liste Plots nous allons mettre dans l'ordre les numéros de plots. Pour cela nous utilisons une boucle for. Ici aussi le wiki peut vous aider au niveau de la syntaxe :



http://www.lslwiki.net/lslwiki/wakka.php?wakka=for



Une variable (x) est utilisée, elle a une valeur initiale (Debut), elle s'incrément (x++ signifie qu'on ajoute 1 à chaque fois, c'est équivalent à x = x +1 mais avec une écriture plus concise) et elle a une valeur limite (NombrePlots + Debut). Dans notre cas x commence à 3 et finit avant 19 (16 + 3), donc à 18. Chaque valeur de x est ajoutée à la liste Plots avec l'instruction Plots += x. Encore une syntaxe simplifiée de Plots = Plots + x. Au passage on en profite pour noircir le plot avec la fonction llSetLinkColor qui permet de colorer une prim liée en indiquant son numéro, la couleur représentée par un vector et les faces qui doivent être colorées. Dans notre cas la constante ALL_SIDES indique que nous intervenons sur toutes les faces, ce qui est convenable puisqu'une seule est apparente.

Nous savons que nous voulons les plots mélangés dans la liste Melange. C'est la fonction llListRandomize qui fait cela automatiquement (pourquoi je me plains de LSL moi finalement ? . Ensuite une autre boucle for est destinée à remplir la liste Couleurs. Puisque les couleurs marchent par paires on se contente de boucler sur celles-ci. Pour avoir une génération de couleur aléatoire on ut lise la fonction llFrand qui avec le paramètre 1 générer une valeur entre 0 et 1, pile ce qui nous faut pour les éléments de notre vecteur de couleur.

Il ne nous reste plus qu'à remplir la liste Visible avec des 0. C'est ce que nous faisons avec une dernière boucle for. Vous remarquez que lorsqu'il n'y a qu'une instruction dans un bloc on peut se passer des accolades, ce qui simplifie un peu la syntaxe. On termine avec un petit message sur le Chat pour dire que le jeu est en cours... Nous pouvons maintenant passer à l'analyse de l'état PremierPlot :



state PremierPlot
{
// Reception d'un message des plots
link_message(integer sender_number, integer number, string message, key id)
{
// Nouveau depart du jeu ?
if(number == GO)
{
initialisations();
return;
}
// Plot deja visible ?
if(TestVisible([sender_number]))
return;
else
{
// Affectation premier plot
Premier = sender_number;
// Colorisation du plot
ColorePlot(sender_number);
// Passage au choix du second plot
state SecondPlot;
}
}
}



Ici aussi nous attendons un message. Si nous recevons celui du bouton (GO) nous procédons aux initialisations pour redémarrer le jeu. sinon nous allons voir si le plot touché n'est pas déjà visible avec la fonction TestVisible que nous verrons plus loin. Si le plot n'est pas visible alors nous affectons Premier avec son numéro et nous colorons le plot avec la procédure ColorePlot que nous verrons également plus loin. Enfin nous passons à l'état SecondPlot pour attendre le second plot touché. Mais avant de voir cet état explorons les méthodes que nous avons vues passer :



// Renvoie la valeur de visibilite TRUE (1) ou FALSE (0)
integer TestVisible(list sender_number)
{
integer Index = IndexPlot(sender_number);
return llList2Integer(Visible, Index);
}



La fonction TestVisible a pour objet de nous renseigner sur le fait qu'un plot est déjà coloré ou pas. Nous connaissons le numéro d'ordre du plot mais pas son emplacement dans la liste Melange (puisque nous avons tout mélangé !). Une autre fonction nous est donc nécessaire pour connaître l'index du plot dans cette liste :



// Renvoie l'index du Plot
integer IndexPlot(list sender_number)
{
return llListFindList(Melange, sender_number);
}



On utilise la fonction llListFindList dont je vous ai parlé au tuto précédent et qui a une liste comme paramètre, d'où le type de paramètre de la fonction. On en revient à TestVisible qui est maintenant renseignée sur l'index du plot. Il suffit alors d'aller lire avec la fonction llList2Integer la valeur correspondante. Un 0 nous dit que la couleur n'est pas visible et un 1 l'inverse.

Nous pouvons maintenant voir l'état SecondPlot :



state SecondPlot
{
// Reception d'un message des plots
link_message(integer sender_number, integer number, string message, key id)
{
// Nouveau depart du jeu ?
if(number == GO)
{
initialisations();
state PremierPlot;
}
// Deja un second plot touche ?
if(Second != NON_TOUCHE)
return;
// Deuxieme plot choisi
Second = sender_number;
if(TestVisible([Second]) || Second == Premier)
return;
else
{
// Colorisation du plot
ColorePlot(Second);
// Test paire trouvee
if(TestePaire(Premier, Second))
{
// Affecte visible pour les deux plots
SetVisible(Premier);
SetVisible(Second);
// Reinitialisation
Premier = NON_TOUCHE;
Second = NON_TOUCHE;
// Test de fin du jeu
if(TestFin())
llResetScript();
// Retour au choix du premier plot
state PremierPlot;
}
else
// Timer 2 secondes
llSetTimerEvent(2);
}
}

// Temporisation
timer()
{
// Remise a noir de la couleur des deux plots
llSetLinkColor(Premier, <0,0,0>, ALL_SIDES);
llSetLinkColor(Second, <0,0,0>, ALL_SIDES);
// Reinitialisation des touches
Premier = NON_TOUCHE;
Second = NON_TOUCHE;
// Reset du timer
llSetTimerEvent(.0);
// Retour au choix du premier plot
state PremierPlot;
}
}



Un peu plus de code là parce que le traitement est plus complexe. Le départ est identique à l'état PremierPlot. On attend un message. Si c'est un GO on réinitialise. Sinon on se demande si le plot touché est déjà touché (pour éviter une sélection multiple pendant la temporisation). Ensuite un double test : le plot est-il déjà visible ? Correspondit au premier plot sélectionné ? Le but est d'éliminer toutes les actions parasites. Si on est satisfait de ces tests on colore le plot et on se demande si on est tombé sur une paire. Pour cela on a prévu une fonction TestePaire :



integer TestePaire(integer a, integer b)
{
if(llList2Vector(Couleurs, IndexPlot([a])) == llList2Vector(Couleurs, IndexPlot([b])))
return TRUE;
return FALSE;
}



Elle attend en paramètres les numéros des deux plots. Ensuite on précède à un test pour comparer les deux couleurs. On sait que les couleurs sont dans la liste Couleurs. La fonction permet d'extraire un vector d'une liste en indiquant son index. On utilise à nouveau la fonction IndexPlot. La valeur de retour est TRUE ou FALSE selon le résultat de la comparaison. Revenons alors à la suite du code de l'état SecondPlot. S'il s'agit d'une paire on renseigne la liste Visible avec la méthode SetVisible :



// Affecte la visibilite au Plot
SetVisible(integer plot)
{
integer Index = IndexPlot([plot]);
Visible = llListReplaceList(Visible, [1], Index, Index);
}



Le but est de mettre un 1 à la place du 0 à l'index qui correspond au plot donc le numéro est passé en index. La fonction llListReplaceList permet de remplacer une ou plusieurs valeurs dans une liste. Si nous en revenons à l'état on réinitialise les variable Premier et Second. On se demande ensuite si le jeu est fini. Est-ce que la paire trouvée est la dernière ? A nouveau une fonction est destinée à nous renseigner :



// Test de fin
integer TestFin()
{
if(llListFindList(Visible, [0]) == -1)
return TRUE;
return FALSE;
}



Comment savons-nous que c'est terminé ? Tout simplement que la liste Visible est remplie de 1. On fait donc un test dans cette liste pour savoir si elle contient encore un 0. A noter au passage que cette fonction aurait pu être simplifiée en return (llListFindList(Visible, [0]) == -1); mais elle est peut-être plus explicite ainsi. La même remarque vaut pour le test des paires. Si on est arrivé à la fin du jeu on fait un reset du script, sinon on retourne à l'état PremierPlot.

Maintenant que se passe-t-il si ce n'est pas une paire ? Dans ce cas nous démarrons un timer avec la fonction llSetTimerEvent dont le seul paramètre indique le nombre de secondes à attendre. Ceci est destiné à laisser le temps de voir les deux couleurs suffisamment avant de les effacer. Le délai est réglé à deux secondes, vous pouvez évidemment le changer s'il vous paraît trop court ou trop long. Au bout du délai l'événement timer est déclenché. Dans un premier temps on remet du noir sur les deux plots puis on réinitialise les variables Premier et Second. On prend aussi la précaution de réinitaliser le timer sinon toutes les deux secondes il va continuer à se déclencher même si on change d'état ! Enfin on retourne à l'état PremierPlot pour attendre la première couleur choisie.

5 commentaires:

  1. C'est avec plaisir que je regarde votre site ; il est formidable. Vraiment très agréable à lire vos jolis partages .Continuez ainsi et encore merci.

    voyance gratuite par telephone

    RépondreSupprimer
  2. Tu touches un point sensible là en effet je pense aussi que c’est un manque de confiance en moi.
    voyance gratuite mail

    RépondreSupprimer
  3. Merci pour cet article qui ajoute un + dans ce qu’on connaît déjà. Je te suis depuis quelques mois, et je tiens à te remercier pour tes conseils qui aident les jeunes bloggueurs aujourd’hui à mieux avancer dans leur activité.
    voyance par mail

    RépondreSupprimer
  4. Magnifique article ! Chapeau bas pour ce grand professionnel et son côté chaleureux ! J'aime vraiment votre site, simple, coloré et chaleureux lui aussi.
    voyance en ligne

    RépondreSupprimer
  5. Un excellent bravo pour un excellent sujet et un excellent blog !!!

    voyance serieuse gratuite par mail

    RépondreSupprimer