Forums Développement Multimédia

Aller au contenu

récupèration d'un tableau dans une boucle for

drag drop IIFE array for

12 réponses à ce sujet

#1 Lina

    Ceinture Orange

  • Members
  • PipPipPip
  • 33 messages

Posté 13 April 2015 - 10:12 AM

Bonjour,

je suis en train de coder un petit jeu d'habillage de poupée en AS3, j'ai décidé de stocker mes habits dans un tableau pour ne pas avoir besoin de les répéter sans arrêt.

Mais je bloque sur un truc, j'ai fait une boucle qui appelle des fonction (drag and drop), cela semblait fonctionner jusqu'à ce que je rajoute une condition pour placer l'item correctement en cas de drop pas trop loin de la cible, et là, seul le dernier objet de mon tableau est impacté.

En cherchant j'ai entendu parler de fonctions IIFE (je ne suis pas sure que ça puisse résoudre mon problème, mais cela semblait correspondre à des cas similaires ou seule la dernière entrée du tableau était prise en compte), mais je ne comprend pas pourquoi cela ne marche pas, pourtant mes trace semblent OK
Je reçois l'erreur :
TypeError: Error #1010: Un terme n'est pas défini et n'a pas de propriété.
at MethodInfo-3()
at test_sets_as3_stockage_tableau_fla::MainTimeline/frame1()

Si quelqu'un peut m'aider à y voir plus clair, voici mon code :

// Tableau des items
var arrayItems:Array = [
//["item", "snapX", "snapY", "originalX", "originalY", "customX", "customY"]
["haut01", 246.60, 166.85, haut01.x, haut01.y, haut01.x, haut01.y],
["haut02", 246.60, 166.85, haut02.x, haut02.y, haut02.x, haut02.y],
["bas01", 237.35, 215.05, bas01.x, bas01.y, bas01.x, bas01.y],
["bas02", 237.35, 215.05, bas02.x, bas02.y, bas02.x, bas02.y]];

// Déclaration des variables utiles
var sizeX:int = arrayItems.length;
// Chargement des items avec leurs fonctions associées
// Première version de ma boucle for qui ne marche que pour le dernier élément du tableau
/*
for (var i:int = 0; i < sizeX; i++)
{
                trace("mon i : "+i);
  trace("nom de l'item : "+arrayItems[i][0]);
  var nomItem:String = arrayItems[i][0];
  var originalX:Number = arrayItems[i][1];
  var originalY:Number = arrayItems[i][2];
  trace("LE nom de l'item : "+nomItem);
  this[nomItem].addEventListener(MouseEvent.MOUSE_DOWN, dragItem);
  this[nomItem].addEventListener(MouseEvent.MOUSE_UP, function (me:MouseEvent):void { dropItem(nomItem, originalX, originalY) });

}
*/

// Deuxième version de ma boucle for ...
for (var i:int = 0; i < sizeX; i++)
{
        (function(i){
  var nomItem:String = arrayItems[i][0];
  var originalX:Number = arrayItems[i][1];
  var originalY:Number = arrayItems[i][2];
  trace("mon i : "+i);
  trace("nom de l'item : "+nomItem);
  this[nomItem].addEventListener(MouseEvent.MOUSE_DOWN, dragItem); // ligne qui fait bugger, pourtant le nomItem est bien récupèré?
  //this[arrayItems[i][0]].addEventListener(MouseEvent.MOUSE_DOWN, dragItem);
  //this[arrayItems[i][0]].addEventListener(MouseEvent.MOUSE_UP, function (me:MouseEvent):void { dropItem(nomItem, originalX, originalY) });
        })(i);

}
//Drag and Drop basique
function dragItem(evt:MouseEvent):void
{
evt.target.startDrag();
}
function dropItem(item:String, posX:Number, posY:Number):void
{
this[item].stopDrag();
trace(this[item].name);
// Détection de collision avec la base
if(this[item].hitTestObject(base))
        {
         trace("dans la zone de snap");
   this[item].x = posX;
                 this[item].y = posY;
        }
        else
        {
   trace("en dehors de la zone de snap");
        }
}
 


#2 Monsieur Spi

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 7010 messages

Posté 13 April 2015 - 10:58 AM

Salut,

Beaucoup d'erreurs dans ton code.
Pour t'aider, tu pourrais partir sur un truc du genre :

// variables globales
var i:int;
var biblio:Sprite;
var nombreChapeaux:int;
var nombreVestes:int;
var zoneDrop:ZoneDrop;

init();

// initialisation du programme
function init():void{
       
        // crée la zone de drop
        zoneDrop = new ZoneDrop();
        zoneDrop.x = 250;
        zoneDrop.y = 20;
        addChild(zoneDrop);
       
        // crée la bibliothèque d'items
        biblio = new Sprite();
        biblio.x = 50;
        biblio.y = 50;
        addChild(biblio);
               
        // crée les items
        nombreChapeaux = 2;
        nombreVestes = 2;
       
        //Crée les chapeaux
        for(i=0; i<nombreChapeaux; i++){
                createNewItem(new Chapeau(), 0);
        }
       
        //Crée les vestes
        for(i=0; i<nombreVestes; i++){
                createNewItem(new Veste(), 50);
        }
}

// créer un nouvel item
function createNewItem(item, posy):void{
        item.x = i*item.width;
        item.y = posy;
        item.origX = item.x;
        item.origY = item.y;
        item.gotoAndStop(i+1);
        biblio.addChild(item);
        item.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
        item.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
}

function attrapeObjet(e:MouseEvent):void{
        e.target.startDrag();
}
function lacheObjet(e:MouseEvent):void{
        stopDrag();
        if(e.target.hitTestObject(zoneDrop)){
                //place l'objet où tu veux
        } else {
                // l'objet revient à son origine
                e.target.x = e.target.origX;
                e.target.y = e.target.origY;
        }
}

Ce n'est pas idéal, mais ça devrais t'aider à comprendre les boucles et la structure générale du programme, ici par exemple on a pas encore besoin de tableau, cela interviendra surement plus tard mais pour le moment on peut s'en passer.

Fichier(s) joint(s)



#3 Lina

    Ceinture Orange

  • Members
  • PipPipPip
  • 33 messages

Posté 13 April 2015 - 11:29 AM

Le truc c'est que je voudrais stocker quelque part les coordonnées de chaque item (qui ne sont pas les coordonnées de départ et qui seront différentes pour chacun) d'où l'idée de les mettre dans un tableau pour en faciliter l'accès à tout moment.
Du coup tu me recommande d'ajouter chaque item sur la scène avec addchild en fait? Mais j'ai peur que ça ne me cause des petits soucis de superposition (y'a pas mal de vêtements sur le jeu final, j'ai peur de ne pas m'y retrouver avec seulement leur nom :s)

En tout cas merci beaucoup pour ta réponse, je découvre toujours de nouvelles choses dans tes posts :D

#4 Monsieur Spi

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 7010 messages

Posté 13 April 2015 - 11:44 AM

Citation

Le truc c'est que je voudrais stocker quelque part les coordonnées de chaque item
Stocke les dans l'item, c'est ce que j'ai fait dans mes boucles avec "origX" et "origY".
Tu peux créer des variables dans tes objets, donc y stocker ce que tu veux.
C'est plus rapide que de stocker ces infos dans un tableau, et ça se rapproche de la POO.

Citation

Du coup tu me recommande d'ajouter chaque item sur la scène avec addchild en fait?
Non, j'ai juste ajouté les items visibles.
Tu peux créer des objets sans pour autant les afficher, par exemple :


var jupe:Jupe = new Jupe();
jupe.x = 120;
jupe.y = 200;
jupe.origX = 100;
jupe.origY = 250;
//...

 

Tant que je ne l'ajoute pas à une liste d'affichage (que ce soit celle de la scène ou d'un autre objet, comme la "biblio" dans mon exemple), l'objet est créé, existe, mais n'est juste pas affiché, à toi de l'afficher quand tu en auras besoin.
Tu peux cette fois utiliser des tableaux pour stocker ces objets non affichés (ou affichés, peu importe), et les retrouver facilement, non pas avec leur nom, mais avec leur ID dans le tableau. Dans ce cas il te faudrait faire un truc du genre :



var jupe:Jupe = new Jupe();
jupe.x = 120;
jupe.y = 200;
jupe.origX = 100;
jupe.origY = 250;
//...
monTableauDeStockageDesJupes.push(jupe);
 

Là tu crée l'objet, tu lui passe les paramètres dont tu as besoin, puis tu stocke directement l'objet dans le tableau (push = ajouter à la suite du tableau). Et dans ce cas tu pourrais créer un tableau de stockage de tes objets par type d'objets (tabJupes, tabPantanlon, tabChapeau,...) Si vraiment tu as besoin de son nom (normalement tu ne devrais pas en avoir besoin) il te suffit d'ajouter une variable avec le nom :


var jupe:Jupe = new Jupe();
jupe.x = 120;
jupe.y = 200;
jupe.origX = 100;
jupe.origY = 250;
jupe.nom = "jupe_bleue";
//...
monTableauDeStockage.push(jupe);
 


#5 Lina

    Ceinture Orange

  • Members
  • PipPipPip
  • 33 messages

Posté 13 April 2015 - 12:37 PM

Je suis en train de décortiquer ton exemple, mais je ne suis pas sure que ce soit applicable à ce que je cherche à faire, j'aime bien l'idée de mettre par exemple tous les hauts dans un même clip, mais ils seront très différents les uns des autres, du coup ils n'auront jamais les même coordonnées, et certains d'entre eux devront passer par dessus les bas, certains en dessous... ça me semblait plus simple de gérer leur hauteur avec les calques en fait, et d'autant plus simple de les dispatcher le plus joliment possible sur la scène, je ne me vois pas mettre en place un système automatique pour ça, ni m'amuser à noter toutes leurs coordonnées idéales de départ :x (j'ai pas loin d'une centaine d'items, tous différents les uns des autres, je ne sais pas si c'est très pertinent de les classer par type en fait)
Est-ce que tu peux me dire quelles sont mes erreurs dans mon code de base?

#6 Jano 95

  • Moderateur
  • PipPipPipPipPipPipPipPip
  • 4558 messages

Posté 13 April 2015 - 13:16 PM

Salut.

Lorsque tu exécute une boucle, la valeur de i est égal à la dernière valeure lue.
Si tu utilises i tu auras toujours sa dernière valeure crée.

La solution est d'utiliser un indice, placé dans l'objet, qui a la valeur de i au moment ou cet objet est créé et cela quelque soit la méthode utilisée.

Vois l'exemple suivant :

for (var i:Number=1 ; i<=5 ; i++) {
var bt:MovieClip = new bouton();
bt.name = "bt"+i;
//Création d'un indice dans le bouton
bt.id = i;
addChild(bt);
bt.txt.text = "Bouton "+i;
bt.txt.mouseEnabled = false;
bt.x = 40;
bt.y = 20 + 40 * i;

bt.addEventListener("mouseOver", Over);
bt.addEventListener("mouseOut", Out);
};
function Over(Evt:Event):void {
var bt = Evt.target;
//Récupération de l'indice avec bt.id
commentaire.text = "Mon curseur se trouve sur le bouton "+bt.id+"  son nom : "+bt.name+"  mais i="+i;
};
function Out(Evt:Event):void {
commentaire.text = ""
};
 

Fichier(s) joint(s)



#7 Monsieur Spi

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 7010 messages

Posté 13 April 2015 - 13:19 PM

Citation

je ne me vois pas mettre en place un système automatique pour ça, ni m'amuser à noter toutes leurs coordonnées idéales de départ :x

C'est pourtant ce que tu fais en faisant :

Citation

gérer leur hauteur avec les calques en fait, et d'autant plus simple de les dispatcher le plus joliment possible sur la scène

La seule différence c'est que tu place les choses à la main au lieu de les placer en code.
Après si tu veux avoir un gros tableau qui regroupe tous tes items, tu peux, faut juste bien réfléchir avant de le créer.

Fais moi confiance, utiliser les calques et la scène ne va pas t'aider au contraire, tu crois que oui car ça semble simple de poser tes éléments à la main, mais tu risque de vite t’apercevoir que c'est le contraire qui va se produire quand tu va vouloir faire évoluer ton jeu à l'aide du code.

Citation

Est-ce que tu peux me dire quelles sont mes erreurs dans mon code de base?


// Tableau des items
var arrayItems:Array = [
        //["item", "snapX", "snapY", "originalX", "originalY", "customX", "customY"]
        ["haut01", 246.60, 166.85, haut01.x, haut01.y, haut01.x, haut01.y],
        ["haut02", 246.60, 166.85, haut02.x, haut02.y, haut02.x, haut02.y],
        ["bas01", 237.35, 215.05, bas01.x, bas01.y, bas01.x, bas01.y],
        ["bas02", 237.35, 215.05, bas02.x, bas02.y, bas02.x, bas02.y]
];
Ca peut fonctionner, mais ça me semble rébarbatif et pas forcément simple à maintenir.
Toutes ces valeurs pourraient se trouver directement dans les objets (des propriétés) et tous les objets pourraient être dans un tableau, il suffirait de trouver l'objet qui se trouve à tel index du tableau et de se servir de ses propriétés.

// Déclaration des variables utiles
var sizeX:int=arrayItems.length;
Aucun intérêt à ce niveau, cette variable ne sert qu'à ta boucle.

// Deuxième version de ma boucle for ...
for (var i:int = 0; i < sizeX; i++) {
        (function(i){;
        var nomItem:String=arrayItems[i][0];
        var originalX:Number=arrayItems[i][1];
        var originalY:Number=arrayItems[i][2];
        this[nomItem].addEventListener(MouseEvent.MOUSE_DOWN, dragItem);
        // ligne qui fait bugger, pourtant le nomItem est bien récupèré?;
        //this[arrayItems[i][0]].addEventListener(MouseEvent.MOUSE_DOWN, dragItem);
        //this[arrayItems[i][0]].addEventListener(MouseEvent.MOUSE_UP, function (me:MouseEvent):void { dropItem(nomItem, originalX, originalY) });
}
}
i);

STOP !!!!! c'est quoi ce machin ? ;)
(function(i){;

Ca ne veut rien dire ce bout de code, de plus on ne met jamais de fonctions à l'intérieur d'une boucle ou d'une autre fonction, c'est hyper casse gueule et risque de tout planter, tu n'es pas en Javascript là mais en AS3...

Du coup ceci ne veut rien dire non plus :
});
}
}
i)
 

Ensuite tu as :
this[nomItem].addEventListener(MouseEvent.MOUSE_DOWN, dragItem);
Ce n'est pas bon, tu essayes d'atteindre un clip par son nom via la syntaxe "this[]", c'est inadapté, lourd et peu pertinent, d'autant que là on ne sais pas encore si l'objet est créé ou pas (à moins de les avoir créés à la main et c'est là tout le problème justement)...

De ce que je comprend, tu place tout à la main, puis tu utilise un tableau pour stocker les infos des choses que tu as placé et tu essayes de les manipuler avec le code. Ce que j'essayes de te faire faire, c'est ne plus poser les choses à la main, mais avec le code, pour ne plus être dépendant des noms de tes objets, des calques, du scénario, etc... Pour moi tu te complique la vie en faisant comme tu fait ;)

Comparons la suite de nos deux codes et ajoutons quelques commentaires :

//Drag and Drop basique
function dragItem(evt:MouseEvent):void {       
        evt.target.startDrag(); // drag la cible de l'événement
}

function dropItem(item:String, posX:Number, posY:Number):void {
        // cherche dans 'this' (on suppose la scène) l'objet ayant pour nom 'item'
        this[item].stopDrag(); // drop l'objet qu'on vient de chercher
        // Détection de collision avec la base
        if (this[item].hitTestObject(base)) { // si collision
                this[item].x=posX; // X de l'item correspond à posX (qui correspond à quoi ?)
                this[item].y=posY; // Y de l'item correspond à posY (qui correspond à quoi ?)
        } else {
                trace("en dehors de la zone de snap");
        }
}

Et le mien :

function attrapeObjet(e:MouseEvent):void{
        e.target.startDrag();
}
function lacheObjet(e:MouseEvent):void{
        stopDrag(); // drop tout objet dragué
        // si collision avec l'objet concerné par l'événement
        if(e.target.hitTestObject(zoneDrop)){
                //place l'objet où tu veux
        } else {
                // l'objet revient à son origine préenregistrée
                e.target.x = e.target.origX;
                e.target.y = e.target.origY;
        }
}

Ici je n'ai pas besoin de chercher l'objet à travers la displaylist de la scène via son nom, l'écouteur d'événement est directement sur l'objet donc il sait quel est l'objet concerné, en outre mon objet à ses propres propriétés (origX et origY), le code est plus simple et plus adaptable quel que soit l'objet.

#8 Lina

    Ceinture Orange

  • Members
  • PipPipPip
  • 33 messages

Posté 13 April 2015 - 14:11 PM

@Monsieur Spi :

De toute façon, même si je finis par les placer en code, je serais bien obligée de le faire à la main à un moment donné pour voir la tête que ça a, et récupérer les coordonnées à la main de chaque objet pour lui attribuer ensuite en code, c'est pour ça que ça me semble fastidieux.

J'avais dans l'idée de récupérer automatiquement leur positionnement de départ avec leur .x et .y

Je vais tenter une version où je crée chaque objet dans le code, pour le moment ça me semble plus compliqué, mais bon j'ai pas l'habitude c'est peut être pour ça :D

C'est vrai que cette variable sizeX ne sert pas à grand chose...

Bon tout ce qu'il y a dans ma "deuxième version de boucle for", j'avoue c'est un peu expérimental, je testais des trucs pour tenter de récupérer ce i pour chaque item et pas seulement le dernier.

Dans ma fonction de drop, les posX et posY correspondent en fait aux coordonnées que doit prendre l'objet si il est placé sur la cible, ces coordonnées sont stockées dans le tableau et je voulais les trouver pour chaque item grâce à la boucle qui aurait appelé la fonction, mais ce n'est pas possible apparemment puisque d'après ce que je comprends, mon i correspondra toujours au dernier item du tableau.

Bon je retente avec ta méthode :D

@Jano 95 :
Merci pour ta réponse,
en fait la création d'un indice dans ce cas ne peut se faire que si on génère l'item directement dans le code? Ça ne peut pas marcher avec mon tableau de base (ou alors j'ai pas trouvé comment) vu que je ne peux récupérer leur nom qu'avec le i...?

#9 Monsieur Spi

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 7010 messages

Posté 13 April 2015 - 14:57 PM

Citation

récupérer les coordonnées à la main de chaque objet pour lui attribuer ensuite en code

Non, pas forcément, là je pense qu'il faut voir la chose autrement.
Et si tu plaçait tous tes objets à la bonne place sur ton "modèle" (mannequin) et que tu ne les affichait que si l'objet correspondant en drag&drop était relâché dessus ? Il suffit de récupérer un objet avec la souris dans ta "bibliothèque" (la penderie où sont les fringues), le glisser sur le mannequin, puis, si il est à la bonne place, supprimer l'objet en cours de drag&drop et afficher l'objet final bien placé sur le mannequin.

Avec cette méthode, tous les objets sont bien placés au final, pas besoin d'enregistrer leurs coordonnées.
Tu peux placer les items comme tu veux avec le code (ou pas, mais je recommande avec...).
Le drag&drop ne sert qu'à attraper un objet et savoir si on le lâche au bon endroit (puis on efface l'objet ou on le remet dans la penderie).

Selon ton jeu, on peut imaginer une sorte de listing des fringues à afficher, comme une penderie avec un type de fringue par case ou étagère. Ca peut se faire en code car les objets sont rangés proprement dans des cases. Si maintenant tu veux une disposition plus artistique libre à toi d'utiliser la scène et y placer tes objets, ce sera juste (à mon avis) plus contraignant.

#10 Monsieur Spi

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 7010 messages

Posté 13 April 2015 - 15:09 PM

Revoilà un exemple, mais cette fois-ci avec ta méthode, c'est à dire en plaçant les objets sur la scène à la main et en utilisant un tableau pour les stocker et les manipuler.

// variables globales
var i:int;
var tabItems:Array;

init();

// initialisation du programme
function init():void{
   
    // crée le tableau des items
    tabItems = [veste1, veste2, chapeau1, chapeau2];
   
    // paramètres des items
    for(i=0; i<tabItems.length; i++){
        var it:MovieClip = tabItems[i];
        it.origX = it.x;
        it.origY = it.y;
        it.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
        it.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
    }
}

function attrapeObjet(e:MouseEvent):void{
    e.target.startDrag();
}
function lacheObjet(e:MouseEvent):void{
    stopDrag();
    var it:Object = e.target;
    if(it.hitTestObject(zoneDrop)){
        //place l'objet où tu veux
    } else {
        it.x = it.origX;
        it.y = it.origY;
    }
}

Ca revient au même, sauf que les objets sont posés à la main, et je pense que tu verras à la prochaine étape que ça risque de te compliquer les choses ;)

Fichier(s) joint(s)



#11 Monsieur Spi

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 7010 messages

Posté 13 April 2015 - 15:35 PM

Et, toujours avec la méthode sur laquelle tu étais parti, voici un exemple de ce dont je parlais plus haut, en plaçant directement tes objets sur le mannequin et en se servant de leurs noms pour les distinguer.

// variables globales
var i:int;
var tabItems:Array;
var tabFinalItems:Array;

init();

// initialisation du programme
function init():void{
   
    var item:MovieClip;
   
    // crée le tableau des items
    tabItems = [veste1, veste2, chapeau1, chapeau2];
    tabFinalItems  = [final_veste1, final_veste2, final_chapeau1, final_chapeau2];
   
    // paramètres des items
    for(i=0; i<tabItems.length; i++){
        item = tabItems[i];
        item.origX = item.x;
        item.origY = item.y;
        item.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
        item.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
    }
    // paramètres des cibles
    for(i=0; i<tabFinalItems.length; i++){
        tabFinalItems[i].alpha = 0.2;
    }
}

function attrapeObjet(e:MouseEvent):void{
    e.target.startDrag();
}
function lacheObjet(e:MouseEvent):void{
    stopDrag();
    var item:Object = e.target;
    var cible:MovieClip;
   
    for(i=0; i<tabFinalItems.length; i++){
        cible = tabFinalItems[i]
        if(item.hitTestObject(cible)){
            if("final_"+item.name == cible.name){
                removeChild(MovieClip(item));
                cible.alpha = 1;
            } else {
                item.x = item.origX;
                item.y = item.origY;
            }
        }
       
    }
}

Fichier(s) joint(s)



#12 Lina

    Ceinture Orange

  • Members
  • PipPipPip
  • 33 messages

Posté 15 April 2015 - 07:45 AM

Merci beaucoup Monsieur Spi :D !
J'ai appris plein de choses avec tes exemples, notamment pour donner des paramètres à mes objets, ça m'aide énormément et ça simplifie tout :)

#13 Monsieur Spi

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 7010 messages

Posté 15 April 2015 - 10:42 AM

Pas de problème, content que tu ait trouvé la solution à ton problème ;)
Je te recommande quand même de te pencher sur le "tout en code", ça te simplifiera la vie pour la suite ;)



1 utilisateur(s) li(sen)t ce sujet

0 membre(s), 1 invité(s), 0 utilisateur(s) anonyme(s)

authorised training centre

Centre de Formation Mediabox - Adobe et Apple Authorised Training Center.

Déclaré auprès de la Direction du Travail et de la Formation Professionnelle

Mediabox : SARL au capital de 62.000€ - Numéro d'activité : 11 75 44555 75 - SIRET : 49371646800035

MEDIABOX, 23, rue de Bruxelles, 75009 PARIS

FFP