Inscrit le: 14 Avr 2011 Messages: 1846 Sujets: 119 Spécialité en worldedit: fonctions paramétriques, équation de mouvement.
Posté le: 25/01/13 23:54 Sujet du message: Gestion des instances de struct
Ce tuto pourrait être considéré comme une suite à celui ci, concentré exclusivement sur les structs.
Nous (nous ?) (oui nous )vous parlerons avant tout de l'utilisation des struct dans des systèmes basiques utilisant plusieurs instances simultanément, comme dans des spell ou des mouvements paramétrés d'unités.
Petite voix : Pfff... Faut tout te dire toi ! t'étais passé à la suite avant d'expliquer en quoi consistait la technique !
Et bien tout part d'une idée, celle-ci étant de, pour une action répétitive, la généraliser en une method avec chaque composante particulière à chaque instance sauvegardée dans la structure.
Petite voix : On a rien compris...
Bon... Prenez 42 vélos et 42 cyclistes. Chaque vélo est rigoureusement identique, seulement chaque cycliste a ses propres caractéristiques. On pourrait imaginer la structure suivante pour les caractériser :
Jass:
struct Velo
real vitesse
integer force
real poids
integer resistance_drogue
integer type_drogue_prise
endstruct
avec donc le vélo qui représente la struct elle même.
Maintenant on veut générer une course avec ses 42 cyclistes automatiquement.
On va devoir simultanément faire bouger chaque vélo, et pour ça utiliser leurs caractéristiques. A chaque instant grâce à une boucle passant en revue chaque instance de la struct (ou chaque vélo avec les carac' cycliste) on fait bouger d'un petit peu un vélo.
A haute fréquence on simule une course de vélo.
I - Organisation d'une struct dans une library
Généralement, lorsqu'une library a pour but de gérer des actions exécutées par un utilisateur simultanément, il est plus aisé de passer par des structures. C'est vers ces cas là que nous allons nous orienter.
Pour un système basique, le schéma de ce genre de lib ressemble à ça :
Jass:
library
globals
//quelques constant private
//une variable de type de la struct en private array
private Nom array This
//une variable integer en private qui index les instances initialisé à 0
private integer I = 0
//un timer private
private Timer T = CreateTimer()
endglobals
struct Nom
//les composants de la struct
private integer i
method destroy takes nothing returns nothing
local integer i = this.i
call this.deallocate()
set I = I - 1
if I == 0 then
call PauseTimer(T)
endif
set This[i] = This[I]
set This[i].i = i
endmethod
private static method Nom2 takes nothing returns nothing
//celle qui exécutera les instances une à une
local integer i = I - 1
loop
exitwhen i < 0
//les actions à exécuter pour chaque instance
set i = i - 1
endloop
endmethod
static method create takes /* ce que tu veux*/ returns thistype
local thistype this = thistype.allocate()
set This[I] = this
set This[I].i = I
set I = I + 1
if I == 1 then
call TimerStart(T, 0.01, true, function thistype.Nom2)
endif
return this
endmethod
endstruct
endlibrary
J'ai mis dans les method de create et destroy le minimum syndical qui doit apparaitre. C'est en fonction de ce que vous voulez faire qu'il vous faudra le compléter.
Petite voix : Et sinon t'expliques pas pourquoi on est obligé de mettre tout ça ?
Patience, j'allais le faire.
II - Description du mécanisme
le bloque globals :
Les constants dont je parle vous sont personnels, à vous d'y mettre ce dont vous avez besoin.
Pour ce qui est des variables que j'ai déjà mis, elles sont indispensables pour ce type de système. Si vous avez besoin de d'autres variables private, n'hésitez pas mais en plus d'être indispensables elles sont généralement suffisantes.
Method create :
Le principe est de créer l'instance, comme son nom l'indique. C'est pourquoi on ne la privatise pas, c'est elle qui sera appelée au cours de la map.
Elle est donc là pour définir les caractéristiques de l'instance avant de s'en servir.
Ensuite notre but étant toujours de réaliser une action, il faut activer la method qui s'occupe de l'action elle même. Cependant comme on veut utiliser qu'une seule et même method pour un nombre x d'instances, il nous faut déterminer à partir de quand on a besoin d'activer la method.
C'est à ce moment là que le timer est utile, celui-ci va permettre d'activer la method. Et comme il n'est pas nécessaire de l'activer plus d'une fois, puisque la method contient une boucle passant en revue toutes les instances de structure, on met une condition qui active le timer que si on a une seule instance.
Petite voix : STOOOOP ! T'as perdu 99% de tes lecteurs la... Fais un exemple sinon ils vont se faire c**** à relire ton pavé.
Oui je pense que j'ai pas le choix.
Exemple :
Imaginons que je veux créer des patrols qui "glissent" sur une ligne se retournant dès qu'ils ne sont plus sur leur path habituel.
Pour créer une telle patrol j'ai besoin de :
-type unit;
-position départ;
-vitesse;
-angle de déplacement;
-de combien il peut rentrer dans un autre path avant de se retourner.
Ceci seront donc les paramètres de ma method create.
Ensuite pour ce qui est des caractéristiques utiles de l'instance, pour l'exécution au long terme de mon déplacement entre 2 points :
-vitesse;
-angle;
-unité créée;
-tolérance.
Ceci seront les composantes de ma structure.
On a donc la method create suivante :
Jass:
static method create takes integer type_unit, rect dep, real speed, real angle, real tolerance returns nothing
local thistype pc = thistype.allocate()
set Pc[I] = pc
set Pc[I].u = CreateUnit(Player(11), type_unit, GetRectCenterX(dep), GetRectCenterY(dep), angle)
set Pc[I].angle = angle
set Pc[I].speed = speed
set Pc[I].speed_save = speed // oubliez cette ligne... c'est pas important pour le tuto mais pour le système oui.
set Pc[I].tolerance = tolerance
set Pc[I].i = I
set I = I + 1
if I == 1 then
call TimerStart(T, 0.01, true, function Patrol_check.LaunchPatrol)
endif
endmethod
Maintenant dans un autre trigger, on appelle cette fonction 6 fois.
1er exécution :
Définition selon les paramètres de l'instance.
On a une instance, donc timer exécuté.
La boucle n'a qu'un tour à faire, et déplace d'un cran la première patrol.
2è exécution :
Définition selon les paramètres de l'instance.
On a 2 instances, donc timer non exécuté.
Maintenant la boucle fait 2 tours car I = 2. Donc même si le timer n'a pas réexécuté la method, tout est exécuté comme il le faut.
il en va de même jusqu'à 6...
Vous avez compris ?
Petite voix : Non mais s'pas grave... De toute façon j'avais décroché à l'intro. Et t'as pas parlé du membre private, il sert à quoi ?
Le membre private est la pour sauvegarder le numéro d'instance attribué à chaque instance. Ainsi lors de la suppression, on aura aucun problème de réindexing.
Method de destroy :
Même principe que pour la create mais dans le sens inverse.
À la différence que cette method n'est pas static, car elle s'applique que pour une instance. Donc pas besoin de paramètre. Si vous avez bien compris le tuto cité à l'intro vous devriez comprendre pourquoi, sinon faites un come back sur la partie V-c) (je n'expliquerais pas mieux).
On fait donc l'inverse :
-Dans le cas où l'un des composantes de la struct est susceptible de leak, on unleak;
-on détruit l'instance;
-on réindex;
-on fête ça.
Et à l'inverse du create, si le nombre d'instance passe à 0, on stop le timer. (En aucun cas on détruit le timer, il peut être utile par la suite. ce n'est qu'un timer, ça n'influencera pas sur le leak...)
Ce qui donne ce que j'avais déjà mis là haut :
Jass:
method destroy takes nothing returns nothing
local integer i = this.i
call this.deallocate()
set I = I - 1
if I == 0 then
call PauseTimer(T)
endif
set This[i] = This[I]
set This[i].i = i
endmethod
Petite voix : J'te conseille de revenir vite fait sur le membre privé.
Voyez vous, il faut réindexer c'est à dire récupérer la dernière instance et la mettre dans la case de notre tableau This qui s'est libérée. Sinon, lorsque l'on fait I = I - 1 cette dernière case ne sera plus exécutée. La case libérée étant le numéro d'instance de l'instance supprimée... C'est là qu'on va s'en servir.
Attention, n'oubliez pas de mettre à jour le numéro d'instance de l'instance déplacée.
Pour ce qui est de la method qui exécute en boucle pour chaque instance, il n'y a rien de spécifique à dire, le plan a été donné en haut, et le contenu est spécifique à chaque système... Donc tout ce que je peux dire c'est que c'est dedans que vous devrez ranger l'"action" à exécuter.
Petite voix : Moi j'trouve juste pas ça logique d'exécuter les instances dans l'ordre décroissant. Pi ça change quoi d'abord ?
Ba sans ça, quelques petits décalage qu'on pourrait qualifier de négligeable vont se créer lors d'un destroy. Voila comment l'exécution va se faire :
on a t[3] un tableau contenant 3 instances. (I = 3)
On a la boucle qui démarre à 0.
t[0] est exécuté.
t[1] est exécuté.
À ce moment là on décide de supprimer t[1], on a donc t[2] qui est déplacé dans t[1] et I = 2.
Du coup t[2] n'est pas exécuté à ce tour là.
Imaginez ce problème pour I = 100 et sur une longue durée... C'est pas énorme mais exécuter les instances de I vers 0 permet d'éviter ce problème.
Petite voix : T'as pas expliqué le choix du private/globals, static/non static et du return.
En effet.
Si vous l'avez toujours pas compris, ce type de système est récurrent. Si vous utilisez que des membres publiques, vous allez vite être à cours de nom simple et explicite. Or le vjass propose de rendre certains membres privatisé à la library, qui règle le problème.
Donc si vous le pouvez, mettez toujours private.
Vous avez remarqué, les methods create et destroy sont en publiques. C'est parce qu'on risque de devoir les utiliser à l'extérieur du système (surtout la method create en fait).
De plus create est un membre static, c'est parce que cette method a plus la propriété de fonction banale rattachée à une structure. J'aurais très bien pu mettre la même chose dans une fonction, mais on vous dira qu'il est plus propre de mettre le membre en static method dans une struct (de plus ça vous permet d'utiliser "thistype")
Elle retourne une valeur du type de la struct, ça permet si besoin à l'utilisateur de récupérer son instance à l'exécution du create.
La method destroy n'est pas static, car elle est rattachée à une instance précise.
La method d'exécution de toutes les instances est static, c'est obligatoire si on veut l'utiliser avec un timer.
Dernière remarque, la période du timer est arbitraire. J'ai mis 0.01 dans l'exemple, mais vous mettez ce qui vous arrange.
Voila ce tuto est terminé, n'oubliez pas que ce n'est qu'un exemple générique d'un système d'indexing. Vous pouvez l'adapter à l'infini selon vos besoins, mais en partant de ça vous devriez avoir une bonne base. _________________
22:27:43<Seiraw> Bah y a deux genre de personnes
22:27:57<Seiraw> les soumis et les soumises
Dernière édition par kungfu-sheep le 29/01/13 19:59; édité 11 fois
Inscrit le: 23 Aoû 2007 Messages: 7143 Sujets: 147 Spécialité en worldedit: le troll, le flood, la vulgarité, mon coeur balance Médailles: 2 (En savoir plus...)
Posté le: 26/01/13 05:08 Sujet du message:
C'est possible que c'est moi qui t'ait conseillé cette syntaxe auparavant, mais il vaut mieux utiliser cetteSyntaxe pour les method.
Y'a pas de consensus mais c'est ce qui est le plus utilisé (y compris dans les langages "réels")
Et au minimum faut faire cela pour create et destroy, car sinon ces methods sont crées pour rien, autant les redéfinir. _________________
Comme l'a dit Troll-Brain, tu ne crée pas de method create et destroy. Déjà une méthod create retourne une instance. L'utilisation de la array peut être évitée.
L'avantage d'une struct est comme même la fonction method. Il serait bien de la présenter, mais bon a toi de voir.
Dans ta première library, le timer dans la method create renvoi a une static method Move, elle n'est pas dans la library, cela pourrait embrouiller quelqu'un.
La syntaxe this pourrait être utilisé dans la fonction CrtPatrol.
"les methods Create et Destroy sont en globals" -> sont publics.
Structure mieux ton grand deux, met des gras ou/et sépare en deux sous parties : create et destroy.
Un library simple mais qui permettrait de vraiment faire quelque chose serait appréciée à la fin. _________________
Inscrit le: 14 Oct 2009 Messages: 719 Sujets: 40 Spécialité en worldedit: Les bugs Médailles: 1 (En savoir plus...)
Posté le: 26/01/13 10:20 Sujet du message:
Jass:
set this = This[i]
J'avoue ne pas comprendre à quoi ça sert .
Dans ta méthode destroy, tu ne supprimes pas this de la variable This. Et c'est ça le problème de ce genre de système : soit on déindex dans la méthode exécutée périodiquement, soit il faut un membre supplémentaire dans la struct pour connaître l'index de this dans This.
Sinon tuto utile. Tu pourrais renvoyer au tuto pour faire un Knockback (ou je sais plus comment on appelle ça) de hiveworkshop où il se sert de ce genre de système. C'est là que j'ai tout appris . _________________
Inscrit le: 23 Aoû 2007 Messages: 7143 Sujets: 147 Spécialité en worldedit: le troll, le flood, la vulgarité, mon coeur balance Médailles: 2 (En savoir plus...)
Posté le: 26/01/13 17:03 Sujet du message:
Les instances sont bien détruites est l'utilisation de l'array est pour stocker toutes les instances en cours et de pouvoir loop dessus.
Par contre il aurait pu "nullifier" quand un membre du tableau est supprimé, juste par principe même si c'est un entier.
La structure qu'il utilise est une pile/file d'attente (dépend comment on l'utilise) utilisant un tableau, mais qui ne préserve pas l'ordre relatif des instances stockées.
Le principe d'une telle structure est très simple.
Quand on a besoin de stocker une valeur supplémentaire on augmente la taille du tableau de 1 et on stocke la valeur dans le dernier index.
Quand on veut supprimer une valeur dans le tableau, on remplace ce membre par le dernier membre du tableau et on réduit la taille du tableau de 1.
Utiliser cette structure permet d'écrire un code simple et efficace, mais forcément l'ordre relatif des données stockées dans le tableau a très peu de chances d'être préservé.
Si on en a besoin on peut toujours utiliser une liste chainée à la place.
Si c'est pas clair pour vous écrivez ce que fait le code sous forme de tableau et vous comprendrez.
Cela dit ce type de structure a ses inconvénients, par exemple pour une raison évidente lors de la boucle sur le tableau, vous ne pouvez pas vous contentez de détruire une instance sans rien faire de plus.
Le cas échéant il est fort probable que vous passiez à côté de certains éléments du tableau.
En fait je viens de me rendre compte que pour éviter ce problème il suffit de parcourir le tableau dans le sens opposé :
Jass:
local integer i = I
loop
exitwhen i <= 0 /// ou == 0 mais c'est plus safe ainsi
set i = i-1
//les actions à exécuter pour chaque instance (This[i] )
endloop
En conclusion je trouve que ce tuto n'est pas très clair et brouillon, c'est à améliorer. _________________
Le violet, c'est moche.
Dernière édition par Troll-Brain le 28/01/13 20:37; édité 2 fois
Inscrit le: 21 Fév 2010 Messages: 1785 Sujets: 22 Spécialité en worldedit: La modestie Médailles: 1 (En savoir plus...)
Posté le: 26/01/13 17:17 Sujet du message:
Oui, mais en l’occurrence, c'est mal fait ^^.
Déjà, ça va poper une jolie petite erreur parce que la variable "i" sort de nul part.
Et ensuite, c'est dans l'array "This" qu'il faut remplacer l'instance, pas juste le petit "this" (qui est d'ailleurs transformé en argument/variable locale à la compilation et donc ça ne sert à rien de le modifier).
Ce qu'il faudrait faire, c'est un truc de ce genre :
Jass:
set I = I-1
set This["Index the this"] = This[I]
set This[I] = 0 // D'après le Principe De Troll-Brain :p
L'index de "this", c'est le petit "i" de la fonction qui loop ("Nom2"), mais ça suppose que l'on ne peut que supprimer une instance à partir de cette fonction.
Sinon, faut rajouter un membre "private integer index" dans la structure et l'initialiser correctement (sur "I") dans la méthode "create". _________________
Inscrit le: 14 Oct 2009 Messages: 719 Sujets: 40 Spécialité en worldedit: Les bugs Médailles: 1 (En savoir plus...)
Posté le: 26/01/13 17:21 Sujet du message:
@TB : Je ne vois toujours pas où il déindexe this dans This.
Sinon il ne serait pas utile de généraliser tout ça en en faisant un module, qu'on a juste à implémenter pour avoir une structure avec cette fonctionnalité? _________________
Inscrit le: 23 Aoû 2007 Messages: 7143 Sujets: 147 Spécialité en worldedit: le troll, le flood, la vulgarité, mon coeur balance Médailles: 2 (En savoir plus...)
Posté le: 26/01/13 17:48 Sujet du message:
Exact Tirlititi (pour les erreurs car j'ai trouvé une solution pour détruire une instance dans la boucle), ca m'apprendra à lire trop vite (et accessoirement à faire confiance à fulldragon) :p
J'ai édité mon message, faut prendre aussi en compte les précisions de Tirlititi.
Avec tous ces éléments tu ne comprends pas Sapeur-Goblin ?
Et comment tu veux reproduire la loop dans un module étant donné que le code doit se trouver dans la loop ?
En fait oui tu peux parce que les modules sont en quelque sorte des textmacro mais c'est très moche de les utiliser comme cela (Nestharus en est fan ...).
En utilisation ca donnerait ca :
Jass:
taMethod takes ....
implement tonModule
// ton code
endloop
endmethod
Ca vaut mieux de repoduire le code plutôt, c'est simple et court, tu peux l'apprendre par coeur, ou l'avoir sous la main. _________________
Inscrit le: 14 Oct 2009 Messages: 719 Sujets: 40 Spécialité en worldedit: Les bugs Médailles: 1 (En savoir plus...)
Posté le: 26/01/13 18:02 Sujet du message:
Non mais un module juste pour le système d'indexing, avec une variable thistype array, integer instanceCount, un membre position, pourquoi pas un timer, une méthode d'indexing et une autre de deindexing.
Sinon, tous ces éléments ne me permettent toujours pas de comprendre quelle ligne permet le déindexing . _________________
Inscrit le: 23 Aoû 2007 Messages: 7143 Sujets: 147 Spécialité en worldedit: le troll, le flood, la vulgarité, mon coeur balance Médailles: 2 (En savoir plus...)
Posté le: 26/01/13 18:09 Sujet du message:
Je ne comprends pas l'utilité du module, non plus pourquoi on parle "d'indexing".
Si c'est juste pour éviter d'écrire les variables c'est débile car ca ne rendrait pas le code vraiment compréhensible ...
Ici le but est juste de garder une trace sur toutes les instances en cours d'utilisation de la struct. _________________
Inscrit le: 14 Avr 2011 Messages: 1846 Sujets: 119 Spécialité en worldedit: fonctions paramétriques, équation de mouvement.
Posté le: 26/01/13 18:47 Sujet du message:
aucun d'entre vous n'a pensé à des fautes de frappes ? >< des copier coller ratés ? :p
vous êtes dure avec moi à penser que j'ai fait exprès que je pensais réellement qu'il fallait faire comme ça ><
bon chuis d'accord avec toutes les remarques sur les fautes de variables, mais quelques points m'ont laissés perplexe.
troll-brain a écrit:
Et au minimum faut faire cela pour create et destroy, car sinon ces methods sont crées pour rien, autant les redéfinir.
redit par ware, je comprends pas ce que vous voulez.
tirlititi a écrit:
Sinon, faut rajouter un membre "private integer index" dans la structure et l'initialiser correctement (sur "I") dans la méthode "create".
c'est une solution au problème de la method destroy ? mais si l'ordre des index change, je suis pas sur qu'on peut à chaque fois le mettre à jour facilement.
sinon un module je sais même pas ce que c'est...
pour ce problème d'index plutôt que rajouter un membre à la struct, pourquoi pas une loop qui check si les instances de this et This[i] (pour i allant de 1 à I) sont identiques ? _________________
22:27:43<Seiraw> Bah y a deux genre de personnes
22:27:57<Seiraw> les soumis et les soumises
Inscrit le: 23 Aoû 2007 Messages: 7143 Sujets: 147 Spécialité en worldedit: le troll, le flood, la vulgarité, mon coeur balance Médailles: 2 (En savoir plus...)
Posté le: 26/01/13 18:56 Sujet du message:
fulldragon a écrit:
aucun d'entre vous n'a pensé à des fautes de frappes ? >< des copier coller ratés ? :p
vous êtes dure avec moi à penser que j'ai fait exprès que je pensais réellement qu'il fallait faire comme ça ><
On a jamais dit cela, avant de proposer une ressource, à fortiori un tuto on est censé tester son code. M'enfin ...
Citation:
redit par ware, je comprends pas ce que vous voulez.
C'est simple, nomme tes methos create et destroy et non pas Create et Destroy.
Ce n'est pas qu'une question d'esthétique, compare les codes "jassifié" si tu ne comprends pas pourquoi.
Citation:
c'est une solution au problème de la method destroy ? mais si l'ordre des index change, je suis pas sur qu'on peut à chaque fois le mettre à jour facilement.
Laisse tomber, il suffit de loop du dernier indice du tableau au premier pour ne pas avoir de problème, comme je l'ai fait dans le code plus haut.
Et non ce type de structure ne permet pas de garder l'ordre relatif, si tu en as besoin go linked list plutôt. _________________
Inscrit le: 14 Avr 2011 Messages: 1846 Sujets: 119 Spécialité en worldedit: fonctions paramétriques, équation de mouvement.
Posté le: 26/01/13 19:03 Sujet du message:
Citation:
Laisse tomber, il suffit de loop du dernier indice du tableau au premier pour ne pas avoir de problème, comme je l'ai fait dans le code plus haut.
Et non ce type de structure ne permet pas de garder l'ordre relatif, si tu en as besoin go linked list plutôt.
arf pas eu le temps :p
j'allais y venir dans un édit justement, j'ai pas compris ton truc. tu peux expliquer en quoi ça arrange ?
Citation:
C'est simple, nomme tes methos create et destroy et non pas Create et Destroy.
Ce n'est pas qu'une question d'esthétique, compare les codes "jassifié" si tu ne comprends pas pourquoi.
les noms j'ai mi un truc générale explicite... de toute façon va falloir rajouter un truc à côté comme je l'ai expliqué
"inspirez vous de CreateUnit(), CreateTimer() pour vos noms"
mais si tu veux vraiment j'enlève la maj...
Citation:
C'est possible que c'est moi qui t'ait conseillé cette syntaxe auparavant, mais il vaut mieux utiliser cetteSyntaxe pour les method.
juste pour les method ? et tu m'as juste conseillé pour les noms de variables et de fonctions. _________________
22:27:43<Seiraw> Bah y a deux genre de personnes
22:27:57<Seiraw> les soumis et les soumises
Inscrit le: 23 Aoû 2007 Messages: 7143 Sujets: 147 Spécialité en worldedit: le troll, le flood, la vulgarité, mon coeur balance Médailles: 2 (En savoir plus...)
Posté le: 26/01/13 19:13 Sujet du message:
fulldragon a écrit:
j'allais y venir dans un édit justement, j'ai pas compris ton truc. tu peux expliquer en quoi ça arrange ?
Ecrit un tableau sur une feuille de papier et simule un parcours avec des destructions d'instance, tu devrais comprendre.
C'est comme cela que j'ai trouvé cette solution.
Citation:
les noms j'ai mi un truc générale explicite... de toute façon va falloir rajouter un truc à côté comme je l'ai expliqué
"inspirez vous de CreateUnit(), CreateTimer() pour vos noms"
mais si tu veux vraiment j'enlève la maj...
Nan mais c'est pas un caprice hein ><
Y'a une raison que j'ai expliqué, encore une fois compare les codes jassifiés avec et sans majuscule pour ces method ...
Citation:
juste pour les method ? et tu m'as juste conseillé pour les noms de variables et de fonctions.
C'est ce que je conseille oui.
Pour les noms de variables global c'est ce que je faisais ouai (Cette_syntaxe), mais là y'a pas mass fan ;p
M'enfin, j'emmerde le monde _________________
Toutes les heures sont au format GMT + 1 Heure Aller à la page 1, 2, 3, 4, 5Suivante
Page 1 sur 5
Vous ne pouvez pas poster de nouveaux sujets dans ce forum Vous ne pouvez pas répondre aux sujets dans ce forum Vous ne pouvez pas éditer vos messages dans ce forum Vous ne pouvez pas supprimer vos messages dans ce forum Vous ne pouvez pas voter dans les sondages de ce forum