Dans cette partie nous allons étudier une notion assez pratique du Lua, celle de manipuler non plus des variables, mais des objets.
Si vous avez déjà étudié le Lua auparavant, vous devriez savoir que c’est un langage utilisant excessivement les tables indexées par clés. Cela veut dire que chaque élément d’un tableau (ou liste) est associé à une clé qui permet de la référencer. Par exemple la table suivante est indexée par des chaines de caractères :
tab = {"cle1"="valeur1", "cle2"="valeur2"} print(tab["cle1"]) -- va afficher "valeur1" |
En Lua, les clés comme les valeurs sont polymorphes, c’est à dire qu’on peut aussi bien stocker des chaines de caractères avec des tables ou encore des fonctions ou tout autre type de valeur :
tab = { [1]="exemple", "2"="de", [function() return 3 end]="table", [false]="polymorphe" } for k, v in pairs(tab) do print(k, v) end |
Affichera :
1 exemple 2 de function: 10f6840 table false polymorphe |
Même l’environnement global ( _G ) est une table !
C’est donc dans cette optique là que l’on peut manipuler le Lua sous forme de langage objet, avec chaque objet une table et chacun des éléments de cet objet une méthode ou une propriété. Par la suite, si l’on veut créer un nouvel objet, il suffira de copier la table « modèle » (concept des meta-table). Pour créer des classes d’objets hérités on procédera de la même manière, sauf qu’on modifiera certaines méthodes.
myObject = { nom="toto", age=21, ChangerNom=function(self, nom) if #nom > 1 then self.nom = nom end end, ChangerAge=function(self, age) if age > 1 then self.age = age end end } |
Et accéder très facilement à chacun des éléments :
print(myObject.age) -- affiche 21 myObject:ChangerAge(18) -- identique à myObject.ChangerAge(myObject, 18) print(myObject.age) -- affiche 18 |
Il faut comprendre cependant qu’il n’y a pas de notion d’espaces publiques et privés comme en C++/C#/Java, car c’est simplement une représentation, pas une réelle implémentation.
De cette manière vous devez enfin comprendre pourquoi on écrit platform.window:width() et non une variable ressemblant à « GetWindowWidth() ». En effet, width() est une méthode de l’objet window qui est une propriété de l’objet platform. Pareil pour platform.gc() que l’on retrouve abrégé en gc. En réalité, gc est un objet et platform.gc() en est son constructeur. D’un côté, c’est donc la même chose (ils ont tous les deux les mêmes méthodes) mais de l’autre non (ils ont des propriétés quelque peu différentes).
Nous allons donc, pour illustrer ce principe, créer ensemble un script Lua qui nous permettra de créer une et une seule fois une classe Forme ayant plusieurs méthodes dont un constructeur permettant de créer plusieurs formes.
Dans l’API Lua de la TI-Nspire, on trouve une méthode « class() » qui ne provient pas du Lua. En réalité, cette méthode économise certaines lignes qui peuvent paraître incompréhensibles.
Ces lignes sont :
Account = {} Account.__index = Account function Account.create(balance) local acnt = {} -- our new object setmetatable(acnt,Account) -- make Account handle lookup acnt.balance = balance -- initialize our object return acnt end function Account:withdraw(amount) self.balance = self.balance - amount end -- create and use an Account acc = Account.create(1000) acc:withdraw(100) |
Source : http://lua-users.org/wiki/SimpleLuaClasses
Maintenant, la même chose en utilisant class() :
Account = class() function Account:init(balance) self.balance = balance -- initialize our object end function Account:withdraw(amount) self.balance = self.balance - amount end -- create and use an Account acc = Account(1000) acc:withdraw(100) |
Avouez que cela est plus clair ?
Il est à noter également que init() est un constructeur. Le nom de ce constructeur est contraint par la méthode class() qui appelle ce dernier pour compléter la construction avec setmetatable().
Nous pouvons donc créer très facilement notre classe Forme :
Forme = class() -- Constructeur function Forme:init(x, y, image) self.xpos = x self.ypos = y self.pic = image end -- Dessiner la forme function Forme:paint(gc) gc:drawImage(self.pic, self.xpos, self.ypos) end -- Repositionner l'objet en indiquant les nouvelles coordonnees function Forme:setxy(newx, newy) if newx > xmin and newx < xmax and newy > ymin and newy < ymax then self.xpos = newx self.ypos = newy end end -- Repositionner l'objet en indiquant le deplacement a effectuer function Forme:move(dx, dy) local newx,newy self:setxy(self.xpos + dx,self.ypos + dy) end |
Vous aurez surement remarqué l’utilisation de variables globales, xmin, ymin xmax et ymax.
xmin = 20 ymin = 30 xmax = 250 ymax = 200 |
Nous pouvons enfin jouer avec notre classe Forme ! Pour cela nous allons créer deux images et les convertir en TI.Image en utilisant l’outil préliminaire de développement :
carre_rouge = image.new("\010\000\000\000\010\000\000\000\000\000\000\000\020\000\000\000\016\000\001\000\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252") smiley = image.new("\016\000\000\000\018\000\000\000\000\000\000\000 \000\000\000\016\000\001\000\156\243\156\243\156\243\189\247\213\222\235\193\136\181g\181\135\181g\181\137\185O\202\189\247\156\243\156\243\156\243\156\243\156\243{\239O\206\168\222F\243D\243d\247d\247#\243\004\235E\214\170\1858\231\156\243\156\243\156\243\213\222m\210\231\230d\247\132\251\195\255\131\255\131\255\131\255C\251\003\247\132\226\233\193r\210z\239\156\243\204\189\198\226\132\247\166\218\007\198\198\226\195\255\131\255#\243\231\197\007\198\002\251\131\230\135\185\246\222\212\218g\210D\243\166\218\166\218\197\226D\239\131\255\131\255C\247\165\218d\222\006\206\194\242\196\205O\206\170\185\005\239\131\255\197\226D\239\131\255\131\255\131\255\131\255C\251\034\255\002\255E\218\226\250b\234\167\193G\173C\247\131\255\131\255\130\218\001\198\162\222\131\255c\251\002\243\161\197\161\197\226\250\226\250\162\246\133\193f\181#\243C\251\131\255\194\193\194\156\002\202C\251\034\255\162\234\194\156\194\156\193\250\193\254\193\254\133\193g\181\003\247c\251\034\255\196\230e\218\196\234\034\255\034\255\226\242%\218%\218\226\250\226\250\161\254\133\193&\169\226\242\034\255\034\255\034\255\034\255\034\255\002\255\002\255\193\254\193\254\226\250\161\254\161\254\161\254d\189G\173\163\234\002\255\226\242\194\242\163\234\162\242\162\242\162\242\130\238\130\242B\242\130\242\162\246B\242\133\193\014\198E\214\194\242f\181\198\156\231\156\231\156\231\156\231\156\231\156\231\156\198\156\231\156B\242\227\213\235\197Y\235\136\185\130\238\193\254\132\189s\206\222\251\222\251\222\251\222\251{\239\198\156\130\242\129\254d\189\179\214\156\243\237\193\196\205\162\242\162\242\198\201)\165)\165)\165)\165H\173B\242\129\254\227\213\169\193\023\227\156\243z\239\137\185\034\226\162\246\130\242B\234\034\230\034\230B\234\034\234\129\254\130\242D\181\213\222\156\243\156\243\156\243\023\227\201\197\002\222\162\246\161\254\129\254\161\254\129\254\129\250\034\234d\189\147\214\156\243\156\243\156\243\156\243\156\243\246\222\011\206\196\205\002\222B\238\034\238\034\230\196\209\165\197\147\214{\239\156\243\156\243\156\243\189\247\156\243\156\243\023\227p\210\011\206\198\205\198\205\232\205O\210\147\214\156\243\156\243\156\243\156\243") forme1 = Forme (xmin + 20, ymin + 20, smiley) forme2 = Forme (xmax - 30, ymax - 30, carre_rouge) |
Dorénavant il ne nous reste plus qu’à créer la structure principale en utilisant divers évènements afin de tester toutes les possibilités de la classe que nous venons de créer.
-- Affichage function on.paint(gc) gc:setColorRGB(231, 231, 231) gc:fillRect(xmin, ymin, xmax-xmin+15, ymax-ymin+15) forme1:paint(gc) forme2:paint(gc) gc:setColorRGB(0, 0, 255) gc:setFont("sansserif" , "b", 11) x = gc:drawString("(" .. forme1.xpos..", ".. forme1.ypos .. ")", 50, 0, "top") end |
-- Permet de deplacer forme1 avec les fleches directionnelles function on.arrowLeft() forme1:move(-5, 0) end function on.arrowRight() forme1:move(5, 0) end function on.arrowUp() forme1:move(0, -5) end function on.arrowDown() forme1:move(0, 5) end |
-- Permet de choisir l'emplacement de forme2 avec la souris function on.mouseDown(wx, wy) forme2:setxy(wx, wy) end |
Et voilà le résultat !