Lingo
Un article de Wikipédia, l'encyclopédie libre.
Lingo est le langage de script qui accompagne le logiciel Macromedia Director.
L'auteur du Lingo est le développeur John Henry Thompson. La lingo a été enrichi par de nombreuses sociétés ayant développé des xtras, dont intel, ageia, havok...
Sommaire |
[] Origine du nom
Le mot Lingo signifie en anglais argot, au sens de langage vernaculaire, langue spécialisée appartenant à un groupe précis (ex. l'argot des typographes).
Contrairement aux langages de niveau 4 les plus répandus (basic, javascript...), le script lingo ne reproduit pas les concepts de la programmation à l'identique mais les remanie à sa façon en vue d'une application efficace et simple. Afin de marquer la distinction il utilise des termes différents, par exemple le "tableau" est renommé "list", une classe est appelée "parent script"...
[] Caractéristiques du langage
La syntaxe du Lingo est très simple et très lisible, elle rappelle (de loin) le Basic ou le Pascal. Lingo est un dérivé ou dialecte du langage HyperTalk utilisé dans le logiciel auteur HyperCard distribué sur les MacIntosh à partir de 1986. Il a été prévu au départ pour être le plus lisible possible pour les anglophones.
- set mavariable to 10
- go to frame 20
- set word 4 of montexte to "bonjour"
Sa syntaxe s'est peu à peu rapprochée de la norme objet (syntaxe à point).
[] Syntaxe
Voici un exemple de fonction :
on multiplie(a,b)
return a*b
end multiplie
Les fonctions commencent en effet toutes par on [nom de fonction][(arguments)] et se terminent par end [nom de fonction]. Lingo est un langage très laxiste, on est par exemple autorisé à ne pas mettre les parenthèses après le nom de la fonction (on multiplie a, b).
À noter, les variables sont typées dynamiquement et il n'existe pas de différenciation entre le "=" d'affectation et le "=" de comparaison.
if a=1 then
b=2
else
if a="une chaîne" then
b=3
end if
end if
Après des années d'évolution syntaxique, Lingo est devenu un langage "pointé" assez classique, et donc très lisible.
Director est un logiciel dédié à l'interactivité. Par conséquent, Lingo permet l'interception facile d'un grand nombre d'évènements tels que : preparemovie (avant l'affichage), startmovie (au moment de l'affichage), mousedown (clic enfoncé), mouseup (clic relâché), etc. Certains scripts intercepteurs évènements concernent l'ensemble du programme, d'autres peuvent ne s'appliquer qu'à des objets précis, comme les sprites (occurrence d'un objet - par exemple graphique - sur la scène).
on mouseup
-- lorsque l'on clique sur l'objet auquel s'applique ce script''
if the mouseh<320 then
-- si la position horizontale de la souris est inférieure à 320
puppetsound(1, "bing")
-- on déclenche le son nommé "bing" sur la piste 1.
end if
end
[] Variables
Les variables numériques en lingo sont simplifiées. Les variables globales ou d'objet ne se déclarent qu'en dehors des fonctions. Les variables locales sont déclarées implicitement.
Le typage est implicite, ainsi:
- a=10 crée un integer
- a=10.0 crée un décimal.
Il n'existe pas de variables booléennes, lingo utilise les entiers 0 et 1, qui pour des raisons de lisibilité peuvent toutefois s'écrire true et false.
Les chaînes de caractères peuvent être traitées comme des tableaux à l'aide de la variable globale "the itemDelimiter".
les noms des variables peuvent contenir des lettres, des chiffres, ou le signe underscore. Elles ne peuvent pas commencer par un chiffre.
[] Symboles
On peut créer des symboles avec le signe dièse (#). Un symbole n'est pas une variable mais un nom de variable ou fonction.
Par exemple, dans maCouleur = #rouge, le mot #rouge ne signifie rien pour Lingo ; en revanche, maCouleur = couleurs[#rouge] permet de retrouver la propriété rouge de l'objet couleurs, la fonction call(#rouge,obj,arguments...) permet d'appeler la méthode rouge de l'objet obj.
[] Quelques opérateurs et fonctions
- l'affectation se fait avec le signe =
- la comparaison se fait ainsi :
- = (égalité)
- < (inférieur)
- > (supérieur)
- <= (inférieur ou égal)
- >= (supérieur ou égal)
-
- les opérateurs sont les mêmes qu'il faille comparer des entiers, des chaînes, ou quoi que ce soit d'autre.
- il y a deux types de division, toutes les deux se codent avec le caractère /. Si les deux nombres sont des entiers alors lingo effectue une division entière. Le reste de la division est obtenu avec l'opérateur mod.
- les fonctions bitAnd(), bitOr(), bitXor() et bitNot() permettent d'effectuer des opérations binaires sur des entiers traités comme des tableaux de 32 bits. Ces fonctions sont lourdes car elles passent par des conversions: pour lire rapidement un tableau de bits on utilise plutôt la division entière et le modulo, mais on perd le 32ème bit.
[] Classes de base
Lingo possède une liste de classe correspondant aux structures de données classiques, avec d'importantes automatisations.
| classe lingo | données correspondantes |
| list | tableau linéaire |
| propList | tableau associatif / struct |
| matrix | tableau à 2 dimensions |
| point | vecteur 2d |
| rect | rectangle 2d |
| quad | quadrilatère 2d |
| vector | vecteur 3d |
| transform | matrice 3d |
[] Classes de gestion des tableaux
[] Classe List
La classe list contient un tableau à mémoire dynamique. Ces tableaux sont "one-based", le premier index est égal à 1. Cela permet d'utiliser le zéro comme identifiant nul.
Y écrire ou y lire le contenu d'une cellule est une opération aussi lourde que la syntaxe pointée car la cellule du tableau est traitée comme une propriété d'objet. L'optimisation des boucles basées sur des tableaux s'appuie donc sur la simplification maximale des données à consulter, ainsi que sur l'algèbre des tableaux qui permet d'éviter les lectures et écritures dans les cellules.
Les listes se créent ainsi :
- monTableau = [] ou monTableau = [1,2"a",0,"b"]
Les tableaux peuvent contenir n'importe quel types de données:
- monTableau = [[1,vector(1,2,3)],[model1,image12]]
Quelques méthodes:
- list.sort() permet un tri croissant automatique.
- list.add() permet d'insérer des données dans une liste sortie en conservant l'ordre de tri.
- list.append() ajoute un élément à la fin du tableau, casse l'ordre de tri
- list.getOne( valeur ) fait une recherche automatique dans le tableau pour trouver l'index de la valeur
- list.deleteAt( index ) efface une valeur à un index précis
- list.deleteOne( valeur ) rechercher l'index de la valeur puis efface la cellule
Algèbre des tableaux: les tableaux peuvent utiliser tous les types d'opérateurs compatibles avec le contenu des cellules. Cela permet une considérable accélération des boucles de calcul puisque celles-ci sont alors gérées en natif.
Exemples avec les entier et les décimaux:
- [1,2,3]+[4,5,6] renvoie [5,7,9]
- [1,2,3]-[1,1,1] renvoie [0,1,2]
- [1,2,3]*[1,2,3] renvoie [1,4,9]
- [1,1] / [2,2.0] renvoie [0,0.5]
- [1,1] mod [2,2.0] renvoie [1,1]
- [1,2] > [2,1] renvoie false (0)
Pour les exemples avec des tableaux de vecteurs et matrices, voir les classes vector et transform.
[] Classe propList
Contient un tableau associatif. Les identifiants peuvent être soit:
- des symboles: monTableau = [#age:24, #sexe:"masculin", #taille: 1.80]
- des chaines de caractère (attention, la lecture est très lente): monTableau = ["age":10,"sex":"m"]
L'algèbre est identique à celui des listes linéaires.
[] Classe matrix
Encode un tableau à deux dimensions. Utilisé pour les terrains du moteur physx.
[] Classes de géométrie 2d
[] Classe point
Encode un vecteur 2d. Ses coordonnées se nomment "locH" et "locV". Elles peuvent être entières ou décimales.
- p = point( a , b )
Les données se lisent par les propriétés locV / locH ou comme un tableau:
- p.locH ou p[1] renvoie a
- p.locV ou p[2] renvoie b
L'algèbre des vecteurs 2d se comporte exactement comme l'algèbre des tableaux et peut se combiner avec:
- point(1,1)+point(2,2) renvoie point(3,3)
- [p1,p2]+[p3,p4] renvoie [p1+p3,p3+p4]
[] Classe rect
Encode un rectangle. Les valeurs peuvent être entières ou décimales.
:r = rect(left,top,right,bottom) :r.left, r.top, r.right, r.bottom ou r[1],r[2],r3],r[4] renvoient ses coordonnées :r.width et r.height renvoient sa largeur et sa hauteur
Quelques méthodes:
- r1.intersects(r2) renvoie le rectangle d'intersection entre deux rectangles
- point.inside(r1) renvoie true si le point est à l'intérieur du rectangle
- map(targetRect, sourceRect, destinationRect) effectue une homotétie du point
- map(targetPoint, sourceRect, destinationRect) effectue une homotétie du rectangle
- rect1.union(rect2) renvoie le rectangle englobant deux autres rectangles
L'algèbre des rectangles est identique à celui des points 2d.
[] Classe quad
Encode un quadrilatère. Utile surtout pour les manipulations d'images.
[] Classes de géométrie 3d
[] Classe vector
Encode un vecteur 3d. Nombres décimaux uniquement.
- v=vector(10,20,30)
Ses coordonnées sont accessibles de deux manières: soit avec les propriétés x,y,z , soit avec l'index de dimension:
- v.x ou v[1] renvoie 10
Quelques méthodes:
- vector.magnitude renvoie la longueur
- vector.normalize() lui attribue une longueur égale à 1 sans changer sa direction
- vector1.cross(vector2) renvoie le produit en croix de deux vecteurs
L'algèbre des vecteurs 3d diffère de celle des listes et des vecteurs 2d:
- la division et le modulo ne sont pas permis
- la multiplication renvoie le produit scalaire
- vector(1,0,0)*vector(0,1,0) renvoie 0.0
L'algèbre des vecteurs peut se combiner avec celui des tableaux.
- [vector(1,0,0),vector(0,1,0)] + [vector(0,1,0),vector(1,0,0)] renvoie [vector(1,1,0),vector(1,1,0)]
- [vector(1,0,0),vector(0,1,0)] * [vector(0,1,0),vector(1,0,0)] renvoie [0,0]
[] Classe transform
Encode une matrice de transformation 3d composée de 16 décimales.
- matrice = transform()
Les 3 premiers groupes de 4 nombres encodent les 3 vecteurs du repère. Le dernier groupe encode l'origine. (le 4ème nombre de ces groupes sert aux calculs internes).
Exemple: choisir le vecteur (1,1,1) pour l'axe X:
- matrice[1]=1
- matrice[2]=1
- matrice[3]=1
Quelques méthodes:
- matrice.invert() inverse la matrice
- matrice.inverse() renvoie l'inverse de la matrice
- matrice.rotate() matrice.translate() effectue une transformation absolue
- matrice.preRotate() matrice.preTranslate() effectue une transformation relative
- matrice1.multiply(matrice2) renvoie la matrice 1 transformée par la matrice 2
- matrice1.preMultiply(matrice2) renvoie la matrice 1 transformée par la matrice 2 en relatif
- matrice1.interpolate(matrice2,pourcentage) renvoie l'interpolation entre deux transformations
Algèbre des matrices:
- matrice1 * matrice2 équivaut à multiply
- matrice * vecteur renvoie le vecteur transformé par la matrice
L'algèbre des matrices se combine également avec les tableaux:
- [matrice1,matrice2] * [vecteur1,vecteur2] renvoie le tableau des vecteurs transformés
- [matrice1,matrice2] * [matrice3,matrice4] renvoie le tableau des matrices transformés
[] Types de scripts
Il existe 4 types de scripts en Lingo, deux de type procédural, deux de type objet:
[] Les scripts procéduraux
Utilisent uniquement des variables et fonctions globales. Leur utilisation doit rester limitée car les variables globales posent des problèmes au niveau de la ram.
On utilise autant que possible les script objets, les script procéduraux uniquement quand c'est nécessaire.
[] "movie script"
Ce sont les scripts utilisés pour le code procédural.
Ces scripts reçoivent quelques fonctions-événements prédéfinis qui correspondent à l'ouverture de l'application, sa fermeture, le rafraichissement d'image.
Evenements d'ouverture et clôture:
- on prepareMovie s'exécute avant l'affichage de la première image. C'est là qu'on prépare le splash screen.
- on startMovie s'exécute après l'affichage de la première image. C'est là qu'on initialise l'application.
- on stopMovie s'exécute à la clôture de l'application, c'est là qu'on détruit son contenu.
Evenements de rafraichissement d'image:
- on prepareFrame s'exécute avant le rendu de l'image, c'est ici qu'on met les relevés de temps.
- on enterFrame s'exécute pendant le laps de temps restant avant le rendu de la prochaine image, c'est là qu'on met tous les calculs.
- on exitFrame s'exécute juste avant le déplacement de la tête de lecture, c'est ici qu'on lui indique l'image qu'elle doit rendre (avec go to)
Evenement indépendant:
- on idle s'exécute selon un intervalle multiple de 16 millisecondes (60 fps). C'est là qu'on met le code indépendant du frameRate de l'application.
Evenements souris:
- on mouseDown et on mouseUp, appelés quand la souris est pressée ou relâchée.
[] Les scripts dits d'acteur
Ce sont des scripts qui se trouvent directement à l'intérieur d'un acteur pour en facilter l'intégration par un simple drag.
Ils reçoivent les évènements de souris et de rafraichissement.
[] Les scripts objet
[] Les scripts dits "parents"
Permettent de simuler des classes d'objet. La syntaxe des méthodes reste procédurale : il faut faire passer une référence à l'objet "me" en premier argument.
[] classe instanciable
- un exemple de script parent instancié:
-- propriétés property pNombre -- méthodes constructeur et destructeur on new me, n , obj pObject = obj pNombre = n return me end on delete me pObject = void end -- méthodes utilisateur on incremente me pNombre = pNombre +1 end
Si ce script se nomme "nombre", on l'instanciera par exemple de cette manière :
monNouveauNombre = new(script "nombre", 10, obj)
et on invoquera sa fonction "incremente" de cette manière :
monNouveauNombre.incremente()
[] singleton
Pour simuler un singleton, on fait un script parent qui ne contient pas de méthodes new() et delete().
On accède à sa référence comme ceci:
mySingleton = Script("singleton_name")
[] héritage
Contrairement à l'ecmascript, l'héritage n'existe pas en lingo, on peut seulement le simuler partiellement avec la propriété "ancestor" des script parents, qui correspond à une instance de la classe-mère. Il ne s'agit donc pas d'héritage mais d'une surcharge. Attention donc à la valeur de l'argument "me": il doit toujours correspondre à l'instance de dernière génération.
Il est ensuite possible de remonter automatiquement aux propriétés de l'objet ancestor depuis la référence fille.
Attention cela ne marche pas pour toutes les propriétés et méthodes des classes natives de director (auquel cas il faut pointer l'ancestor) , par ailleurs cela demande à director un temps de calcul supplémentaire : il cherche d'abord la propriété dans l'instance fille, s'il ne la trouve pas il cherche dans l'ancestor.
Pour ces raisons la simulation d'héritage est déconseillé en lingo, elle doit être utilisée le moins souvent possible.
- Exemple de simulation d'une sous-classe de List
property ancestor on new ( me ) -- sous classe de l'objet list ancestor = [1,10,20,30] return me end
obj=new Script( "testClass" ) put obj[ 1 ] -- on retrouve les propriétés de l'ancestor
[] Les scripts d'application ou "behaviors"
Ce sont des objets complexes dont le script n'est pas directement accessible. Ils contiennent de nombreux événements permettant d'interagir avec les sprites, la souris, le clavier, par exemple pour créer des boutons ou des éditeurs de texte.
Les scripts de comportement sont souvent développés pour être réutilisés par des graphistes ou intégrateurs. Par un simple drag and drop ces scripts se rattachent à des objets de la scène: image, sprite, acteur. Ces scripts ont des fonctions prédéfinies qui permettent de paramétrer manuellement les instances de même que les composants flash.
[] Implémentations
Le Lingo est un langage propriétaire, il n'en existe donc qu'une seule implémentation.
[] Commandes lingo et syntaxe javascript
Depuis sa version MX2004, le logiciel Director accepte l'usage de deux langages différents : le Lingo, et une implémentation de Javascript/ECMAScript qui exécute une grosse partie des opérations lingo.
Mirror_ebab
La source est wikipedia http://fr.wikipedia.org/wiki/Lingo
Revue de presse Lingo
