Forums Développement Multimédia

Aller au contenu

Drag avec un clip enfant

drag clip enfant parent

11 réponses à ce sujet

#1 Lina

    Ceinture Orange

  • Members
  • PipPipPip
  • 33 messages

Posté 18 May 2015 - 08:56 AM

Bonjour,

je voudrais pouvoir dragger un clip contenant un enfant.

J'ai donc un code qui ressemble à ça :

mc_parent.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_parent.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
function attrapeObjet(e:MouseEvent):void{
e.target.startDrag();
}
function lacheObjet(e:MouseEvent):void{
e.target.stopDrag();
}
 

Et sur ma scène mon clip "mc_parent" contenant lui même un clip "mc_enfant"

Tout va bien tant que je drag en cliquant uniquement sur "mc_parent", mais si je drag en cliquant sur "mc_enfant", il n'y a plus que l'enfant qui se déplace indépendamment du parent.
Ce que je souhaiterais c'est que le clip parent se déplace avec son enfant, même si on ne clique que sur "mc_enfant".

Je ne sais pas si je suis très claire dans mes explications, mais bon je ne vois vraiment pas comment faire, j'ai bien trouvé des subterfuges, mais bien trop compliqués et je suis sure qu'il doit exister une façon simple de faire ça avec le code.

Par la suite, je serais amenée à placer des actions sur cet enfant.

Toute aide sera la bienvenue!

#2 dldler

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 4163 messages

Posté 18 May 2015 - 09:42 AM

Bonjour Lina.
C'est toute la différence entre les 2 propriétés :
- target
- currenttarget

- target sera toujours l'objet cliqué
- currenttarget sera toujours l'objet qui écoute

La solution semble être :
e.currenttarget.startDrag();


Sinon, il y a une autre solution, moins bien pour ton exemple mais qui pourra te rendre service un jour : tu peux rendre les enfants d'un movieClip insensible à la souris.
Ça se fait avec : parent.mouseChildren = false;
En rendant l'enfant insensible, il devient inerte mais les événements souris sont quand même reçu par le parent et dans ce cas, le target serait bon.

#3 Lina

    Ceinture Orange

  • Members
  • PipPipPip
  • 33 messages

Posté 18 May 2015 - 14:38 PM

Merci dldler pour ta réponse.

Ah oui effectivement, ça marche bien avec currentTarget !

parent.mouseChildren = false peut-être intéressant aussi, mais bon pour le moment j'ai besoin de pouvoir effectuer des actions sur le clip enfant, du coup ce sera pas ici, je le garde dans un coin de ma tête.

Par contre je bloque sur autre chose maintenant, dans l'idéal, je voudrais pouvoir droper le futur enfant sur le parent, afin de n'en faire qu'un objet draggable par le parent, le clic sur l'enfant devrait permettre de récupérer l'enfant à part.

J'ai procédé comme ça (surement pas la meilleure façon, mais bon j'ai pas trouvé mieux pour le moment :s ) :

mc_parent.mc_enfant.alpha=0;
mc_parent.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_parent.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
mc_enfant.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_enfant.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
function attrapeObjet(e:MouseEvent):void{
e.currentTarget.startDrag();//currentTarget permet de selectionner uniquement le parent
}
function lacheObjet(e:MouseEvent):void{
e.currentTarget.stopDrag();
}
mc_parent.mc_enfant.addEventListener(MouseEvent.MOUSE_DOWN, uneAction);
function uneAction(e:MouseEvent):void{
addChild(mc_enfant);
mc_enfant.startDrag();
mc_parent.mc_enfant.alpha=0;
}
mc_enfant.addEventListener(MouseEvent.MOUSE_UP, lacheEnfant);
function lacheEnfant(e:MouseEvent):void{
if(e.currentTarget.hitTestObject(mc_parent)){
  removeChild(mc_enfant);
  mc_parent.mc_enfant.alpha=1;
}
}
 

Sur ma scène, j'ai mc_enfant, mc_parent contenant mc_enfant avec l'alpha à 0 au départ.

Tout marche comme je veux, sauf lorsque j'essaie de récupérer l'enfant, je voudrais pouvoir le dragger seul directement, mais là c'est le parent qui vient à la place.

Il y a surement une méthode plus intelligente de procéder, mais là j'avoue que je tourne un peu en rond...

Fichier(s) joint(s)



#4 dldler

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 4163 messages

Posté 18 May 2015 - 15:21 PM

Re


Désolé, je n'ai pas de version assez récente de Flash pour ouvrir ton fichier et je ne visualise pas ce que tu veux faire.


Sinon, ceci est faux ou au moins trompeur dans le commentaire :

e.currentTarget.startDrag();//currentTarget permet de selectionner uniquement le parent
 
currentTarget ne permet pas de récupérer le parent.
currentTarget récupère l'objet sur lequel tu as fait le addEventListener

Exemple
Soit un clip c_a contenant 2 enfants : c_b et c_c.

Je peux faire:
c_a.addEventListener(MouseEvent.truc,machin);

function machin(event:MouseEvent):void {
   trace (currentTarget); // tracera toujours c_a
   trace (target); // tracera l'objet cliqué, donc selon le contexte : c_a, c_b ou c_c
}


Pour ton problème suivant, si tu veux (et pour le peu que j'ai compris) dragguer "parfois" le parent et "parfois" l'enfant, il faut que tu définisses la condition logique correspondant au "parfois", sans cela tu ne peux pas coder.
Ça te permettra probablement de poser un seul écouteur, et dans la fonction de rappel tu pose un si :

if(condition_du_parfois) {
   mc_parent.startDrag();
} else {
  mc_enfant.startDrag();
}

ou mieux :

if(condition_du_parfois) {
   currentTarget.startDrag();
} else {
  target.startDrag();
}


#5 Lina

    Ceinture Orange

  • Members
  • PipPipPip
  • 33 messages

Posté 19 May 2015 - 08:59 AM

Merci encore !

Oui c'est plus facile à gérer avec des conditions, du coup j'ai un peu réadapté ce que j'avais fait au départ :


import flash.events.MouseEvent;
import flash.display.MovieClip;
// je retire les enfants au lancement
mc_barre.removeChild(mc_barre.mc_1);
mc_barre.removeChild(mc_barre.mc_2);
mc_barre.removeChild(mc_barre.mc_3);
mc_barre.removeChild(mc_barre.mc_4);
mc_barre.removeChild(mc_barre.mc_5);
// écouteurs
mc_barre.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_barre.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
mc_1.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_1.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
mc_2.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_2.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
mc_3.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_3.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
mc_4.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_4.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
mc_5.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_5.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
function attrapeObjet(e:MouseEvent):void{
//Si l'objet attrapé est le parent
if(e.target.name==e.currentTarget.name) {
  //on drag le parent
  e.currentTarget.startDrag();
}
//Sinon
else{
  //on récupère et drag l'enfant attrapé
  // récupèrer le clip enfant (mc_1, mc_2...) dynamiquement ?
  var enfantX:int;
  var enfantY:int;
  enfantX = mc_barre.mc_1.x;
  enfantY = mc_barre.mc_1.y;
  mc_barre.removeChild(mc_barre.mc_1);
  addChild(mc_1);
  mc_1.x=mc_barre.x+enfantX;
  mc_1.y=mc_barre.y+enfantY;
  mc_1.startDrag();
}

}
function lacheObjet(e:MouseEvent):void{
e.currentTarget.stopDrag();
}
mc_1.addEventListener(MouseEvent.MOUSE_UP, bouleDrop);
function bouleDrop(e:MouseEvent):void{
if(e.currentTarget.hitTestObject(mc_barre)) {
  //recupèrer ce nom (mc_1, mc_2...) dynamiquement?
  removeChild(mc_1);
  mc_barre.addChild(mc_barre.mc_1);
}
}
 

En revanche je vais être amenée à avoir plusieurs enfants pour un parent, du coup j'aurais voulu pouvoir ajouter ou supprimer ces enfants de manière dynamique, selon ce qui est cliqué.

Mais cela ne fonctionne pas comme je voudrais, là j'ai écrit le nom de mon premier enfant directement, mais dans l'idéal, j'aimerais le récupérer selon le clip sélectionné.

Voilà ce que j'ai tenté pour récupérer le movie clip, mais ça ne fonctionne pas :


import flash.events.MouseEvent;
import flash.display.MovieClip;
var mc_enfant:MovieClip;
var mc_parent:MovieClip;
// je retire les enfants au lancement
mc_barre.removeChild(mc_barre.mc_1);
mc_barre.removeChild(mc_barre.mc_2);
mc_barre.removeChild(mc_barre.mc_3);
mc_barre.removeChild(mc_barre.mc_4);
mc_barre.removeChild(mc_barre.mc_5);
// écouteurs
mc_barre.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_barre.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
mc_1.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_1.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
mc_2.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_2.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
mc_3.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_3.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
mc_4.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_4.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
mc_5.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_5.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
function attrapeObjet(e:MouseEvent):void{
//Si l'objet attrapé est le parent
if(e.target.name==e.currentTarget.name) {
  //on drag le parent
  e.currentTarget.startDrag();
}
//Sinon
else{
  //on récupère et drag l'enfant attrapé
  // récupèrer le clip enfant (mc_1, mc_2...) dynamiquement ?
  var enfantX:int;
  var enfantY:int;
  mc_parent=e.currentTarget;
  mc_enfant=e.target;
  enfantX = mc_parent.mc_enfant.x;
  enfantY = mc_parent.mc_enfant.y;
  mc_parent.removeChild(mc_parent.mc_enfant);
  addChild(mc_enfant);
  mc_enfant.x=mc_parent.x+enfantX;
  mc_enfant.y=mc_parent.y+enfantY;
  mc_enfant.startDrag();
}

}
function lacheObjet(e:MouseEvent):void{
e.currentTarget.stopDrag();
}
mc_1.addEventListener(MouseEvent.MOUSE_UP, bouleDrop);
function bouleDrop(e:MouseEvent):void{
if(e.currentTarget.hitTestObject(mc_barre)) {
  mc_enfant=e.currentTarget;
  //recupèrer ce nom (mc_1, mc_2...) dynamiquement?
  removeChild(mc_enfant);
  mc_barre.addChild(mc_barre.mc_enfant);
}
}
 

Mais ça me renvoie des erreurs que je ne comprend pas bien (Séquence 1, Calque 'actions', Image 1, ligne 40 1118: Contrainte implicite d'une valeur du type statique Object vers un type peut-être sans rapport flash.display:MovieClip.)

J'ai joint mon fichier (en CS5 cette fois ci ;) ), je pense que c'est plus parlant que le code tout seul.

Fichier(s) joint(s)



#6 dldler

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 4163 messages

Posté 19 May 2015 - 11:31 AM

Malheureusement, je n'ai que du 4, et ce n'est pas trop grave.
En survolant, j'arrive à suivre.

Premier "truc", ta condition :
if(e.target.name==e.currentTarget.name) {

Ça me permet de comprendre un peu ce que tu veux faire.
En fait, je suppose que ton clip parent à un genre de fond de couleur et des enfants. Quand on clic sur le fond, on drag le parent. Quand on clique sur un enfant, on drag l'enfant. Ça, c'est bon.

Par contre, faire référence aux objets en captant leur nom, c'est une (très) mauvaise habitude. Quand tu es capable d'atteindre le nom d'un objet, c'est que tu peut atteindre l'objet, donc c'est inutile de faire travailler le programme en plus. Tu peux remplacer ta condition par :
if(e.target==e.currentTarget) {
Ça, c'est mieux !

Ensuite, pas de souci pour ton stopDrag, comme tu l'as compris, c'est toujours le target que tu drag, facile. Et tu vois bien que là, tu n'as pas besoin de faire référence à son nom.
Mais alors, pourquoi écouter autant de fois le mouseUp ? Je suis même surpris que ça ne te cause pas des soucis !
Pout moi, toutes les lignes
mc_xxx.addEventListener(MouseEvent.MOUSE_DOWN, attrapeObjet);
mc_xxx.addEventListener(MouseEvent.MOUSE_UP, lacheObjet);
Passe les en commentaires (avec // devant) et tentes comme ça pour voir si ça ne ferait pas la même chose.
Si c'est bon, prends le temps de réfléchir a cette chaine d'écoute d'événement, c'est important
- j'écoute les clics sur le parent
- avec target ou currentTarget je sais si c'est le parent ou un de ses enfants qui est cliqué
- j'agis en fonction
- je n'ai absolument pas besoin d'écouter si un enfant est cliqué, je le saurai par son parent.

#7 dldler

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 4163 messages

Posté 19 May 2015 - 11:39 AM

Pour la 2e question la remarque sur le nom est la même. Par contre, si tu pouvais me donner la ligne ou l'erreur se produit (le message d'erreur doit te la donner) ça m'aiderait à voir ce qui cloche.

Sinon, globalement, si tu sais comment stocker le nom d'un movieclip, tu sais aussi stocker un movieclip… c'est juste une question de type de variable et ça a plus d'avantages :

var stockage_du_mc:MovieClip; // Je prépare une variable de stockage en dehors de ma fonction pour qu'elle soit tout le temps accessible

mc_parent.addEventListener(MouseEvent.MOUSE_UP,mouse_up);
function mouse_up(event:MouseEvent):void {
   if (event.target != event.currentTarget) {
          stockage_du_mc = event.target as MovieClip; // Je mémorise le clip déposé
}
 


#8 Lina

    Ceinture Orange

  • Members
  • PipPipPip
  • 33 messages

Posté 19 May 2015 - 12:29 PM

oui c'est vrai que pour la condition il n'y avait pas besoin du nom en fait!

Par contre j'ai bien besoin de tous mes écouteurs mc_1[...]5 car ils ne sont pas des enfants à la base.

En fait ce que j'essaie de faire, c'est de faire apparaitre des enfants dans mon objet principal lorsque ces éléments (mc_1 à 5) sont placés dessus. Je veux faire apparaitre les enfants avec addChild dans mon parent (ici mc_barre) et faire disparaitre de la scène l'élément concerné avec un removeChild.

Ensuite si l'un des enfants est cliqué, je voudrais le faire disparaitre pour faire réapparaitre le même clip qu'au début, indépendamment du parent.

Je ne les mets pas en enfants à la base car j'ai besoin qu'ils puissent être déplacés indépendamment les uns des autres.

Après ce n'est peut-être pas la méthode la plus adaptée pour arriver à ce résultat...

Justement j'ai tenté de créer des variables de type MovieClip afin faire mes addChild, mais impossible de récupérer quoi que ce soit, je dois mal m'y prendre.

J'ai fait ça :

mc_1.addEventListener(MouseEvent.MOUSE_UP, bouleDrop);
mc_2.addEventListener(MouseEvent.MOUSE_UP, bouleDrop);
mc_3.addEventListener(MouseEvent.MOUSE_UP, bouleDrop);
mc_4.addEventListener(MouseEvent.MOUSE_UP, bouleDrop);
mc_5.addEventListener(MouseEvent.MOUSE_UP, bouleDrop);
function bouleDrop(e:MouseEvent):void{
if(e.currentTarget.hitTestObject(mc_barre)) {
  var mc_enfant: MovieClip = getChildByName(e.currentTarget.name) as MovieClip;
  removeChild(mc_enfant);
  mc_barre.addChild(mc_barre.mc_enfant);
}
}
 
Ce que je ne comprends pas c'est que le removeChild fonctionne, par contre le addChild me renvoie une erreur :
TypeError: Error #2007: Le paramètre child ne doit pas être nul.
at flash.display::DisplayObjectContainer/addChild()
at test_drag_and_drop_child_v4_fla::MainTimeline/bouleDrop()

Je ne peux pas enregistrer en cs4, mais je joins un swf afin d'être plus explicite, le numéro 1 a le comportement attendu (en trichant), le but étant d'automatiser pour chacun.

Fichier(s) joint(s)



#9 dldler

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 4163 messages

Posté 19 May 2015 - 13:07 PM

Anté-Scriptum : Ça ne te dérange pas si je reste générique ? Je rentrerai dans le fond du code plus tard si j'ai besoin.

Trois petites choses :

1

Citation

Par contre j'ai bien besoin de tous mes écouteurs mc_1[...]5 car ils ne sont pas des enfants à la base.
Et alors ? C'est toi qui décide, tu n'as qu'a en faire des enfants. Tu auras moins d'écouteurs et ça t'obligeras à penser générique plutôt que cas particulier…

En fait, je pense que tu as une "vision" graphique du truc avant d'en avoir une vision logique… Je m'explique. Quand tu dis que les enfants ne sont pas sur le parent, ils sont bien "quelque part" ? Ce "quelque part" peut très bien être un Movie Clip, même s'il n'a pas de fond de couleur, de contour… un movieclip "mc_dehors" à côté de ton movieclip parent qui serait le mc_dedans.

Un écouteur sur le "dehors" avec une fonction dédié, et là tu sais exactement ce que tu as à faire.
Un écouteur sur le "dedans" avec une fonction dédiée, idem.



Pour le problème du addChild, removeChild, rien de grave. Il faut juste que tu assimiles les 2 points suivants :

2
Il faut juste comprendre que addChil et removeChild ne sont que des instructions d'inscription à ce qu'on appelle la liste d'affichage d'un clip.
Par exemple : parent.addChild(enfant); donne au clip parent la responsabilité d'afficher le clip enfant.
Si je fais : parent.removeChild(enfant); je retire cette responsabilité au clip parent.

Maitenant, si je fais :
dedans.addChild(enfant);
puis
dehors.addChild(enfant);
il se passe deux choses quand la seconde ligne s'exécute :
- la responsabilité de l'affichage de "enfant" est donnée à "dehors", ce qui implique qu'elle a été retirée à "dedans".
Du coup, je ne peux plus faire : dedans.removeChild(enfant); car enfant n'est plus dans la liste d'affichage de "dedans".
En bref : inutile de faire un removeChild si on fait un addChild juste après.

3
La ligne qui plante, c'est un autre genre d'erreur : un mauvais accès à la variable par la syntaxe à point :
Ton code :
  var mc_enfant: MovieClip = getChildByName(e.currentTarget.name) as MovieClip;
    removeChild(mc_enfant); // inutile !
    mc_barre.addChild(mc_barre.mc_enfant); // plantage
La correction :
  var mc_enfant: MovieClip = getChildByName(e.currentTarget.name) as MovieClip;
  mc_barre.addChild(mc_enfant);

- J'ai supprimé le removeChild, inutile puisqu'on fait un addChild aussitôt après
- la variable mc_enfant est une propriété de ton code principal et non pas du mc_barre ! La syntaxe pointée permet d'accéder aux propriétés d'un objet, ce que les débutants confondent parfois avec la liste d'affichage (c'est dû souvent à l'habitude de travailler au scénario où propriétés et enfants se confondent…)

Si l'un de ces points n'est pas clair, n'hésite pas à demander je prendrai plus de temps pour l'expliquer. Ce sont des points essentiels qui vont beaucoup te simplifier la vie si tu les assimiles correctement).

#10 dldler

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 4163 messages

Posté 19 May 2015 - 15:50 PM

Petit test pour tenter de reproduire ton swf.
Regarde le code, il y a beaucoup de commentaires (enlève au fur et à mesure de ta lecture tout ceux que tu comprends sans peine, ça soulagera ta vision de l'ensemble).

Le code est simple, je pense.
On peut faire encore plus simple, mais pour l'instant j'ai préféré rester sur ta structure en espérant que ça te parle plus.



- Afficher le SWF -
Fichier joint  essai.swf   2.23 Ko   5 téléchargement(s)

Fichier(s) joint(s)

  • Fichier joint  essai.fla   165.5 Ko   3 téléchargement(s)


#11 Lina

    Ceinture Orange

  • Members
  • PipPipPip
  • 33 messages

Posté 20 May 2015 - 09:08 AM

Merci beaucoup dldler pour ton aide, je me suis penchée sur tes fichiers, ça m'a permis de comprendre pas mal de choses et de simplifier mon code!
Mais tu as raison, j'ai vraiment débuté avec une vision graphique des choses, je faisais pratiquement tout avec la timeline et je plaçais du code sur chaque clip et c'est pas évident de s'en détacher quand les mauvaises habitudes sont là, mais bon, je travaille là dessus, je me rend compte que le code offre beaucoup plus de possibilités et simplifie vraiment le travail au final.

#12 dldler

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 4163 messages

Posté 20 May 2015 - 09:15 AM

;-)
J'ai fais le même parcours, et crois moi ça vaut le coup :
• plein de nouvelles possibilités grâce au code
• plein d'astuces de graphiste qui simplifient ce même code. Dans ton cas, par exemple :
1 – bien choisir les origines des clips, ça simplifie toutes les maths
2 – regarde comment les effets de volume et de chromie s'appliquent sans une ligne de code

Bonus avec ce genre de code : si tu veux utiliser 10, 20 ou 26470 boules, tu as juste à modifier l'aspect au scénario. Pas une ligne de code à ajouter ou modifier…



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