17 décembre 2008

Reflets de Lune

Notre vie est éphémère
Pareille au reflet de la lune
Dans la goutte d'eau
Tombant du bec du héron


Dôgen Zenji

5 novembre 2008

LSL : l'évènement listen

Après avoir passé un long moment à détailler les variables du LSL et quelques fonctions importantes, nous en avons enfin terminé avec les cours d'initiation au LSL.
Les tutoriaux qui vont suivre détailleront la création de petits scripts qui illustreront certaines fonctions ou evenements du LSL.

Le script du jour consistera à réaliser un objet extrèmement casse pied : un perroquet. Pour notre exemple, bien sur, un simple cube posé sur son épaule fera l'affaire. Ce qui m'interesse, c'est le script.
Avant d'entrer dans le vif du sujet, expliquons en gros le fonctionnement d'un script.

Un script contient un ou plusieurs états. A un instant t, seul un etat est actif. Un script contient toujours un etat nommé default, qui est l'etat par defaut, comme son nom l'indique. Cet etat doit obligatoirement être le premier etat contenu dans le script. Lorsqu'un script se met en route, c'est cet etat qui est appelé.

Exemple : un script de porte, avec deux etats : default (qui correspondra à l'etat porte fermée) et ouvert (etat pour la porte ouverte cela va de soi)
Chaque état va gerer les actions possibles du script à un moment de sa vie, de son déroulement.

Le LSL est donc un langage de scripting d'etat, mais c'est aussi un langage evenementiel, c'est à dire qui fonctionne sur le principe emeteur / récepteur d'evenement.

Un evenement est declenché, l'etat dans lequel le script se trouve peut recevoir l'evenement et le traiter, à condition qu'il soit abonner au préalable à la réception de l'évènement. Ainsi pour la gestion de nombreux evenements, nous aurons une paire fonction/evenement, la fonction abonnant l'etat à la reception d'un evenement, l'evenement accomplissant l'action désiré.



I - Quelques evenements

state_entry est l'evenement qui est appelé par le script à chaque fois que l'on entre dans un etat. De meme, state_exit, lorsque l'on sort. Ainsi, si dans le script on demande par exemple à changer d'état, avant d'entrer dans le nouvel etat, l'evenement state_exit sera appelé.

touch_start est appelé à chaque fois que on clique sur l'objet. C'est un des evenements les plus utilisés du LSL.

listen se declenche chaque fois qu'un message est envoyé sur le chat, et cela quelque soit le canal utilisé. Cet evenement nous sera donc tres utile pour notre perroquet.

timer permet de réaliser une action à interval fixe. Je veux par exemple faire clignoter une lampe. Je dois donc l'allumer puis l'eteindre, etc... toutes les 0.5 sec par exemple. On utilisera pour cela le timer.

sensor permet d'effectuer une detection. C'est grace à lui que quand vous entrez dans une boutique, vous avez le message : Welcome Deirdre Thor... Enfin, la, c'est moi qui est le message surtout....

L'evenement changed permet de détecter un changement au niveau de l'objet : liaison, inventaire, couleur, propriétaire, forme, ....

collision permet de détecter des collisions, comme son nom l'indique. Cet evenement est tres utilisé chez les marchand d'arme : quand il entre en collision avec sa cible, le missile explose....



II - Cahier des charges


Voyons à present le cahier des charges de notre perroquet.

  • On clique dessus pour le reveiller, le mettre en route (donc... touch_start)

  • Une fois réveillé, il va répéter tout ce que son propriétaire dit dans le chat (donc.... listen)

  • Si je lui dit : "zut".... il s'arrete de bavarder, et de me casser les pieds, et se rendort, attendant que je le reveille à nouveau.

  • Seul son propriétaire peut le reveiller, et il ne repete que ce que celui-ci dit.



III - Reflechissons

Notre perroquet peut dormir ou etre eveiller. Nous utiliserons donc deux etats. Il serait tout à fait possible de le faire en un seul, toutefois, il faudrait resoudre des problemes du style : je clique deux fois sur le perroquet, que se passe-t-il ?

  • Endormi : etat default

  • Eveillé : etat bavard



Mise en route du perroquet : par clic, donc utilisation d'un evenement touch_start dans l'etat default.

L'infame bavardage. Pas de doute ici, il nous faut un listen. Pour mettre notre etat en ecoute, il faudra l'abonner au listen, par la fonction llListen. Un script peut etre abonner à la reception de plusieurs listen. Pour les différentier, la fonction llListen renvoie un nombre entier correspondant au numero de l'écoute, numero que nous conserverons dans une variable. Pourquoi cela ? Quand mon perroquet va se réendormir, il n'est plus besoin de conserver l'ecoute. Donc on la supprimera grace à la fonction llListenRemove qui prend en parametre.... le numero de l'ecoute justement.
Notre llListen contiendra un filtre : le perroquet n'écoute que son maitre.

prototype de llListen : integer llListen(integer chan, string avi, key id, string msg);

  • chan : le canal de l'ecoute : pour nous il s'agira du canal 0, le chat classique, mais nous utiliserons la constante PUBLIC_CHANNEL pour plus de clarté

  • avi : le nom de l'avatar. Nous utiliserons "", la chaine vide, nous filtrerons sur la key plutot que sur le nom

  • id : la key du proprietaire du perroquet

  • msg : le message attendu pour agir. Ici, chaine vide encore, on ecoute tout ce qui est dit.



IV - Le script et ses commentaires


key avatar;
integer ecoute;


default
{
state_entry()
{
avatar = llGetOwnerKey(llGetKey());
}


touch_start(integer total_number)
{
if (llDetectedKey(0) == avatar){
state bavard;
}
}
}





state bavard{


state_entry()
{
ecoute = llListen(PUBLIC_CHANNEL, "", avatar, "");
}


listen(integer channel, string name, key id, string message)
{
if( message != "zut") llSay(PUBLIC_CHANNEL, "Coco repete : " + message );
else {
llSay(PUBLIC_CHANNEL, "Coco est fatigué, Coco va dormir");
llListenRemove(ecoute);
state default;
}
}


}




A l'entrée dans l'etat default, on recupere la key du proprietaire du perroquet. Puis le script attend qu'on clique sur le perroquet. Quand on clique, il vérifie que celui qui clique est bien le proprietaire du perroquet. Si c'est le cas, le perroquet se reveille, on passe dans l'etat bavard.

A l'entrée dans cet etat, on ouvre l'ecoute sur le canal public, pour le proprietaire.
Chaque fois que ce dernier dit quelquechose, l'evenement listen s'active. Cet evenement recoit 4 paramètres : le canal sur le quel l'ecoute est faite (channel), le nom de celui qui a parlé (name), sa clé (id), et ce qu'il a dit (message).
Si le message n'est pas "zut", le perroquet le repete dans le chat.
Sinon, il dit qu'il va se coucher, supprime l'ecoute qui n'est plus utile et change d'etat pour revenir a l'etat de sommeil, l'etat default.
De retour dans default, le perroquet attend un clic pour recommencer son cinéma et nous casser encore plus les pieds.



V - Conclusion

Voila, il ne vous reste plus qu'a copier - coller ce script dans une primitive et à le tester. C'est tout pour aujourd'hui.... Deirdre est fatiguée, Deirdre va se coucher ... Hihihi !!

Ce petit tuto avait pour but d'illustrer un exemple d'utilisation de l'evenement listen. La prochaine fois, nous travaillerons sur le timer.
N'hésitez pas a me laisser des commentaires, si des erreurs se sont glisser dans le script, ou si vous avez des envie de tutoriaux précis, ou pour m'encourager a continuer ce blog !!

29 octobre 2008

Voyages

Je me promène sans bruit.
La lune pour seul amie.

Où me guide mes pas ?
Toujours plus loin au fond des bois.
Toujours plus loin au plus profond de moi.

Qui peut comprendre comprendre le chemin parcouru ?
Celui qui le parcours, le cœur léger et sans but.


Le chemin du serpent dans l'herbe,
Seul le serpent le comprend



26 octobre 2008

Firmament

Je suis assise et je regarde le ciel. La lune pleine illumine le ciel. Adossée au pied de l'arbre, je contemple le ciel. La lune argentée fait briller mes cheveux blancs.

Il n'y a rien de plus vaste sous le ciel,
Que la pointe d'un épi automnal
Et une grande montagne
Est une petit chose face au firmament.
Tchouang Tseu


25 octobre 2008

Arguties

Un petit texte de Tchouang Tseu...
Notre société passe son temps à parler pour ne rien dire.. On fait des réunions, on bouge, on déplace du vent, tout ça pour quoi ??
On découpe, on dissèque, on classe, on range dans des boites... Si ça ne rentre pas alors cela est suspect.
Quand on rencontre quelqu'un pour la première fois.. on ne cherche pas à la connaitre, la première chose qui nous traverse la tête, c'est de la mettre dans une boite avec une étiquette dessus. Sympa, pas sympa, bizarre, étrange, non conforme, on stéréotype à tout va !!
Il n'est jamais trop tard pour changer nos habitudes....


Tout le monde discute et émet
Des catégories de contraires.
J'aimerais entendre un discours,
Qui n'entre dans aucune catégorie


24 octobre 2008

Pensée du jour

Une petit pensée que je laisse à vos meditations pour le Week end.... Ô toi lecteur de ce blog, qu'il te soit doux et agréable.....


Comme les montagnes dominent
Sur de vastes étendues,
C’est seulement grâce à elles
Que l’on peut chevaucher les nuages,
Ce sont elles qui confèrent l’inimaginable
Privilège de s’élever avec le vent.

23 octobre 2008

La vie.... la mort...

Une petit histoire.......

On raconte que, un jour, en chine, l'empereur fit venir à lui un homme sage et pratiquant la voie.
Il lui demanda : "Toi qui es la sagesse incarnée, calligraphie-moi un poême, sur ce qu'est le bonheur absolu, apprends moi le secret du bonheur, de la félicité. Que je l'accroche à la porte de mon palais."

Le sage médita, et calligraphia :
Le grand père meurt
Le père meurt
Le fils meurt


A la vue de ce poeme, l'empereur se mis en colère, pensant que le sage se moquait de lui. Il voulait entendre parler de bonheur, et le vieil homme parlait de mort.

Alors le sage dit :
C'est pourtant cela, le secret du bonheur.... Nul ne doit survivre à ses enfants.

Et l'empereur comprit... et s'inclina devant le sage.

22 octobre 2008

Cours d'initiation au langage LSL (VI)

Interessons nous aujourd'hui au type list....

Ce type de variable est assez particulier car il permet d'agréger ensemble des variables de types différents. Une liste, le type list, est une suite d'éléments de types divers. Ainsi je peux trouver dans une liste des string, des integer, .....
Une liste est délimitée par des crochets, et les éléments sont séparés par des virgules.

Exemple de liste :

List ma_liste = [ 12, 2.36, "un message"];


Ici : un integer (12), un float (2.36), et une string ("une message"). Chaque type est écris avec ses propres conventions : la string est encadrée par les guillemets. Si il y avait un vector, on l'écrirait entre <>....


ma_liste = [];


Par cette instruction, on vide la liste.... c'est une liste mais qui ne contient rien, c'est donc une liste vide.


On ajoute une élément à une liste en utilisant l'opérateur += (qui correspend en fait à : ma_liste = ma_liste + .......).
On remarquera que, on ajoute, en fait, à une liste une autre liste.

ma_liste += ["un pitit texte"];
ma_liste += ["un autre pitit texte", 12, <0.0,0.0,>];



Parcourir une liste

On utilisera la fonction llGetListLength qui renvoie le nombre d'éléments d'une liste. Le premier élément d'une liste à pour index 0. Et donc, le dernier, la taille de la liste moins 1.
De même, pour obtenir un element de la liste, on utilisera la fonction llList2String

Prototype des fonctions :
integer llGetListLength( list src )
string llList2String( list src, integer index )


list ma_liste = ["e1", "e2", "e3"];
integer idx;

for(idx = 0; idx < llGetListLength(ma_liste); idx++){
llSay(PUBLIC_CHANNEL, llList2String(ma_liste, idx) + " a pour index : " + (string)idx);
}


Attention toutefois... Ma liste ne contient ici que des éléments de type string..... j'utilise donc la fonction llList2String. Mais pour un integer , je dois utiliser la fonction llList2Integer, et ainsi de suite.....


Il existe bien sur des fonctions pour savoir quel est le type de l'élément qui se situe à l'index idx.... Cela permet ensuite de choisir la bonne fonction llList2.... Je vous renvoie au wiki second life consacré au LSL, et qui reste LA référence et le livre de chevet de tout bon scripteur.



Recherche

Pour savoir si une valeur est presente dans une liste, nous utiliserons la fonction llListFindList.
Prototype : integer llListFindList(list source, list element)

Exemple :

list ma_liste = ["e1", "e2", "e3"];
integer idx = llListFind list(ma_liste, ["e2"]);


Notons que l'élément recherché est passé à la fonction sous la forme d'une liste de un seul élément. Dans le cas présent, idx vaut 1.



Transformer une list en string

prototype de la fonction : string llDumpList2String( list ma_liste, string separateur);
Exemple d'utilisation :

list ma_liste = ["e1", "e2", "e3", "e4"];
string s= llDumpList2String(ma_liste, "-");
llSay(PUBLIC_CHANNEL, s);


La fonction prend une string qui correspond au séparateur que l'on souhaite mettre entre les éléments. Ici, j'ai choisi - ..... D'où l'affichage suivant dans le chat :
e1-e2-e3-e4

Ceci est parfois tres utile pour le debuggage d'un script, de maniere à savoir ce que contient une liste...

Voilà, c'est tout pour aujourd'hui........ La prochaine fois on s'attaque aux choses sérieuses !! (encore que ...)
Si vous avez des questions, des remarques, n'hesitez pas bien sur à me laisser un message. Si vous avez aussi des envies de tuto, pareil.... mais attention, je ne developpe pas de script pour autrui.... rendre service oui, faire le boulot des autres qui le reprendront, voir même qui feront un objet payant sous sl avec ce code, non merci.... ou alors je prends 80% hihihi !!
Maintenant, si c'est uen commande, alors là, c'est autre chose.... laissez moi un IM sous SL

Donc, si vous voulez faire une alarme, très bonne idée, mais ne vous attendez pas à recevoir un code entier. Par contre je me ferais un plaisir de vous faire un petit tuto sur les sensors... avec un exemple simple, qui vous aidera a réaliser votre alarme.

Voila ! c'est tout pour ce soir......

21 octobre 2008

Cours d'initiation au langage LSL (V)

Bien, le cours du jour concernera le type de données string.
Il sera l'avant dernier cours à présenter les éléments de base du langage LSL, le dernier sera consacré au type list. Les prochains cours seront tous construits sur le même modèle : nous commencerons à étudier les évènements. Pour cela, explication sera donnée de l'évènement et de sa capture. Puis, je vous proposerai un petit script : cahier des charges de ce qu'il doit faire, le script, et l'explication des différentes parties du script.

Mais commençons par le commencement... le type string !



I - STRING : les chaines de caractères

Une chaine de caractères se repère très vite car elle est délimitée par des guillemets....
Dans l'exemple qui suit, "Bonjour à tous" est une chaine de caractères affectée à la variable msg.

string msg;
msg = "bonjour à tous";


L'opération la plus utilisée sur les chaine de caractères, est la concaténation : il s'agit de regrouper , de coller des chaines de caractères pour n'en former plus qu'une seule.

msg = "Bonjour " + "à tous";

Que l'on écrive : "Bonjour à tous" ou "Bonjour " + "à tous" cela revient à la même chose.
La concaténation de chaines se fait grâce à l'opérateur +


string msg1, msg2, msg;
msg1 = "Kikooooo";
msg2 = "les amis !!";

msg = msg1 + msg2;


Que vaut msg ?? il est la concaténation de msg1 et msg2, soit : Kikoooooles amis !! On remarque qu'il y a un problème d'espacement, ce qui est normal, vu que msg1 et msg2 ne contiennent pas d'espace. Pour que le message soit correct, je dois ajouter un espace entre msg1 et msg2...
Ce qui nous donne :

msg = msg1 + " " + msg2;

Et là, ca marche !!
Les guillemets sont les délimiteurs de la chaine... mais alors, comment faire si je veux une chaine de caractères qui contienne des guillemets ?? Il faudra pour cela utiliser le caractère d'échappement \, qui despécialisera les guillemets :

msg = "Comment ca \"lapin\" ?? ";

La chaine msg, si on l'affiche donnera : Comment ca "lapin" ??
Voici les caractères d'échappement du LSL :

\t tabulation
\n passage à la ligne
\" "
\\ \



Quelques fonctions sur les chaines......



llStringLength compte le nombre de caractères d'une chaine

string msg = "Youppi c'est les vacances !!";
llWhisper(PUBLIC_CHANNEL, msg + " contient exactement " + (string)llStringLength(msg) + " caractères...");


Nous lirons sur le chat : Youppi c'est les vacances !! contient exactement 28 caractères...
Une petite remarque.... que viens faire ici (string) ??
La fonction llStringLength renvoie un nombre entier.... or, un nombre n'est pas une chaine de caractères.... Je dois donc convertir l'entier en string par l'opération de cast (cf un des précédent cours...). Ici je caste en string donc : (string).
Il s'agit la d'une erreur fréquente... le compilateur refuse de faire son travail et nous donne une erreur dans le message que l'on veut écrire sur le chat..... bien souvent, c'est parce que la chaine que l'on veut écrire, contient un morceau qui n'est pas du type string... L'ajout du cast (string) résoud bien souvent le problème.



llGetSubString permet d'extraire d'une chaine une sous chaine.
Prototype de la fonction : string llGetSubString(string msg, integer debut, integer fin)
Par exemple : dans l'exemple suivant, on va extraire juste le mot "les"..... (le mot "les", pas le mot laid, ou le Molay, car pour ce dernier, c'est impossible depuis 1314... hihi)

string msg = "Youppi c'est les vacances !!";
msg = llGetSubString( msg, 13, 15 );
llSay(PUBLIC_CHANNEL, msg);


La première lettre de la chaine est indicée 0...... donc je prends les caractères en commençant au caractère d'index 13 et termine au 15 inclus...



llSubStringIndex permet de savoir si une chaine donnée est incluse dans une plus grande.. et si oui à quel endroit....
Elle renvoie donc un entier correspondant à l'index de la sous chaine, et elle renvoie -1 si elle ne trouve pas.
Prototype : integer llSubStringIndex(string msg, string sous_chaine);

Exemple : ma chaine contient-elle le mot Ouma ??

string msg = "Mais qui vois je la bas ?? Serait ce mon amie Ouma ??
integer idx = llSubStringIndex(msg, "Ouma");
if (idx == -1) llSay(PUBLIC_CHANNEL, "Ouma n'est pas la !!");
else llSay(PUBLIC_CHANNEL, "Ouma est la !!");


Si j'exécute ce code..... je peux lire dans le chat : Ouma est la !! hihi.... J'aime faire participer, à leur insu, mes amies à mes cours !!


Autre exemple d'application de cette fonction : beaucoup d'objets fonctionnent avec une notecard qui contient des informations pour paramétrer l'objet. En général, on note des informations sur une ligne, et chaque ligne contient ainsi plusieurs paquets de paramètres.
Illustrons cela par un exemple : nous ne verrons pas ici la lecture de la notecard, mais seulement le traitement de la ligne....
Ma notecard contient un nom d'avatar puis un point virgule, puis un nombre. Et ainsi de suite sur chaque ligne....

par exemple :
Deirdre;25
Yoyo; 27
Ouma;32
Lala; 36
Nounours;34




Dans mon script, je récupère une ligne de cette notecard dans une variable ligne. Je veux ensuite affecter le nom à la variable nom, et le nombre à la variable nombre....... Pour cela, je vais agir de la manière suivante : créer deux sous chaines : de 0 à index de ";" et de index de ";" à fin.... Mon pivot est donc l'adresse du ";".

string ligne = "Deirdre;25";
string nom;
string nombre;

nom = llGetSubString(ligne, 0, llSubStringIndex(ligne, ";")-1);
nombre = llGetSubString(ligne, llSubStringIndex(ligne, ";") + 1, llStringLength(ligne));

llSay(PUBLIC_CHANNEL, nom + " a pour nmbre : " + (string)nombre);



Pour le nom, llSubStringIndex(ligne, ";") me donne l'index du ";". Or je ne veux pas de lui dans le nom... d'où le -1 qui recule l'indice....
De même, pour le nombre, je me place apres l'index pour commencer à le lire....
Remarquez aussi que je vais jusque llStringLength(ligne), qui correspond a la longueur de ma chaine. J'aurais pu aussi écrire :

nombre = llGetSubString(ligne, llSubStringIndex(ligne, ";") + 1, -1);

Ici, -1 signifie : le dernier caractère de la chaine. Cette version est préférable, car elle evite l'appel à une nouvelle fonction, et rendre le code plus aisé à lire, à partir du moment où on sait ce que ce -1 veut dire bien sur.


Il existe d'autres fonctions concenrant le traitement des string. Je n'en ai présenté ici que quelques unes, pour donner une idée de leur utilisation.
C'est tout pour aujourd'hui.

16 octobre 2008

Poême Zen....

Je dédis ce petit poême Zen  à Lanna, ma Senseï.
Elle comprendra :)


L'ombre des Bambous balaie les marches
Mais nulle poussière n'est soulevée.
Le clair de lune pénètre profondément la marre
Mais l'eau n'en garde nulle trace

Que cette pensée t'apporte la paix et la sérénité Ô Senseï.....
Et n'oublie pas que :

La noirceur de l'ombre des pins
dépend de la clarté de la lune
Kodo Sawaki



8 octobre 2008

Cours d'initiation au langage LSL (IV)

Aujourd'hui, nous allons voir les fonctions LSL liées aux variables de type KEY, ainsi que quelques constantes utilisables à tout moment dans vos scripts, sans avoir besoin de les définir, cela est déjà fait dans le langage LSL.

Mais avant, voici quelques fonctions essentielles et les plus courantes du LSL...



I. Les fonctions de communication


Pour communiquer, un objet fait appel à un certain nombre de fonctions. Pour parler, on doit le faire dans un canal de chat. Le canal lisible par tous est le canal publique 0. Tout ce qui est dit sur ce canal est perceptible par tous, dans une limite de distance.


Une limite ??

Et oui, quand on chuchote, parle ou crie, on touche plus ou moins de personnes. Ainsi :


Chuchoterwhisper10m
Parlersay20m
Criershout100m



L'avatar ne peut que parler ou crier, mais un objet scripté peut en plus chuchoter.

Par contre, on peut choisir le canal sur lequel on parle. Si on ne précise rien, il s'agit du canal 0.
Si on veut parler sur un autre canal, on doit ecrire le numéro du canal précédé de /
Par exemple :

/100 kikooooo !

Ecrira donc "kikooooo !" sur le canal 100.

Toutefois, un avatar ne peut pas écouter sur ce canal, et toute phrase écrite dessus ne produit rien sur le chat (qui est le canal 0). Par contre un objet le peut (nous verrons comment plus tard). C'est de cette manière que l'on peut commander certain objet via le chat.

Si je veux faire cracher du feu à mon dragonnet, je lui parle sur le canal 28 (par exemple).
Ainsi : /28 flammeon, et il souffle, et /28 flammeoff, et il s'arrete. Personne ne m'a vu écrire un ordre, ni quel est cet ordre, ni le canal que j'utilise.

Ainsi donc, dans toutes les fonctions qui vont suivre, l'un des paramètres est ce canal.
Toutefois, pour plus de compréhension dans la lecture du script, on evitera d'écrire 0 pour indiquer le canal public.
Il existe des constantes que l'on utilisera. Ecrire le nom de la constante revient à ecrire la valeur à laquelle elle correspond, mais en rendant son script plus lisible.
  • PUBLIC_CHANNEL : le chat public, de valeur 0

  • DEBUG_CHANNEL : le canal utilisé pour le débuggage de script


1) Chuchoter, parler, crier.


llWhisper(integer channel, string message);
llSay(integer channel, string message);
llShout(integer channel, string message);


  • channel : le canal ou l'on parle

  • message : le message.


Par exemple :

llWhisper(PUBLIC_CHANNEL, "Bisous toi !!");
llSay(PUBLIC_CHANNEL, "Bonjour à tous !!");
llShout(PUBLIC_CHANNEL, "Lapin !!!!");



Note : Lapin ??
Ben oui.... vous savez comment on dit lapin à un sourd ?? non ?? on dit : LAPIN !!




2) Plus loin c'est possible ?

Et oui, un objet peut parler sur une sim entiere. Mais attention, il ne peut pas le faire sur le canal PUBLIC_CHANNEL. Imaginer le spam sur une sim où toutes les dix secondes, on aurait une voix qui parlait !!!

llRegionSay(integer channel, string message);



3) Plus prêt, dans le creux de l'oreille ? Rooooo !!

Encore oui !! Rooooo derechef !! Cela est possible.
L'objet parle dans le chat, mais seul son propriétaire peut entendre ce qu'il dit. Dans ce cas pas besoin de préciser le canal, il n'y a que le message en paramettre à cette fonction. Le message s'affiche dans votre fenetre de chat, dans une couleur jaune, indiquant que seul vous pouvait l'entendre


llOwnerSay(string message);


4) Il y a autre chose ?

Oui, mais nous verrons plus tard; Il s'agit de la petite boite de dialogue bleue, qui apparait dans le coin supérieur droit de la fenetre de SL.
Elle fait dejà l'objet d'un tutorial entier.



II. Tout (ou presque) sur les KEY



Petit retour sur les Key.
C'est un identifiant unique, ou UUID pour Universally Unique Identifier, qui caractérise chaque objet de SL. C'est en fait une STRING dont le format est défini d'une certaine manière. Un UUID est donc stocké dans une variable de type KEY.

Masque d'une Key : XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, soit donc une série de 5 nombres ecrits en base hexadécimale. Par exemple : 153f69ca-1de5-ffff-0000-125cb84a58e2.

Il existe pour les Keys une constante, la clé "nulle" :
  • NULL_KEY , de valeur : 00000000-0000-0000-0000-000000000000


llGetKey( ) ==> key

Cette fonction retourne la Key de l'objet qui la contient, ou pour etre plus précis, du prim dans lequel se trouve le script. Ainsi, un objet constitué de plusieurs prims possède une Key par prim le composant.



llDetectedKey( integer number ) ==> key

number représente la valeur de l'index de détection.
Cette fonction sert souvent pour connaitre la key d'un objet détecté.
Exemple d'utilisation : connaitre qui touche l'objet. L'index correspondant à un avatar est 0.


default
{

touch_start(integer total_number)
{
key k = llDetectedKey(0);
llSay("j'ai été touché par " k);
}
}



Créez un objet et copier ce script dedans. Puis cliquer sur l'objet, et vous obtiendrez une key qui est celle de votre avatar, votre UUID personnel.



llKey2Name( key id ) ==> string

Cette fonction renvoie de le nom correspondant à la prim ou à l'avatar dont la key est passée en paramètre.
Nous pouvons utiliser cette fonction avec celle vue précedemment. llDetectedKey donne la clé de celui qui touche, et passe cette clé à llKey2Name, qui la convertit en un nom.

Exemple :


default
{

touch_start(integer total_number)
{
llSay("Bonjour " llKey2Nam(llDetectedKey(0)));
}
}




llGetOwnerKey(key id) ==> key
llGetOwner() ==>key


La fonction llGetOwnerKey retourne la clé du propriétaire de l'objet que l'on passe à la fonction par sa key.
llGetOWner retourne la key du propriétaire de l'objet.

Arrêtons sur ce point. Imaginons un objet qui effectue une action quand on le touche. Je veux que seul le propriétaire de l'objet puisse le faire fonctionner. Pour cela il faut que je regarde si celui qui le touche est bien le propriétaire. Cela se fera par key interposées.



key proprio;

default
{

state_entry()
{
// à l'entrée dans le script, on affecte à proprio
// la key du propriétaire de l'objet
proprio = llGetOwner();
}

on_rez(integer a)
{
// Si jamais on rez l'objet, on affecte à proprio
// la key du propriétaire de l'objet
proprio = llGetOwner();
}


touch_start(integer total_number)
{
if ( proprio == llDetectedKey(0))
{
llSay(PUBLIC_CHANNEL, "Bonjour mon Maître !!);
} else {
llSay(PUBLIC_CHANNEL, "Vous n'êtes pas mon Maître !!);
}
}
}



Encore une petite chose.
Si on donne notre objet à quelqu'un, le propriétaire change. Notre objet doit donc obéir à son nouveau propriétaire. Toutefois, la valeur retournée par llGetOwner n'est pas remise à jour immédiatement, ni automatiquement. D'où la présence de l'évenement on_rez, qui est appelé lorsque l'on pose l'objet au sol, ce qu'on appelle faire un rez de l'objet. C'est en général la technique la plus souvent utilisée.
Mais imaginons un objet que l'on porte sur soi.... On ne vas pas tout le temps le poser au sol, cela est casse pied. Pour l'utilisateur final qui se moque bien de savoir comment ca marche, ce n'est pas très pratique...

Alors ??

Et bien, on va utliser le bout de code suivant. Il suffit de l'ajouter dans le script et le tour est joué. L'evenement est appelé chaque fois que quelque chose change dans l'objet. Ici on ne s'interesse qu'au changement de propriétaire. Nous verrons cet evenement plus en detail plus tard.


changed(integer change)
{
if (change & CHANGED_OWNER)
llResetScript();
}



La fonction llResetScript permet de réinitialiser un script.
Dès que l'objet change de proprietaire, l'evenement changed est appelé, et fait un reset du script.

Il faut savoir que lorsque l'on utilise un objet, les variables qui le contiennent varient en fonction de ce que leur demande le script. Si je range l'objet dans mon inventaire, l'etat de ces variables est sauvegardé. Et lorsque je le sors, je le retrouve avec ces variables.
Ce qui signifie que si il y a une erreur de code dans mon script qui fait "planter" l'objet, le fait de le remettre dans mon inventaire et de le ressortir ne sert à rien. Si c'est un petit plantage de sl, là, cela fonctionne. Parfois, mon katana plante et ne fonctionne plus. Le seul moyen pour moi pour le refaire fonctionner est de le réinitialiser entièrement, en posant au sol la boite que j'ai achetée, qui me rend un katana tout neuf, avec des variables réinitialisées.




III. Conclusion


Voila pour aujourd'hui. La prochaine fois, nous étudierons en detail quelques fonctions très utiles concernant le type STRING, ainsi que le type LIST, si j'ai du courage !!

30 septembre 2008

Mon petit dojo (3)

Après avoir eu une vision extérieure, passons à une vision plus intérieure... Sans vouloir faire de jeu de mots
Il se compose de deux étages :

  • une coursive fait le tour de la zone de pratique du sabre qui se trouve en bas... Elle donne accès à trois pièces : 


    • le balcon, qui permet d'accéder au toit

    • une salle de méditation aux parois de verre semi transparent

    • une salle (à aménager selon ses gouts)


  • la pièce du bas  très haute, permettant ainsi des jumps sans se cogner la tete. Un petit tp permet de remonter.







Quelques vues de la salle de méditation... qui a été aménagée






Pour finir deux vues générales, avant de refermer le dossier "Dojo".... Et de pouvoir y méditer ou s'y entrainer à la pratique du Sabre suivant les règles du respects et de l'honneur...







Bonne semaine à tous....

26 septembre 2008

Mon petit dojo (2)

Continuons la présentation des quelques photos de mon "petit dojo"....
Pour faire suite au post d'hier.
Donc après avoir vu quelques vues générales du dojo flottant.. voici des vues de près.




Promenons-nous sur le toit...
Certaines branches de l'arbre dépassent, permettant ainsi aux bulbes luminescents d'éclairer le petit jardin...
On peut aussi y voir mes colonnes d'entrainement... Sur SL, il y a par dessus les valeurs de "hit" et de "colision". Mais quand je prends une photo, je supprime tous ces trucs la... alors ben forcément, on ne voit rien.. ^_^



Tournons légèrement la tête... et voila, le petit abri surmonter du très beau dragon de chez Grendels ! Le soleil se couche, on irait bien s'alonger sur les coussins, pour profiter du beau temps ( c'est vrai que sur SL, il fait toujours beau !!)




Retournons nous...un petit coin dans le fond, avec une arche végétale....



Et voila le toit dans sa totalité. Il manque juste le petit bout avec l'escalier pour descendre.


Il y a pas a dire, mais SL est formidable pour créer et bâtir des univers oniriques... On crée, on anime, et ensuite, on peut se promener et utiliser ses créations....




25 septembre 2008

Mon petit dojo

Pour changer une peu des cours de scripts, et revenir un peu au Zen, je vous presente ici mon dernier travail de build.
Il s'agit d'un "petit" dojo, avec tout le confort et les installations nécessaire à ce qu'il soit opérationnel.

Dojo... lieu d'éveil.... tu te dois d'être beau et adapté pour cela.


Et quand je dis petit...
  • base : 60m x 60m
  • hauteur : 30m
  • 110 prims...


Il est composé de deux parties :
  • le dojo en tant que tel
  • l'arbre-fondation

Je tiens ici à remercier une des plus exceptionnelles buidleuses de SL : Oumani. Je lui dois les sculpties qui ont servi a réaliser l'arbre, mais surtout, une grande partie de ce que je sais en build.
Elle m'a donné les bases, et quelles bases !!! Elle m'a aussi transmis un peu de sa touche "onirique", qui rend ses créations si belles...
La première fois que Ouma m'a montré ce qu'elle faisait, je suis devenue muette devant tant de beauté, et ceux qui me connaissent savent que, me faire taire, est un grand exploit !!
Ce jour la, j'ai émis l'idée, l'envie, d'etre capable un jour d'en faire autant. Le chemin est encore long pour cela, mais j'ai commencé à y faire mes premiers pas.
Et Ouma m'y encourage....


Je remercie aussi Nounours, qui a poursuivi ma formation et qui m'a appris à économiser les prims ^_^ .
De plus, elle m'a permis de m'exprimer en créativité sur une sim merveilleuse mais destinée à mourir dans peu de temps.... Savoir que tant de beauté va mourir, cela me blesse profondément, mais ce qui a été réalisé là, fait partie de ce que j'ai vu de plus beau sur SL, et fait aussi.
Merci a toi de m'avoir permis de faire mes premiers builds... quand toute timide encore, sachant rezzer avec fierté un beau cube en bois, tu m'as encouragé à en faire une jolie table de pierre. Ta confiance m'honore.
Merci aussi de m'avoir inculqué a coup de masse dans ma tete dure de bonnes praqtiques de scripting. Et de m'avoir guidé lors de mes premiers pas en ce domaine.

Et j'attends avec une impatience non dissimulée, le jour où, avec toi et Ouma, toutes les trois, il nous sera donné à nouveau la chance de builder dans sa totalité une sim... Pour que naisse à nouveau la beauté et le rêve.


Je tiens à remercier aussi ma Yoyo.... qui sait voir tous les petits détails, qui font d'un objet tout simple, une chose belle, car complète.
Toi qui passe après moi, et qui me montre tout ce qui n'est pas ajusté au mm, les textures décalées de presque rien, qui me dit que ca, c'est pas assez détaillé, donc ca fait pas vrai....
Sans toi, je ferais encore du build approximatif.... Avec des murs aux textures où on se dit que.... on voit le motif qui se répète là, là et là encore... et que ca fait pinpin en somme....
Quand tu me dis que maintenant ca va... je sais que ca doit etre vachement bien ^_^.
Que ma cabane de planches pourries est si réaliste que, ca donne envie d'y vivre. hihi !!



Mais revenons a nos moutons....
Le dojo se compose de plusieurs partie :
  • un balcon pour y regarder le jour se lever, prendre l'air, et monter sur le "toit"
  • une salle de méditation, avec vue sur le ciel
  • une salle à aménager à sa convenance
  • un zone de pratique du sabre assez grande et surtout haute, permettant les jumps sans se cogner au plafond, ou perdre de vue son adversaire, par ce que la caméra en mouselock est collée au plafond
  • le toit : avec un lieu de détente habité pourvue de coussins moelleux, etune grande surface libre ou on pourra poser divers instruments d'entrainement.

A noter que ce dojo n'est pas poser sur le sol mais flotte dans les airs.. En effet, il est bâtît dans les branches d'un arbre étrange et merveilleux, dont les bulbes luminescents diffusent une douce lumière la nuit.

Si vous êtes intéressé par ce dojo, contactez moi, il est à vendre !! pour la somme de 2000 L$. Il est livré avec un appareil d'entrainement : 5 colonnes alignées entre lesquels on peut apprendre à se déplacer et bien plus encore, les colonnes indiquant les nombres de frappes et de collisions. Les coussins pour se relaxer ou méditer sont offerts sur simple demande :)).....


Les grands discours sont ennuyeux, passons un peu a quelques images...



Vue générale du dojo, avec "l'arbre"



La base de l'arbre... et ses bulbes lumineux



Vue du haut... on peut y voir le jardin, les piliers pour s'entrainer et une beau dragon sur le toit....
Le dragon n'est pas de moi, j'attends le jour où je saurai faire avec la aussi une grande impatience.
Le dragon est de chez Grendels, sur la sim Avaria.... Un conseil : courrez la-bas visiter la sim et dépenser vos L$ à la boutique. Votre porte monnaie ne fondra pas et vous rentrerez chez vous avec de si belles choses !!

D'autres photos encore demain....

18 septembre 2008

Cours d'initiation au langage LSL (III suite)

après la structure de contrôle IF la dernière fois, continuons ce cours d'initiation au LSL.
Aujourd'hui : les structures de contrôle (suite et fin) et les fonctions.



I. Les structures de controle


1) La boucle WHILE


Cette structure permet de répéter une série d'instructions tant qu'une condition n'est remplie.

while(condition){
instruction1;
instruction2;
....
}


Exemple : Parcourons les entiers de 1 à 10, et indiquons s'ils sont pairs ou impairs.


integer n = 1;
// on commence à 1.
// on définit la variable à parcourir
// et on l'initialise de suite

while ( n <= 10 ){ if (n%2 == 0){
llSay(0, "le nombre "+ n + " est pair");
}else {
llSay(0, "le nombre " + n + " est impair");
}
n++;
}

Voyons dans le détail ce que fait ce bout de code. On définit tout d'abord une variable n = 1, que l'on va parcourir.
Entrée dans la boucle : on teste n. Est-il plus petit que 10 ? Oui. On traite les instructions.
On calcule n%2 qui vaut 1..... donc on exécute le code du ELSE : on écrit : le nombre 1 est impair.
Ensuite, on incrémente n de 1....Fin de la boucle.

On recommence : test ( n <= 10 ?) ...oui => n%2 = 0 donc on ecrit : le nombre 2 est pair. Incrément de n... n vaut 3.
Et on recommence.... jusque n = 11.
Lors du test, la condition n <= 10 est fausse. On n'exécute donc pas le code qui suit, et on sort de la boucle. L'instruction n++ est extrêmement importante. Imaginons qu'on l'oublie. Le nombre n ne varie pas. Le test de la condition est toujours vrai, donc le code s'exécute sans fin, puisque a chaque nouvelle boucle, on teste le meme nombre. Nous sommes dans le cas d'une boucle infinie. Le programme boucle sans fin. Notons aussi que avant de démarrer une boucle, le programme effectue un test. Donc que si on parcourt les entiers de 1 à 100, 100 tests sont effectués. Dans le cas où on ne souhaite faire que une boucle un certain nombre de fois bien défini, il existe une autre structure de controle plus adéquate. Ici, aucun risque de boucle infinie, puis qu'il n'est pas nécessaire d'incrémenter une variable. Passons tout de suite à cette structure :

2) La boucle FOR

Sa structure est la suivante :

for(nom de la variable et affectation; condition de fin; incrément){
instruction1;
instruction2;
....
}

  • Nom de la variable et incrément : on précise la variable et sa valeur initiale

  • Condition : la contrainte qui est excercée sur cette variable. Dans le même genre que celle du WHILE.

  • Incrément : le pas d'incrémentation de la variable.

Reprenons l'exemple précédent avec une boucle FOR :

integer n;// l'initialisation n'est pas nécessaire

for( n = 1 ; n <= 10 ; n++){ if (n%2 == 0){
llSay(0, "le nombre "+ n + " est pair");
}else {
llSay(0, "le nombre " + n + " est impair");
}
}


Notons aussi que l'incrément n'est pas obligatoirement 1... Par exemple, comptons de 2 en 2 jusque 10 :

integer n;
for( n = 0 ; n <= 10; n+=2){ llSay(0; n);
}

on peut aussi bien sur compter à l'envers.... de 10 à 0 par saut de 2....

integer n;
for( n = 10 ; n >= 0 ; n-=2){
llSay(0; n);
}



3) Encore ??


Il existe encore deux autres structures.... le DO...WHILE et le JUMP. Mais je n'en parlerai pas. Pourquoi ?

Le DO ... WHILE fonctionne de la même manière que le WHILE, sauf que le test s'effectue à la fin et non pas au début. Ainsi, quel que soit le résultat du test, la série d'instructions de la boucle est effectuée au moins une fois. Cette structure, présente dans beaucoup de langage, est finalement très peur utilisée.

Le JUMP permet de "sauter", de faire un saut directement à une autre partie du programme. Même si cela peut parfois s'avérer pratique, cette instruction n'est utilisée que par ceux qui n'y connaissent rien à l'algorithmique, et programment comme des gougnafiers !!
Le code est alors illisible, et, même si il parait plus facile de faire des jumps plutot que de coder proprement, dans le maintient et la correction d'un code, l'utilisation du jump rend le code ingérable. Donc à fuir absolument !!



II. Les fonctions


Elles sont de deux sortes : celles qui font partie de l'API du LSL, et que l'on peut utiliser directement, et celles que l'on écrit soi même avec ses petites mains.


1) Les fonctions LSL

Elles se reconnaissent car elles commencent toujours par ll. Par exemple la fonction llSay deja rencontrée. Je détaillerai dans le prochain cours les fonctions LSL indispensables à connaitre.


2) Les fonctions que je code avec mes mimines à moi

Pour pouvoir utiliser ces fonctions, il faut tout d'abord les définir. Une fonction est une portion de code, qui effectue une serie d'instructions. Elle peut prendre des paramètres en entrée ou non. Elle peut retourner un résultat ou non.
Certains langages (mais pas le LSL) distinguent les deux, et appellent fonction quand il y a retour d'un résultat, et procédure quand il n'y en a pas.
Une fonction contenant une suite d'instructions, celles-ci peuvent etre d'autres fonctions, ou des fonctions LSL.

Notons tout de suite que la déclaration des fonctions se fait au début du script, généralement apres la declaration des variables globales.
D'où le squelette d'un script LSL (squelette définitif) :


//définition des variables globales


// Définition des fonctions personnelles


// un état du script : etat1

state etat1 {

evenement1(){
fonction1();
fonction2();
}

evenement2(){
fonction3();
fonction4();
}

}


Voyons tout d'abord une fonction sans paramètre; son squelette est le suivant :


ma_fonction(){
instruction1;
instruction2;
....
}


Par exemple, une fonction qui ecrit : "bonjour à tous !!" dans le chat....

dire_bonjour(){
llSay(0, "Bonjour à tous !!");
}


Lorsqu'on souhaite appeler cette fonction, il suffit d'écrire :

dire_bonjour();

Et c'est tout !!


Ensuite, une fonction avec paramètre. Son squelette est le suivant :


ma_fonction( type1 variable1, type2 variable2, ...){
instruction1;
instruction2;
....
}


Par exemple :

dire_bonjour2(string nom){
llSay(0, "Bonjour " + nom + " !! Comment vas tu ?");
}


Lorsqu'on souhaite appeler cette fonction, il suffit d'écrire :

dire_bonjour2("Nounours");

Ce qui affiche : Bonjour Nounours !! Comment vas tu ?
(C'est vrai ça... Bonjour Nounours !! Tu vas bien ?? Oui j'espère !! )


Ou encore :

parite(integer i){
if (i%2 == 0){
llSay(0, "le nombre "+ i + " est pair");
}else {
llSay(0, "le nombre " + i + " est impair");
}
}

que l'on appellera de cette manière :

parite(25);


Une petite dernière avec plusieurs paramètres :

compare(integer i, integer j){
if ( i <=j ){ llSay(0, i + " est plus petit ou égal que "+ j);
} else {
llSay(0, i + " est plus grand que "+ j);
}
}


Et pour finir une fonction qui retourne une valeur, quelle est ou non des paramètres. Son squelette :


type ma_fonction(type1 variable1, type2 variable2, ...){
instruction1;
instruction2;
....
return valeur;
}


Prenons un exemple : une fonction qui fait la somme de deux entiers. Bon je sais, on peut le faire avec un simple +, mais enfin !! C'est qui le prof ici ?? Grrrrrr......

integer ajoute(integer i, integer j){
return i+j;
}


On utilise cette fonction de la manière suivante :

integer h;
h= ajoute(10, 25);



III. Un peu de pratique


Dans son cours sur l'importance d'un bon algorithme, Nounours nous donne deux fonctions, écrites en langage algorithmique. Essayons de les ecrire en LSL. Je rappelle que l'une de ses fonctions est optimisée alors que l'autre non


1) La fonction non optimisée :

Je rappelle l'algorithme :

somme_entier_naturels_version1 ( n )
{
// Cette fonction va sommer tous les entiers de 1 à n (n étant un paramètre)
// On crée une variable résultat qui aura pour nom "somme"
somme = 0
POUR compteur DE 1 A n FAIRE
// On crée une variable compteur qu iprendra successivement
// les valeur entieres de 1 à n

// On fait la somme des entiers de 1 à n et on le stocke dans "somme"
somme = somme + compteur;

// Fin de la boucle "POUR", donc le programme restera dans cette boucle
// tant que compteur sera compris entre 1 et n.
// L'incrémentation de compteur se fait automatiqueemnt
// à la ligne "POUR compteur..."
FIN_POUR

// On retourne le résultat "somme" qui contient, grace à la boucle "POUR",
// la sommation des entiers de 1 à n
RETOURNER somme
}


Cela nous donnera en LSL :

integer somme_entier_version1(integer n){
integer somme = 0;
integer compteur;

for (compteur = 1; compteur <= n; compteur++){ somme = somme + n ;
// ou encore : somme += n;
}

return somme;
}



2) La fonction optimisée

Je rappelle l'algorithme :


somme_entier_naturels_version2 ( n )
{
// Cette fonction va sommer tous les entiers de 1 à n (n étant un paramètre)
// On sait que la somme des entiers naturels de 1 à n est une formule mathématique!
// http://fr.wikipedia.org/wiki/Somme_(arithm%C3%A9tique)
// somme = n * (n + 1) / 2
// Pourquoi ne pas réutiliser ce résultat?
// On crée une variable somme, et on lui met la formule!

somme = n * (n + 1) / 2

// Et maintenant, on retourne le résultat!
RETOURNER somme
}


Cela nous donnera en LSL :

integer somme_entier_version2(integer n){
integer somme;
somme = n * ( n + 1) / 2;
return somme;
}

On pourrait aller encore plus vite en écrivant directement :

return n * ( n + 1) / 2;




IV. Conclusion

Voilà, c'est tout pour aujourd'hui. Vous pouvez éteindre votre cerveau. La prochaine fois, je parlerai de certaines fonctions LSL, dont celles en rapport avec le type string, qui je le rappelle, ne concerne en rien le vêtement coupe-crotte très à la mode.
Ce prochain cours sera l'avant dernier dernier......

Non, pas d'inquiétude, je ne vais pas arrêter !! Mais puisqu'il s'agit d'un cour d'initiation au LSL, nous en avons fini. Nous passerons à des cours tout courts !!
Avec de vrais bouts de code utiles dedans.

11 septembre 2008

Cours d'initiation au langage LSL (III)

Plusieurs petites choses avant de commencer....

Lorsque je scripte, je ne le fais pas sur sl... j'utilise un logiciel, y écris mon code, puis hop, copier/coller dans sl.

Pourquoi ? Car le petit logiciel que j'utilise est très pratique, il y a autocomplétion du code, possibilité d'avoir accès à la page de wiki LSL de la fonction utilisée, et surtout, il y a un mode débug où je peux voir mon code tourner... une sorte de simulateur qui exécute mon code. Ultra pratique !!

Le logiciel : LSLEditor : http://www.lsleditor.org/

Dommage qu'il ne tourne que sous Zindowz (heurkkkk !! Linux power !!) et nécessite .NET Framework.
(note : je m'excuse auprès de mes lecteurs d'avoir écrit autant de gros mots en seulement une ligne)

Sinon, j'avais oublié de vous donner la page du wiki officiel de LSL : plein de docs sur toutes les fonctions, des exemples de codes, plein de bonnes choses : http://wiki.secondlife.com/wiki/LSL_Portal

Sur la page, en haut, la possibilité de choisir Français comme langage....

Bien passons aux choses sérieuses : le programme du jour.
Alors aujourd'hui : Les opérateurs, les structures de contrôles, affectations des variables



I. Les opérateurs mathématiques et l'affectation de variables numériques


Les 4 opérations mathématiques standards : + - * /
Il faut leur ajouter l'opération modulo %, qui donne le reste de la division entière.
En cas de calcul long, les priorités opératoires mathématiques sont appliquées. Si on veut les changer, l'emploie de parenthèses est indispensables.

Ainsi, on peut trouver :

integer i;
integer j;
i = 45;
j = 5;
i = i + j ;// maintenant i vaut 50 (= 45 + 5)
integer k = i/j;// k vaut 45/5 = 9
integer h = i % 10;// h est le reste de la division
// de 45 par 10.... donc h = 5
intger g = i - k*j;


Attention toutefois..... Les différents types de variables ne sont pas forcément compatibles..... Il faut dans ce cas les convertir (cast) l'un vers l'autre. Certains cast sont implicites, d'autres ne le sont pas du tout.

Exemple :

integer i = 50;
float j = i;// j vaut 50.0, 50 en valeur flottante, la conversion est implicite
// car il n'y a pas de perte de données
j = 50.36;
i = (integer)j;//conversion explicite car i vaut 50, partie entière de j.
// Ici, on perd de l'information. Donc il faut
// expliciter le cast.


Un dernier exemple piège dans lequel on tombe souvent :

float i = 2/3;


Si on demande la valeur de i... Qu'obtient-on ? On s'attend à 0.6666. Mais en fait i vaut 0 !!
Explication, je sens le mal de tête monter en vous....
2 et 3 sont des entiers.... on fait le calcul 2/3, avec des entiers, ce qui implique la division entiere qui vaut l'entier 0. Cet entier est ensuite casté en float implicitement et affecté a i. Donc i = 0.0 !!

Comment faire alors ?? Simplement écrire :

float i = 2.0/3.0;


2.0 et 3.0 sont des floats. La division est une division de floats, son résultat est donc le float 0.66666.
On affecte alors ce résultat à la variable i. CQFD !!


Oui M'dame !! C'est bien beau, mais si j'ai pas 2 et 3, mais deux variables entières k et l valant 2 et 3, et que je veux les diviser et avoir un résultat décimal ?? Je fais comment moi ? j'écris quand même pas k.0 et l.0 ??

Bien sur que non !!! M'enfin !!!
Dans ce cas, on cast explicitement : on caste chaque variables AVANT de faire la division !! Ainsi :

integer k = 2;
integer l = 3;
float i = (float)k / (float)l;


Donnons encore l'opérateur d'incrémentation et de décrémentation :

integer i = 5;
i++;// i++ équivaut à i = i+1; i vaut 6;
i--;// equivaut à i=i-1; i vaut 5


De même, pour éviter certaines formules trop longues :

integer i = 20;
i += 5;// équivalent à i = i+5;
i -= 5;// équivalent à i = i-5;
i *= 5;// équivalent à i = i*5;
i /= 5;// équivalent à i = i/5;
i %= 5;// équivalent à i = i%5;


Juste un dernier mot sur l'emploi du modulo %. A quoi peut-il servir ??

Par exemple pour convertir des secondes, en minutes et secondes :

integer t = 78;
integer min = 78/60;// 1 min
integer sec = 78%60;// et 18 sec

Ou encore pour connaitre la parité d'un nombre. Si un nombre i est pair, i%2 vaut 0, s'il est impair, i%2 vaut 1.
Car, tout nombre pair peut s'écrire sous la forme i = 2*n, et tout nombre impair sous la forme i = 2*n + 1. (pour n entier cela va de soi)




II. Les structures de controle


Note : j'utilise ici la fonction llSay. Elle permet à l'objet de parler sur un canal. Le canal 0 correspond au chat.



1) les conditions : IF.... [ELSE]

Cette structure permet de tester une condition, et d'exécuter des instructions si elle est vraie. Voir aussi d'en exécuter d'autres si elle est fausse.

if(la_condition){
instruction1;
instruction2;
} else {
instruction3;
instruction4;
}

La partie ELSE est facultative....

Je veux bien M'dame.... mais c'est quoi cette condition ?
Vous le faites exprès ou quoi ? La condition est ce que l'on teste.... Deux opérandes que l'on compare avec une opération de comparaison.

Par exemple :


i == 4 vrai si i vaut 4. Noter bien les deux ==
i > 4
i <>

Une opération de comparaison, ou plusieurs, avec des opérateurs logiques :
&& => ET, || => OU, ! => NOT


par exemple, je veux tester si i est compris entre 5 et 10....
Cela signifie que i est plus petit ou égal à 10 ET plus grand ou egal à 5. On écrira donc :

(i >= 5) && (i <= 10)


Par exemple :

if ((i >= 5) && (i <= 10)){ llSay(0, " Le nombre est compris entre 5 et 10");
}

ou encore

if ( i%2 ){
llSay(0, "Le nombre est pair");
}else {
llSay(0, "Le nombre est impair");
}


C'est tout pour aujourd'hui, vous pouvez aller prendre l'air, il fait si beau dehors ! Respirez à plein poumon.

10 septembre 2008

LSL avancé : la boite dialogue bleue

Comme promis, voici une petit tutoriel sur la petite boite de dialogue bleue qui apparait dans le coin supérieur droit de l'écran.
Cette fenêtre de dialogue est équipée de boutons permettant ainsi une interaction.

Il y a donc dans cette fenêtre 3 parties :
  • le message, le texte de la boite de dialogue, présentant les boutons, donnant une information, ....
  • des boutons (12 au maximum)
  • un bouton "ignorer", permettant de fermer la fenêtre sans rien faire

La fonction qui va permettre la création de cette boite est la fonction llDialog :

llDialog( key avatar, string message, list buttons, integer chat_channel );

  • avatar : le UUID de l'avatar qui verra apparaitre la boite de dialogue, sa clé
  • message : le message de la boite de dialogue
  • buttons : le texte des boutons, présenté sous la forme d'une liste
  • chat_channel : le canal où se fera la trasnmition de l'information

Mais pourquoi donc parler de cette boite de dialogue juste après avoir parlé du listen ??

Ben, justement... La fonction llDialog fait parler notre avatar, c'est là son mode de fonctionnement. Lorsque l'on clique sur un bouton, notre avatar parle sur le canal choisi et dit le texte du bouton. Ainsi, il faut pour traiter l'information qu'il y ait derrière quelqu'un a l'écoute !! Donc un événement listen.

Donc llDialog s'utilise conjointement avec llListen / Listen, et aussi llListenRemove, car il n'est pas nécessaire de garder l'écoute en fonction une fois le message transmis.

Nous avons donc les étapes suivantes :

  1. création de l'écoute : llListen
  2. ouverture de la boite de dialogue : llDialog
  3. réception et traitement du message : evenement listen
  4. suppression de l'écoute


Concernant les boutons....

Le texte des boutons doit être passé à llDialog sous la forme d'une liste. L'ordre des éléments de cette liste a son importance : elle détermine l'ordre des boutons.
Si deux boutons ont le même nom, le message transmis sera le même, donc notre script ne pourra pas savoir quel bouton a transmis ledit message. Donc attention à choisir des textes tous différents pour les boutons.

L'ordre des boutons par rapport aux éléments de la liste est le suivant :

9 - 10 - 11
6 - 7 - 8
3 - 4 - 5
0 - 1 - 2


Premier exemple : un menu simple

Nous allons travailler sur un objet tout simple : un cube. Lorsqu'on clique sur lui, il nous propose de lui changer sa couleur. Nous supposerons que nous avons une fonction qui fera ce changement de couleur.



key avatar; //l'avatar qui clique sur l'objet
integer ecoute;
integer canal = 123456;

list menu = ["rouge", "noir", "blanc"];
string message_menu = "Choisissez ma nouvelle couleur";



changer_couleur(string couleur){
//a ecrire, on change la couleur des faces du cube
}



default
{

touch_start(integer total_number)
{
avatar = llDetectedKey(0);
ecoute = llListen(canal, "", avatar, "");
llDialog(avatar, message_menu, menu, canal);
}


listen(integer channel, string name, key id, string message)
{
if ( message == "rouge" ) changer_couleur("rouge");
else if (message == "noir") changer_couleur("noir");
else if (message == "blanc") changer_couleur("blanc");
llListenRemove(ecoute);
}

}


Lorsqu'on touche l'objet, l'évènement touch_start est déclenché. on récupère la clé de l'avatar, on ouvre une écoute, en précisant cette clé : ainsi, seule celui qui a cliqué sera écouté.
On fait ensuite appel a la la fonction llDialog. On lui passe comme paramètre la clé de l'avatar, de manière a ce que ce soit lui qui voit s'ouvrir la boite de dialogue, le texte de la boite et la liste des boutons, ainsi que le canal.

Puis, quand un bouton a été choisi, le script intercepte le message grace à son ecoute : on traite l'évènement listen. Le message envoyé est le texte du bouton. On fait donc un teste sur les valeurs possibles avec une série de IF.
Si la valeur est correcte : on change la couleur.....

Puis, le travail ayant été fait, on supprime l'écoute.


Second exemple : menu et sous menus



Nous allons complexifier l'exemple précédent : nous avons cette fois beaucoup de couleurs, et nous voulons les trier par famille. Famille des rouges, des bleus, des verts.
Il nous faut donc 4 menus :
  • menu_principal : permet de choisir la couleur
  • menu_rouge : choix de la couleur ou retour au menu principal
  • menu_bleu et menu_vert, sur le meme principe que menu_rouge
Modifions le programme précédent :


key avatar; //l'avatar qui clique sur l'objet
integer ecoute;
integer canal = 123456;

list menu_principal = ["rouge", "bleu", "vert"];
string message_menu = "Choisissez ma nouvelle couleur";

list menu_rouge = ["vif", "magenta", "<<<", "sang"]; string menu_rouge = "Choisissez votre nuance de rouge : "; list menu_bleu = ["marine", "royal", "<<<", "cyan"]; string menu_bleu = "Choisissez votre nuance de bleu : "; list menu_vert = ["clair", "pomme", "<<<", "emeraude"]; string menu_vert = "Choisissez votre nuance de vert : "; changer_couleur(string couleur){ //a ecrire, on change la couleur des faces du cube } default { touch_start(integer total_number)
{
avatar = llDetectedKey(0);
ecoute = llListen(canal, "", avatar, "");
llDialog(avatar, message_menu, menu, canal);
}


listen(integer channel, string name, key id, string message)
{
//on traite le menu principal
if (message == "rouge") llDialog(avatar, message_rouge, menu_rouge, canal);
else if (message == "vert") llDialog(avatar, message_vert, menu_vert, canal);
else if (message == "bleu") llDialog(avatar, message_bleu, menu_bleu, canal);

//on traite pour le sous menu, le bouton <<< else if (message == "<<<") llDialog(avatar, message_menu, menu, canal); // les couleurs....
else if (message == "vif") {
changer_couleur("vif");
llListenRemove(ecoute);
}
else if (message == "magenta") {
changer_couleur("magenta");
llListenRemove(ecoute);
}
// on traite ainsi toutes les autres couleurs
// pour la derniere, on ecrit seulement else
// au lieu de else if
}

}



Ce script fonctionne comme le précédent : on capture la cle de celui qui a cliqué, on ouvre une écoute et on affiche la boite de dialogue, celle du menu principal.

Ensuite, en fonction de ce qui est cliqué, on agit de manières différentes :
  • si on est sur le menu principal, on affiche à nouveau la boite de dialogue, mais avec le menu couleur choisi
  • si on est sur un menu couleur et que le bouton de retour est pressé : on affiche à nouveau la boite de dialogue du menu principal
  • sinon : on est sur une couleur, donc on effectue la tache demandée

Un dernier conseil pour finir : dans certains cas, on a plusieurs copies du meme objet l'une près de l'autre. Et il ne faudrait pas que tous ces objets se parlent sur le meme canal : dans certains cas, cela peut provoquer des interractions. Imaginer : vous commandez l'allumage d'une lampe, et toutes les lampes s'allument !!

Pour cela ajouter ceci au début de l'état default :

state_entry()
{
canal = (integer)llFrand(-100000) - 10000;
}

Cela positionne le canal d'écoute sur une valeur aléatoire grande et négative. La plage de valeurs étant importante, la probabilité que deux objets obtiennent le même canal est quasi nulle.



Voila de quoi agrémenter vos objets de zoulies boites de dialogue, pour encore plus d'interactivité.
Si vous avez des questions, ou envie de voir ici d'autres tutoriels, n'hésitez pas. Je ferais de mon mieux.

Initiation au langage LSL (II)

Bien après avoir vu les bases du LSL, allons un peu plus loin dans notre connaissance du langage.
Parlons un peu des différentes variables utilisables dans SL.


Notons tout d'abord que pour utiliser une variable, il faut l'avoir définie auparavant. Donc, on déclare une variable, on lui donne une valeur, et ensuite on l'utilise. Une variable se définit par deux paramètres : son type, qui définit quel type de données elle contiendra, et sa portée, qui définit à quel endroit du script elle pourra être utilisée.


I. Portée et définition d'une variable


Variables globales : elles sont accessibles à tout moment à l'intérieur du script. Leur portée est donc le script lui même.

Variables locales : elles sont définies à l'intérieur d'un bloc de code (délimité par des accolades). Elles ne peuvent donc être utilisées que dans ce bloc. Elles n'ont aucune existence en dehors de ce bloc. Si une variable locale a le même nom qu'une variable globale (c'est possible même si c'est un très mauvaise idée !! A éviter absolument), c'est la variable locale qui sera utilisée.

Variables paramètres : ces variables servent à définir les paramètres d'une fonction. Nous verrons cela en détail lorsque nous parlerons des fonction.

Les variables globales doivent être définies en début de script, avant même l'état default. Les variables locales sont définies dans le bloc de code où elles nous sont utiles, lorsque l'on en a besoin.

Pour définir une variable, il faut écrire son type suivit de son nom, et enfin un point virgule pour terminer l'instruction.
Par exemple :

integer un_entier;
string message;


Il est possible de donner une valeur à une variable au moment où on la définit. Ainsi, elle est initialisée dès sa création. Si on ne donne pas de valeur lors de la définition de la variable, le LSL lui donne une valeur par défaut : 0, pour les entiers, "" la chaine vide pour les chaines de caractères.

integer un_entier = 5;
string message = "Kikou !!!!";

En ce qui concerne le choix du nom de la variable, plusieurs points :
  • donner un nom qui a du sens, éviter les noms comme : truc, machin, bidule, toto, foo, bar, deirdre, ....

  • pas de caractères spéciaux dans le nom, ni d'espace : utiliser des lettres, des chiffres, ainsi que le underscore : _


II. Les différents Types du LSL


Le LSL est un langage fortement typé. C'est à dire que toute variable, pour pouvoir être utilisée, doit avoir été définie. Cette définition précise son type, c'est à dire le contenu qu'elle pourra recevoir.
On sera parfois obligé de convertir un type en un autre. Le transtipage, ou cast, est l'opération permettant de faire cela. Tout cela sera vu par la suite.

Voici les différents types de données pour le LSL :
  • integer: nombre entier, signé, codé sur 32 bits. Ex : 0, 6, -56
  • float: nombre flottant signé, un décimal en gros. Il doit s'écrire obligatoirement avec le point décimal et supporte la notation exponentielle. Ex : 0.0, 1.698, -6.25, 6.0E6, -6.3E-4
Notons au passage que si le float vaut 0, on doit écrire celui-ci 0.0, ou encore .0
  • string : chaine de caractères. Elle doit être délimitée par des guillemets. Ex : "Kikou !!"
Si la chaine doit contenir des guillemets, on les précède de \. Ex : "J'adore ce genre de \"bidule\". "
  • key : la key représente une chaine de caractères de format spécifique qui correspond à l'UUID, identifiant unique de tout objet sur SL.
  • vector : 3 floats réunis pour définir un vecteur. On encadre par <>. Ex : <0.0,0.0,0.0> pour le vecteur nul.
  • rotation : 4 floats réunis pour interpréter une rotation. Fonctionne comme le vector. Ex : <1.0,0.3,2.3,.2>
  • list : liste d'éléments hétéroclites. On encadre par [ et ]. Ex : [ 0, -52.6, "kikouu !!"]


III. Conclusion


Pour conclure, je vous conseille ardemment de commenter votre code, c'est à dire de laisser quelques petites explications renseignant sur ce qu'un bout de code fait, à quoi sert cette fonction, pour quoi il pleut encore aujourd'hui, Henri IV savait-il seulement faire du cheval...

Le texte écrit en commentaire ne sera pas interprété par le script. Pour commenter une ligne de texte, il suffit d'écrire // avant le texte à mettre en commentaire. Tout ce qui suit // sur la même ligne est ignoré par le script.
Exemple :

// Ouiiii !!! Ceci est votre premier commentaire !! bravo !!


Pour résumer, voici le squelette général d'un script, modifié par ce que nous venons de voir :

//Début du script - Définition des variables globales
string ma_chaine_globale; // commentaire sur ce que va contenir cette variable
integer un_entier_global; // commentaire sur ce que va contenir cette variable


// Définition des fonctions
// on verra ca plus tard....



// un état du script : etat1

state etat1 {

evenement1(){
fonction1();
fonction2();
}

evenement2(){
fonction3();
fonction4();
}

}


9 septembre 2008

LSL avancé : l'évènement listen

Avant de poursuivre les tutoriels pour les débutants en LSL, voici à la demande d'une amie un petit tutoriel sur l'évènement listen.


Comme tous les évènements, le listen se découpe en deux parties :

  1. On met en place une écoute, c'est a dire que l'on demande à notre script d'écouter.
  2. La gestion de l'écoute : dès que le script a entendu quelque chose, il fait appelle a l'évènement listen qui contient les instructions à effectuer.


Lancer une écoute


Cela se fait à partir de la fonction llListen :

integer llListen( integer canal, string nom, key id, string msg );


canal(integer) : le canal à écouter. Si on veut écouter le chat, on utilisera la constante PUBLIC_CHANNEL. En général, pour permettre le dialogue entre plusieurs objets, on utilisera un autre canal que ce dernier, pour éviter la saturation du script.

nom(string) : le nom de l'avatar, ou de l'objet, que l'on souhaite écouter. A condition bien sur qu'il parle sur le bon canal. Si on veut que notre script écoute tout sur un canal précis, on mettra comme valeur : "", soit la chaine vide.

id(key) : l'UUID de l'avatar, ou de l'objet. Même principe que pour le nom, sauf que là, on travaille avec l'UUID. Pour écouter tout le monde : on utilisera la clé vide : NULL_KEY.

msg(string) : un message particulier. Cela permet à notre script de déclencher l'évènement que si un message précis a été entendu.

A noter que l'appel à la fonction llListen renvoie un nombre entier qui référence l'écoute. Nous verrons plus loin à quoi cela peut servir.

Pourquoi mettre autant de filtres ?
Plus les filtres sont précis, et moins le script aura de travail à faire, donc moins de ressources consommées, ce qui allège la charge sur la sim.


Traiter l'écoute

Cela se fait avec l'évènement listen :

listen( integer channel, string name, key id, string message ){
//les instructions à effectuer...
}


parmi les paramètres que reçoit l'évènement listen, on trouve :

  • channel : le canal sur lequel se fait l'écoute
  • name, id : le nom et la clé qui ont été envoyé par llListen
  • message : le texte qui a été écouté




Premier exemple d'application

Le projet est de faire un objet perroquet, qui va répéter tout ce que je dis. On va donc se construire un perroquet (un bête cube pour aller plus vite).
Cahier des charges :
  • quand on clique sur lui, il écoute ce que dit son propriétaire.
  • Il répète ce qui est dit
  • si on lui dit : zut, il s'arrête



integer ecoute;
integer canal = PUBLIC_CHANNEL;
key avatar;


default
{

touch_start(integer total_number)
{
avatar = llGetOwner();
ecoute = llListen(canal, "", avatar, "");
}

listen(integer channel, string name, key id, string message)
{
if (message != "zut") llSay(canal, message);
else {
llSay(canal, "Je me tais à present...");
llListenRemove(ecoute);
}
}

}


Détaillons un peu ce code...

Lorsque l'on touche notre perroquet, on le met en route.... Il commence par récupérer l'UUID de son propriétaire, puis ouvre un canal d'écoute. L'UUID de l'avatar sert de filtre : il ne va pas écouter tout ce qui se passe sur le chat, mais seulement ce que son propriétaire dira. La fonction llListen renvoie un entier que l'on affecte a la variable ecoute, et qui correspond au numéro de l'écoute. Cela permettra ensuite de pouvoir supprimer cette écoute en question lorsqu'elle ne sera plus utile.

A chaque fois que le propriétaire parle dans le chat, le script déclenche un évènement listen. Si ce qui est dit est "zut", le perroquet annonce qu'il se tait (à la bonne heure). Puis il annule l'écoute (llListenRemove) : cela met le script en sommeil. Il faudra a nouveau cliquer sur le perroquet, pour lancer un nouvel évènement touch_star, qui initiera une nouvelle écoute.



Autre exemple


Imaginons cette fois-ci que nous avons chez nous plusieurs lampes. Nous avons des objets lampes, et un autre objet qui permet d'allumer ou d'éteindre toutes les lampes en même temps.

Comment cela va-t-il fonctionner ?

Nos lampes vont toutes avoir le même script : ce dernier sera à l'écoute d'un canal précis. Si on parle sur ce canal, la lampe s'allume ou s'éteint.
Nous avons aussi un autre objet "interrupteur". Lorsque l'on clique dessus, il donne l'ordre au lampe de s'allumer ou de s'éteindre.
Il va parler sur le canal choisi, et donner son ordre. Les lampes seront à l'écoute.

Donc deux scripts : un pour chacune des lampes, et un pour l'objet.


Script de l'interrupteur :


integer canal = -1234567;

default
{

touch_start(integer total_number)
{
llSay(canal, "clic");
}

}


A chaque fois que l'on clique sur lui, l'interrupteur dit "clic" sur le canal choisi (ici négatif : un avatar ne peut parler sur un canal négatif, on utilisera donc les canaux négatifs pour les dialogues d'objets par exemple)



Script pour les lampes :




integer canal = -1234567;
integer ampoule;


//fonction qui commandera allumage/extinction de la lampe
commande_ampoule(integer amp) {
//si amp == 0 : on éteint
//si amp ==1 : on allume
// à écrire donc..... je vais pas tout faire non plus :)
}


default
{

state_entry()
{
ampoule = 0;
llListen(canal, "", NULL_KEY, "clic");
}


listen(integer channel, string name, key id, string message)
{
ampoule = (ampoule + 1)%2;
commande_ampoule(ampoule);
}

}



A l'initialisation du script (state_entry), on donne à ampoule la valeur 0 ( 0 pour éteint, et 1 pour allumer). La variable ampoule représente l'état de l'ampoule. Puis on ouvre une écoute. On ne récupère une référence surcette écoute, car puisque la lampe sera toujours en attente d'un ordre, l'écoute ne sera jamais supprimée.
Par contre, l'écoute ne réagira que au mot "clic". Et sera sourd a tout autre.

Quand le clic retentit, l'évènement listen est lâché !! Tout d'abord, on change l'état de l'ampoule (on ajoute 1, et si le résultat est 2, on le passe a 0 ; tout cela est écrit avec modulo 2, qui permet de gérer plus proprement un état double).
Le nouvel état de l'ampoule est ensuite passé à la fonction commande_ampoule.... qui agit en conséquence.


Nous verrons dans un prochain tutoriel, l'utilisation de la tite boite de dialogue bleue, qui utilise l'évènement listen, raison pour laquelle j'ai tout d'abord commencé par lui.