Forums Développement Multimédia

Aller au contenu

- - - - -

Cartes à gratter

TUTO

5 réponses à ce sujet

#1 stefbuet

    Ceinture Bleue

  • Members
  • PipPipPipPipPip
  • 78 messages

Posté 17 July 2011 - 01:04 AM

Salut,

Article expliquant d'une manière la plus polyvalente possible comment développer des cartes à gratter réalistes avec effet de particules et génération de son dynamique.

http://blog.mediabox...-jeu-a-gratter/

#2 Nataly

    Community Jane

  • Moderateur
  • PipPipPipPipPipPipPipPip
  • 5783 messages

Posté 30 July 2011 - 12:54 PM

Salut :)

j'attaque le tuto, et pour essayer de gagner un peu de temps je vais faire les commentaires en direct, et en suivant le fil de la lecture.

Du coup, scuz le manque d'intro et le côté "liste de ce qui ne va pas"… c'est la loi du genre.

Je sais que tu as une orthographe correcte, ce serait donc sympa, à l'avenir de te relire, au moins une fois ;)
Tu te rendrais compte, en plus et par exemple, que tu as oublié de reporter les fameux \\ qui obligent aux sauts de lignes :deal:

- du coup je relis en mode édition et je corrige au fur et à mesure.
- du coup-bis j'apporte des modifs de "français" répétitions, redondances, toussa… Tu auras intérêt à vérifier que je n'ai pas trahi ta pensée

Citation

Nous aurons besoin de deux données de base, à savoir les classes de type BitmapData du fond de la case après grattage et de la texture qui sera grattée exportées depuis la bibliothèque de Flash.

ça j'ose pas le réécrire (suis pas sûre de comprendre) mais ça vaut le coup que tu t'y essaies.

Citation

Pour créer un effet de masquage, j’ai décidé d’afficher tout d’abord un bitmap de l’image de fond (avec gestion transparence) puis d'afficher par-dessus un bitmap avec la texture de recouvrement.

… jusque là, là je suis (à transparence près, je vois pas pourquoi… mais ça gène pas la lecture)…


Citation

Afin d’épouser la forme de la case à gratter, on crée un deuxième bitmap de l’image de fond et on le définit comme masque de la surface grattable. Par la suite, pour simuler un effet de grattage nous modifierons directement les pixels du bitmap de la surface supérieure masquée car nous ne pouvons utiliser qu’un seul masque par DiplayObject.

là je commence à m'y perdre… un petit crobard, peut-être ?

Le code donne des indications : j'y lis une classe CardBmpPattern, une CoverBmpPattern… peut être qu'en les présentant dans l'explication en question, on s'y retrouverait mieux, peut-être que ça t'aiderait à formuler…


Dans la vraie vie, si je suivais le tuto je testerais le bout de code que tu proposes pour m'y retrouver… Mais elles sont où les deux classes ? Me dis pas que ce sont des natives :shock: (évidemment si tu optais pour une terminologie française des objets de ton cru (et autres fonctions), on saurait tout de suite :arrow: ) J'imagine qu'elles sont ds les sources, tu devrais tout de même en dire un mot.

• N'hésite pas à couper les phrases trop longues (il n'y a que Proust et autres Balzac pour s'étaler sans nuire à l'entendement ;))
• N'hésite pas à retourner à la ligne dans un paragraphe pour structurer ton discours : souvent il suffit d'un retour pour que le même paragraphe devienne facile à comprendre (n'oublie pas que les lecteurs découvrent ton propos)


Quand je vois :

"Nous classe est maintenant capable d’afficher"
, ou "dans" en lieu et place de "sans", je ne peux pas croire que tu te sois relu ;)


-- > Excellent le paragraphe sur "comment et quand considérer que l'image est suffisamment découverte".
C'est très sympa d'explorer les mauvaises pistes sans pour autant se prendre le mur \o/

La ruse du poids, ça m'éclate :)

la formule qui illustre la mauvaise technique fait super classe et m'impressionne sérieux, heureux qu'elle ne serve à rien :mrgreen:

Et j'aime beaucoup l'illustration en image de la bonne formule, ça c'est malin :idea:


Citation

La fonction qui détecte si la case est ouverte ou pas, sera appelée à chaque cycle d’affichage par la fonction qui gère l’effacement de la surface grattable, si la case n’à pas encore été découverte ! (tout à la fin de cette dernière)

… là j'ai comme un bogue… :? Limite on s'en fout de comprendre ou pas, le code devrait lever le doute, mais puisque c'est écrit…

Un truc pendant que j'y pense : relis toi à haute voix : si tu étouffes avant de rencontrer un point, eh bien ajoute le ;)
Et chaque fois que tu poses une inflexion (de voix) il faut une ponctuation (virgule, point virgule et autres tirets).


Citation

alors autant utiliser cette technique, qui peut s’appliquer à de nombreux procédés, en particulier l'encodage de sons/images avec l'AS3

Oué ! sympa d'ouvrir le propos ! :) c'est toujours bien d'élargir les champs d'application d'une compréhension… décidément ce tuto me plait :)


Je me permets aussi quelques ré-écritures en considérant que tu t'adresses à un public à son aise avec AS3. Ce n'est pas un tuto pour expliquer à des débutants comment on écrit en AS, mais bien comment on le met au service de cette problématique. Je considère donc que parler d'EnterFrame (tout court) plutôt qu'écrire des choses du genre "grâce à l'écoute de l'événement EnterFrame…" ne nuit pas à la compréhension, tout en allégeant la phrase.

-- > Très bien aussi le chapitre III. Effets réalistes

En fait j'aime beaucoup ce parti pris du : je vous raconte le raisonnement, indépendamment de la problématique de traduction en code. Ça m'apprend beaucoup de choses, et ça éveille mon intérêt, ça permet le pont entre vraie vie et dév :)


Citation

Le détail des arguments de la fonction emit particle sera détaillé juste après :

là je fatigue ! C'est peut-être une bête coquille, mais j'ai la flemme d'éplucher pour corriger sans trahir ta pensée… tu peux la refaire ? :oops:

-- > Le son ! c'est très fort, version malin. Bien mieux qu'un bête son pré-enregistré. Et pareil, ça, ça donne des idées pour d'autres contextes.

** mode débile qui fatigue ** c'est quoi un sample ?
non c'est vrai j'imagine qu'il s'agit d'un anglicisme, j'ai pas le courage de chercher du coup je comprends pas.

---------------------

Après cette première relecture/correction, il faudrait une lecture du point de vue technique. C'était mon objectif : faire - avec mes petits doigts agiles, un fla et ton tuto - une zolie carte à gratter… Mais là :( trop tard :(

Je ne peux donc te donner qu'un sentiment d'après ce que j'ai lu :

C'est très intéressant, ça explore les pistes, explique les parti-pris de développement et le rendu est plus que convaincant, de quoi donc aider ceux qui se demandent bien comment s'y prendre. Je dis bien aider : ceux qui voudront choper les sources telles que le pourront, ceux qui voudront s'approprier les compréhensions pour en faire un truc à leur sauce le pourront aussi. En cela c'est une réussite. :roi:

Si d'autres se sentent d'un retour du point de vue technique… qu'ils n'hésitent surtout pas.
Moi j'arrête là :mrgreen:

Mais je prends encore deux lignes pour remercier chaudement : de ce dont je puis juger, c'est du bon et beau tuto


(la prochaine fois… tu te relieras ? dis ? ;))
Le savoir est le seul bien qui s'accroit quand on le partage
une tartine de tutos

#3 Pierre

  • Administrateur
  • PipPipPipPipPipPipPipPip
  • 12757 messages

Posté 08 August 2011 - 15:02 PM

compensé et annoncé ;)

#4 stefbuet

    Ceinture Bleue

  • Members
  • PipPipPipPipPip
  • 78 messages

Posté 09 August 2011 - 18:09 PM

Salut Nataly,
je vais changer plusieurs points de l'article en tenant compte de tes remarques.
Merci pour le temps passé dessus pas forcement facile dans le contexte actuel ;)

#5 Kulgar

    Ceinture Marron

  • Members
  • PipPipPipPipPipPip
  • 177 messages

Posté 25 August 2011 - 11:40 AM

Coucou :)

Voir le messageNataly, le 30 July 2011 - 12:54 PM, dit :

j'attaque le tuto, et pour essayer de gagner un peu de temps je vais faire les commentaires en direct, et en suivant le fil de la lecture.

Du coup, scuz le manque d'intro et le côté "liste de ce qui ne va pas"… c'est la loi du genre.

Je vais faire pareil :P

Alors de mon côté, je trouve que cela manque un petit peu d'explication du code. Par exemple ces deux lignes :


coverBmp.cacheAsBitmap=true;
[...]
coverMask.cacheAsBitmap=true;
 

Je sais qu'il est très important qu'ils aient cette propriété à "true" pour un ticket à gratter; mais malgré toutes les explications sur le sujet que j'ai pu lire, je n'ai toujours pas bien compris pourquoi. Aussi aurais-je très apprécié qu'une explication figure dans ton tutoriel :).

Note : j'imagine que la gestion de la transparence pour l'image de fond est nécessaire si on veut faire une image de "gains" collée sur un fond de carte à gratter ^^, mais je ne pense pas que cela soit primordial, on peut toujours s'en sortir au niveau graphismes je pense :)

...

A cette ligne ci :

Citation

Ici, nous aurons recours au mode de dessin de type BlendMode ::ERASE pour rendre la matière transparente.
Ca serait bien de mettre un lien vers :
http://help.adobe.co...Mode.html#ERASE

Tout le monde ne connaît pas forcément cette classe ;). D'ailleurs ils disent : "This process requires that the blendMode property of the parent display object be set to flash.display.BlendMode.LAYER." ce serait bien d'expliquer pourquoi dans ton cas, il n'y a pas besoin de faire ça ^^.

Me semble aussi que tu as oublié de montrer qu'il faut ajouter les imports :
       
import flash.display.DisplayObject;
import flash.display.BlendMode;
import flash.events.Event;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
 

Ca serait bien d'ajouter quelques commentaires et/ou explications dans le code ^^. Par exemple pourquoi as-tu créé la propriété "state" et pourquoi tu la testes ensuite avec "!isDrawing".

Ou encore expliquer ces quelques lignes, pourquoi avoir recours à ce procédé (il en existe d'autres, notamment je n'ai jamais eu à utiliser la classe Matrix pour créer mes propres tickets ;) ) :

var m:Matrix=new Matrix();
m.tx=brush.width/2;
m.ty=brush.height/2;
brushBmpData.draw(brush,m,c);
 

De sorte que quelqu'un qui suit ton tutoriel comprend bien tout ce qu'il est en train de coder :). C'est le meilleur moyen pour qu'il puisse extraire au mieux de ton tutoriel toute l’essence de ton enseignement ;).

Y'a une variable :
previousVelocity=0;
Qui traine dans le même tronçon de code et qui n'a pas été définie ni présentée auparavant. J'imagine que tu as oublié de la supprimer ?

...

Le coup de l'interpolation linéaire est bien trouvé, ok pour cette partie ^^.

...

Houlala, par contre pour le code concernant la classe main cela manque clairement d'explications :


cards.push(new ScratchCard(rnd>rnd2?FlashFailBmp:FlashBmp, MetalBmp, rnd>rnd2?"FAIL":"WIN"));
 

J'ai beau lire et relire cette ligne, je la trouve vraiment trop complexe ^^'. Je vois bien que tu effectues le tirage au sort en même temps, mais c'est une écriture que peu de Flasheur ont l'habitude de voir, surtout pour la condition ternaire : if condition ? do this : else do that; personnellement je ne l'avais rencontré qu'en Ruby On Rails (où, en bon fainéants, les programmeurs adorent cette écriture), je ne savais même pas que ça existait aussi en Flash ^^.

Passons à la deuxième partie.

Je cite :

Citation

comme sur l’image ci-dessous qui montre que le symbole est encore totalement invisible

Elle est où l'image ? :) Je pense que tu l'as oubliée ^^.

Tu dis que détecter le pourcentage gratté n'est pas la meilleure méthode. Soit... Mais c'est une méthode qui fonctionne très très bien quand même ^^. En général un pourcentage de 85% est idéal, le symbole est toujours bien visible et il ne faut pas gratter chaque pixel pour que ça se termine. Et puis, normalement, ceux qui grattent ils commencent par là où l'image est sensée se trouver (sous réserve que le ticket est bien conçu).

Ta méthode semble vraiment bien pensée par contre :) mais à te lire on semble comprendre que c'est l'unique méthode possible, que la détection des % n'est pas à retenir... Je ne suis pas sûr qu'il faille être aussi catégorique ;), plutôt dire :

En général on fait une détection du % des pixels grattés et on s'arrête lorsque suffisamment de pixels ont été grattés pour dévoiler l'image, mais je vais vous proposer une méthode plus complexe mais bien plus aboutie

Sinon, tu testes 10 rangées de pixel à chaque cycle pour optimiser le tout. Mais... N'eut-il pas été plus simple d'utiliser un timer qui tourne en boucle et qui toutes les secondes (ou 500 millisecondes) effectue le test pour la fin du grattage ? C'est juste une simple question, pour savoir si ça n'aurait pas été plus simple pour une optimisation plus grande encore ?

Passons à la partie suivante ^^

...

Pour les particules, tu supprimes l'événement :

addEventListener(Event.ENTER_FRAME, update);
 

Uniquement si la particule ne doit pas rester affichée. Mais... La particule qui ne bouge plus n'a plus besoin de cet événement non plus, ce serait plus propre de supprimer l'événement lorsque velocity.y == 0 && velocity.x == 0.

Cette partie me semble plutôt bien expliquée, le code se comprend plutôt bien quoi que certains passages nécessiteraient peut-être plus d'explications. Notamment sur la gestion de la fin de vie d'une particule ^^ : quels paramètres prends-tu en compte pour la supprimer, comment calcules-tu la vitesse de la particule, comment tu la mets à jour et comment tu prends en compte la vitesse de grattage ^^, ça serait bien d'expliquer tout ça en mettant le code correspondant avec les explications :).

Pour la génération de son, rien à redire ;) c'est cool :D


Voilà :) dans l'ensemble c'est du très bon boulot, tout est vraiment bien pensé, mais selon moi le code manque vraiment d'explications pour comprendre tout ce que tu fais ^^. Il ne s'agit pas d'expliquer telles ou telles fonctions en Flash, mais bien d'ajouter des éléments d'explication pour éviter qu'un bloc entier de code nous tombe dessus sans pouvoir en saisir tous les éléments.

En tout cas j'aurais appris des choses sympas sur la génération du bruit et des particules :) merci pour ce joli tuto :Hola:

Kouliane.

#6 Kulgar

    Ceinture Marron

  • Members
  • PipPipPipPipPipPip
  • 177 messages

Posté 30 August 2011 - 09:58 AM

Rebonjour :)

J'ai créé un ticket à partir du code du tutoriel, je dirais qu'à priori tout fonctionne ^^

J'ai encore 2-3 remarques. Notamment qu'il faut vraiment mieux expliquer ton code, par exemple :

var sign=velocity.x/Math.abs(velocity.x);
                        velocity.x+=sign*(0.2+lifeFactor*3)*timeFactor;
                        if(velocity.x/Math.abs(velocity.x)!=sign) velocity.x=0;
                        sign=velocity.y/Math.abs(velocity.y);
                        velocity.y+=sign*(0.2+lifeFactor*3)*timeFactor
                        if(velocity.y/Math.abs(velocity.y)!=sign) velocity.y=0;
 
                        if(!willLive) alpha=Math.sin((1-(now-creationTime)/lifeTime)*Math.PI/2);

Dans ce tronçon on ne sait pas bien ce que c'est que "sign" ni la raison du test qui, s'il réussit, place les vitesses des particules à 0. De même, il serait bien d'expliquer comment tu as trouvé la formule pour la fin de vie de la particule.

Il ne faut pas écrire 50 000 lignes d'explication mais juste quelque chose comme ça (si j'ai bien compris) :

Citation

Pour l'interpolation nous allons calculer la vitesse de déplacement du curseur en considérant la distance entre la position précédente de la brosse et sa position actuelle. Nous allons considérer au maximum 5 étapes intermédiaires, et pour optimiser le tout, si jamais la moitié de la vitesse calculée est inférieur à 5, nous prendrons plutôt ce nombre d'étapes.

Ensuite, nous calculons la position de la brosse à chacune de ces étapes. Le calcul est assez simple, connaissant les positions précédente et actuelle de la brosse, nous pouvons déterminer la position x/y de chaque étape à l'aide de la formule mathématique :
position_x = (num_etape / nbr_etapes) * position_actuelle_x + [1-(num_etape / nbr_etapes)] * position_precedente_x

Et de même pour y. La copie de la brosse "transparente" sur la matière à gratter ne change pas, il faut juste ne pas oublier de mettre à jour la Matrix "pos".

Et encore cette explication n'est pas parfaite, mais en l'état le tutoriel manque vraiment de ce genre d'explications.

var nowPos:Point=new Point(mouseBrush.x-this.x, mouseBrush.y-this.y);
                        var currentPos:Point=new Point();
 
                        var  currentVelocity:Number=Math.sqrt(Math.pow(nowPos.x-lastPos.x,2)+Math.pow(nowPos.y-lastPos.y,2));
 
                        var passNumber:Number=Math.max(5,Math.round(currentVelocity/2));
 
                        for(var i:uint=0; i<passNumber; i++) {
 
                                currentPos.x=(i/(passNumber-1))*nowPos.x+(1-i/(passNumber-1))*lastPos.x;
                                currentPos.y=(i/(passNumber-1))*nowPos.y+(1-i/(passNumber-1))*lastPos.y;
 
                                var pos:Matrix=new Matrix();
                                pos.tx=currentPos.x+coverBmpData.width/2;
                                pos.ty=currentPos.y+coverBmpData.height/2;
                                var col:ColorTransform=new ColorTransform(0,0,0,0,0,0,0,255);
                                coverBmpData.draw(mouseBrush, pos, col, BlendMode.ERASE);
                        }


Egalement ça serait bien de mettre un lien vers un tuto (s'il existe) sur l'utilisation de getPixel et à fortiori l'utilisation des bitmap. Notamment pour que le lecteur, lorsqu'il tombe sur ça :

brushBmpData.getPixel32(offset.x,offset.y)>>24)&0xff>10


N'aie pas cette tête là : :?
Il y a par exemple ce magnifique chapitre de Thibault Imbert.
Ou encore la doc Adobe qui, sur le sujet, donne même un petit exemple sympathique.

Cette ligne ci :
if((coverBmpData.getPixel32(mouseBrush.x-this.x+offset.x-brushBmpData.width/2+coverBmpData.width/2, mouseBrush.y-this.y+offset.y-brushBmpData.height/2+coverBmpData.height/2)>>24)&0xff>10) {

Pourquoi ne pas avoir créé un point comme tu l'as fait pour offset ? D'autant que tu réutilises ces coordonnées dans la ligne juste après.

Pour la génération du son, comme je voulais juste tester rapidement j'ai copié/collé ton code, et à force de gratter il arrive forcément un moment où le son ne s'arrête plus même si je ne gratte plus (et que je ne touche plus à la souris). Ca survient surtout lorsque le curseur va vite...

Quant à ce code :

package {
 
        // imports...
 
        public class ScratchCard extends MovieClip {
 
                private var isOpened:Boolean;
 
                public function ScratchCard(...) {
 
                        // [...]
                        isOpened=false;
 
                }
 
                function checkForCompletion():void {
 
                        var count:Number = 0;
 
                        for(var i:Number=analysedWidth; i<coverBmpData.width; i++) {
 
                                for (var j:Number=0; j<coverBmpData.height; j++) {
 
                                        var a:Number=(coverBmpData.getPixel32(i,j)>>24)&0xff;
                                        if (a < 10)
                                        {
                                                var px:Number=Math.pow(1-(2*Math.abs(i-coverBmpData.width/2)/coverBmpData.width),1.5);
                                                var py:Number=Math.pow(1-(2*Math.abs(j-coverBmpData.height/2)/coverBmpData.height),1.5);
                                                count +=  px * py;
                                        }
                                }
                        }
 
                        count/=coverBmpData.height*coverBmpData.width;
 
                        if(accumulatedCount > 0.11) {
                                dispatchEvent(new Event(Event.COMPLETE));
                                isOpened = true;
                        }
 
 
                }
 
        }//end of class
 
}//end of package

J'ai voulu le tester mais ça a fait complètement planter mon lecteur Flash. De fait j'ai préféré ne pas essayer le tronçon de code suivant. ^^'

Voilà, désolée de faire ma chieuse quant aux explications, mais j'insiste, ton tutoriel serait parfait avec plus d'explications pour les passages plus mathématiques :roll:

Il n'en reste pas moins que c'est du très bon boulot ;)

A bientôt

Kouliane



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