Forums Développement Multimédia

Aller au contenu

[RESOLU] [AS3] Problème avec le DRAW du Bitmapdata

Draw CODE Actionscript

12 réponses à ce sujet

#1 RolyMix

    Ceinture Orange

  • Members
  • PipPipPip
  • 48 messages

Posté 05 January 2014 - 22:08 PM

Bonjour à tous !

je cherche encore et encore et je ne trouve pas mon problème... il n'y a pas beaucoup d'exemple existant de la fonction DRAW comme j'aimerai l'utiliser... Permettez moi de vous montrer comment je l'utilise et le problème que j'ai.

Je souhaite faire un jeu avec un brouillard de guerre. (zone non découverte en gris, zones découverte en estompé sans ennemis, champ de vision visible.

J'utilise donc un sprite (sZone) de la taille de mon niveau (labyrinthe vu de dessus) et je dessine dans ce sprite des ronds qui représente en fait les endroits que mon perso a déjà découvert.

J'ai donc un sprite Gris uni (sGris) . Par dessus je place un sprite (sNiveau) contenant mon niveau estompé sans ennemis, et je crée un masque (mNiveau) à sNiveau avec sZone. Ainsi, ce qui est découvert est visible mais le reste noir.

En plus, sZone, pour qu'il corresponde à l'affichage, je le décale pour corresponde à la position de mon perso dans le niveau.

Jusque la tout va bien, je n'utilise pas Draw, mais plus je me déplace dansle niveau, plus mon sZone est lourd, et plus ca rame ! Et oui !

Alors au lieu de le déplacer, je voudrais prendre juste un bout de sZone, qui correspond à ma zone de jeu.
C'est la que j'utilise le draw comme suit dans la fonction :



         public function getFogZone(x:int, y:int, W:int, H:int, bckGround:Graphics = null):Sprite {
                        var zoneEcran:Rectangle = new Rectangle(x,y,W,H);
                        var fogZone:BitmapData = new BitmapData(W ,H);
                        var fogReturn:Sprite = new Sprite();
                       
                        // Importer l'image de fond a estomper importée par bckGround
                 sNiveau.graphics.copyFrom(bckGround);
                        // Estomper avec un noir mitransparent
                 sNiveau.graphics.beginFill(0x000000, 0.5);
                 sNiveaugraphics.drawRect(0, 0, W, H);
                 sNiveau.graphics.endFill();
                       
                        // Récupérer le morceau de brouillard correspondant à la zone écran pour le mettre dans le masque
                        fogZone.draw(sZone, null, null, null, zoneEcran);
                 // Remplissage dans le masque de la partie de sZone récupérée et posée dans fogZone
                 mNiveau.graphics.clear();
                 mNiveau.graphics.beginBitmapFill(fogZone, null, true);
                 mNiveau.graphics.drawRect(0, 0, W, H);
                 mNiveau.graphics.endFill();
                       
                 // Renvoie de l'objet fogWar qui en fait contient les couches et les masque pour le brouillard.
                 return _fogWar;
                }

 

Je sais que sZone se remplit bien. Mais apparemment il n'y a jamais rien dans mon masque... qui saurait me dire pourquoi ?

J'espère que vous pourrez m'aider et surtout que j'ai été assez clair.

Je bosse sous FlashDevelop

#2 RolyMix

    Ceinture Orange

  • Members
  • PipPipPip
  • 48 messages

Posté 06 January 2014 - 23:38 PM

petit up ? Personne n'a une petite idée ? au moins me dire si l'écriture est bonne :) les 28 qui ont lu on tout compris de mon charabia ? pas besoin de précision sur quelque chose ?

#3 Monsieur Spi

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 6996 messages

Posté 07 January 2014 - 00:04 AM

Citation

Alors au lieu de le déplacer, je voudrais prendre juste un bout de sZone, qui correspond à ma zone de jeu.

C'est la solution, mais il faut l'utiliser pour tout le jeu...
En gros le but serait d'avoir une fonction "render" qui s'occupe de tracer à l'écran tout le graphismes de ton jeu en un seul Bitmap.
Mais cela impose de travailler sans clips, mais avec des textures et une méthode de rendu en temps réel.
C'est là qu'il est important de séparer caculs et rendu, normalement si tes calculs sont corrects, peut importe le rendu, autrement dit, lorsque les paramètres des objets (joueur, décor, ennemis, effets, ..) sont calculés, tu dois avoir une fonction de rendu qui trace en un seul Bitmap ( de la taille de ta zone visible ) l'intégralité des objets en fonction de leurs paramètres et d'une feuille de sprites.

#4 RolyMix

    Ceinture Orange

  • Members
  • PipPipPip
  • 48 messages

Posté 07 January 2014 - 00:33 AM

Salut M Spi!! Content de te revoir, j'espère que ça va pour toi. On s'est vu au cette de formation a Paris.

Ok c noté et j'en suis aussi persuadé. J'ai bien un moteur de rendu. Mon seul soucis, c'est comment définir une zone de pixel vide et remplir le reste sans passer par le masque?

#5 RolyMix

    Ceinture Orange

  • Members
  • PipPipPip
  • 48 messages

Posté 07 January 2014 - 00:46 AM

Peut être que ma méthode pour définir les zone visitée n'est pas bonne... Je dessine dans un spirite, à valeurs régulières, un cercle correspondant a la visibilité du joueur. Je passe donc pas la propriété Graphics de mon spirite et je fait des drawCircle.

Le pb, c'est justement que pour récupérer des données de ce sprite (sZone dans mon exemple) qui serait compatible avec mon moteur de rendu, les données devrait être au format Bitmapdata.

Je voulais donc passer par la fonction Draw d'un Bitmapdata pour récupérer la partie de sZone qui me va. Mais apparemment rien n'est dessiné par cette fonction...

Ici dans l'exemple je m'en sert comme masque, ok, c'est le mal :) mais comment par traitement de pixel, je peux lui dire quel pixel recopier en fonction d'une image "masque" sans que ça me bouffe trop de ressources?

J'ai essayer un truc pour tester les pixels et les copier avec du getpixel32 et du setpixel32 mais c'est trop gourmand! Une autre idée?

Merci en tout cas d'avoir répondu

#6 Monsieur Spi

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 6996 messages

Posté 07 January 2014 - 14:01 PM

Hello, content de te revoir par ici aussi ;-)

D'après moi la solution la plus simple serait de passer par des tuiles.

Solution 1 - avec des maps :
Tu aurais une map (tableau) correspondant à ton décor, une autre correspondant aux collisions, et une autre correspondant aux zones visibles (il y a des moyens de simplifier en une seule map mais dans un premier temps tu peux faire avec des maps séparées). Au moment de tracer ton rendu, tu regarde dans la map des zones visibles si la tuile doit être affichée ou non, et pour la phase "découverte du niveau" par ton joueur tu modifie la map des zones visibles au fur et à mesure de sa progression selon les paramètres que tu veux (par exemple 8 tuiles autour de ton joueur).

Solution 2 - en POO :
Tes tuiles sont des objets (POO), dans lesquels tu peux créer des paramètres, un de ceux-ci peut être "visité". Dans ce cas il te suffit de regarder ce paramètre avant de tracer ta tuile pour savoir si elle est visible ou non.

#7 lilive

  • Moderateur
  • PipPipPipPipPipPipPipPip
  • 2993 messages

Posté 07 January 2014 - 22:45 PM

Bonjour,
Sinon, certainement moins optimisé, mais qui serait plus proche de ta démarche actuelle, et qui serait à tenter :

Si j'ai bien compris tu as sGris, sZone et sNiveau qui sont "superposés" et qui font la même taille.
J'imagine que tu dessines dans sZone de très nombreux disques, un à chaque changement de position du perso.
Et quand le nombre de disque devient trop important, ça rame.
Tu pourrais essayer d'avoir un BitmapData bZone qui sert de masque. Il faudrait un BitmapData avec transparence, et totalement vide au départ: bZone = new BitmapData(w, h, true, 0x00000000)
A chaque déplacement du perso, tu pourrais:
- effacer sZone d'un sZone.graphics.clear()
- dessiner le nouveau disque dans sZone
- faire un draw de sZone dans bZone
Ainsi, bZone accumulerait les disques au fur et à mesure, mais sous forme de pixels plutôt que de graphics, soulageant ainsi flash au moment du rendu.

C'est sûr que ça ne sera jamais aussi bien qu'un moteur de rendu à base de tuiles qui s'occupe de dessiner uniquement la partie visible du terrain (ce qui est recommandé dans bien des cas), mais si ça se trouve ça suffira comme optimisation, et comme c'est proche de ce que tu faisais déjà marcher ça sera plus simple à coder.

#8 RolyMix

    Ceinture Orange

  • Members
  • PipPipPip
  • 48 messages

Posté 07 January 2014 - 23:11 PM

Merci les gars pour vos réponses (heu... lilive, comme Olive ?) !

M Spi, je suis d'accord avec toi sur la fluidité avec cette technique. Je la note, mais elle n'est pas très précise aussi... Cependant, je pourrais effectivement faire ma grille en POO vu que je vais y mettre des objet et autre interaction (à un autre moment) donc ca pourra servir).

Lilive, en voyant la réponse à M Spi, j'ai aussi penser à un truc comme ca. La petite nuance, c'est que les j'ai mes tableaux en mémoire, le seul sprite qui fait la taille du niveau c'est la zone découverte nommée sZone.
sGris, lui fait la taille de l'écran, il ne change jamais, il sert de fond pour les partie non découverte
sNiveau lui, c'est une copie du dessin du niveau, avant qu'on y ai placé les ennemis. Il est à chaque fois grisé pour rendre le coté "inactif" du brouillard.
Enfin, par dessus tout ca, je met l'image du niveau (de la taille de l'écran)

Pour revenir à sZone, surtout sa conception :
Effectivement, même si je fait des cercle ils se cumullent dans le graphics du sprite, et ca devient ultra super lourd. Alors j'ai aussi pensé la methode que tu proposes. Elle devrait pas trop mal fonctionner. Mon perso n'a pas spécialement la vision qui augmentera. Je vais donc, créer un BitmapData qui représentera le rond de la vision. Avec la methode que tu donnes, je fais un draw d'un cercle concu en graphics, et le dessine dans un bitmapdata une seule fois. (si dans le jeu, la zone de vision change, je pourrais le redessiner ponctuellement)

De ce fait, je n'ai plus qu'a fair eun copypixel de mon cercle Bitmapdata de la bonne taille dans, non plus un sprite, mais un autre bitmapdata. Ensuite, je n'afficherait evidemment pas l'ensemble de mon bitmapdata bZone ( ;) b comme bitmapdata lol ) mais juste le morceau que je veux qui correcpond à la position de mon écran.

Reste juste un détail a me donner sur ma fonction Draw : le rect dedans, il défini bien la zone qu'on veux dessiner du IBitmapDrawable passé en parametre, non ?

#9 Monsieur Spi

  • Community Manager
  • PipPipPipPipPipPipPipPip
  • 6996 messages

Posté 07 January 2014 - 23:17 PM

Conseil : si tu te lance dans la créa d'un jeu un peu long à faire, tu devrait prendre le temps de réfléchir à la solution tuiles et en implémenter un test pour voir, ça t'éviterai d'aller trop loin dans un sens pour tout refaire et te poser d'autres questions par la suite Image IPB


Lillive > la bise au passage ;-)

#10 RolyMix

    Ceinture Orange

  • Members
  • PipPipPip
  • 48 messages

Posté 07 January 2014 - 23:39 PM

Ok note. Dans l'ordre, c'est "finir" fonctionnellement ma class Brouillard de guerre mais après, je remplace la structure du niveau par des tuiles POO. Ce sera "facile" car ma grille est en fait une Class Map et avec les get set que j'ai mis, ça devrait être invisible, tout se fera dans ma class Map :)

Mais merci encore de m'avoir répondu, et surtout précisez moi bien comment marche le Draw (oui j'ai lu la doc :) mais ça n'a pas l'air de marcher comme j'ai lu... Le rect, en paramètre, c'est quoi??)

#11 RolyMix

    Ceinture Orange

  • Members
  • PipPipPip
  • 48 messages

Posté 10 January 2014 - 22:39 PM

Bon, cher amis, voici un petit compte rendu de ce que je me suis rendu compte à propos de la fonction DRAW.

par exemple :
fogZone.draw(_ZoneExplo, null, null, null, zoneEcran);
les arguments :
- source : image source nommée ici _ZoneExplo
- matrix : (je ne m'en sert pas)
- colorTransform : (je ne m'en sert pas)
- blendMode : (je ne m'en sert pas)
- clipRect : Le rectangle (zoneEcran) correspondant à la zone dans _ZoneExplo que je veux copier dans le clip fogZone
- smoothing : (je ne m'en sert pas)

Donc, le "probleme" que j'ai, c'est qu'en fait, si le rectangle zoneEcran a une valeur X,Y, la copie dans fogZone se fera aussi au même endroit, x,y.

Voici la subtilité de cette fonction que je ne connaissais pas et qui a toute son importance et dont la doc ne met pas spécialement en évidence (ou du moins jene l'ai pas compris comme ça).

Si ma compréhension est bonne, ce paramètre clipRect se définirait comme suit :
" Objet Rectangle qui définit la zone de l’image de destination à dessiner. Si cette valeur n’est pas fournie, aucun découpage n’est effectué et l’objet source est dessiné dans sa totalité sur l'image de destination. "

J'ai donc mal compris l'utilisation de Rect...

Du coup, avec cette fonction, je ne copie effectivement que la partie qui me va, mais je suis obligé de me trimbaler une image de la taille de mon niveau (même si le reste de l'image est vide) et de la décaler pour que la zone que j'ai tracé se place sur la scene. C'est lourd :( il faut que je trouve une autre solution... je vous tiens au jus pour ceux qui veulent.

Je proposerai mon brouillard de guerre en Class une fois qu'il sera fini :) Sinon pour le sujet de Draw, tout est clair (à moins que mon raisonnement soit erroné... n'hésitez pas à me reprendre dans ce cas :D )

#12 lilive

  • Moderateur
  • PipPipPipPipPipPipPipPip
  • 2993 messages

Posté 11 January 2014 - 15:07 PM

Effectivement la doc de clipRect est fausse.
Elle laisse entendre que ce paramètre permet de prélever dans la source un rectangle de pixels, et que seul ces pixels sont utilisés lors du draw().
Alors qu'en fait ce paramètre permet de définir le rectangle de pixels de la destination qui seront affectés par le draw. Le draw fonctionne exactement comme si on n'avait pas utilisé ce paramètre (en tenant compte du paramètre matrix de la même façon) mais les pixels de la destination en dehors du rectangle ne sont pas modifiés.



Sans BitmapData

En laissant de côté la piste des bitmapData, la question que je me pose pour ton problème est la suivante :
A partir de combien de disques (graphics.drawCircle) à dessiner sur la scène à chaque nouvel affichage du jeu flash est-il dépassé ?
Ton idée est si je l'ai comprise de dessiner un nouveau disque à chaque fois que le perso se déplace. Si tu fais cela au moindre déplacement, cad dès que le perso bouge d'un pixel, tu peux te retrouver avec des milliers de disques à dessiner.
Tu peux dessiner les disques dans un Graphics qui "fait la taille"* du niveau, dans ce cas tu dessines tous les disques dans le Graphics, même ceux qui sont inutiles car en dehors de la scène. Si j'ai bien compris c'est ce que tu faisais quand tu as ouvert cette discussion.

Une optimisation pourrait être de t'arranger pour mémoriser dans un tableau les coordonnées de chaque disque, et ne dessiner dans un Graphics qui "ferait la taille"* de la scène que les disques qui sont effectivement visibles sur la scène. Mais même ainsi tu peux avoir des milliers de disques à dessiner si le perso s'est déjà baladé. Flash le supportera-t-il sans ramer ?

* J'ai mis "faire la taille" entre guillemets parce-que qu'un Graphics a pour largeur et hauteur celle des éléments qu'il contient, et que si tu dessines des disques dedans le graphics pourra être en fait "plus grand" que le niveau, ou que la scène.

Une autre optimisation serait d'espacer au maximum les disques. Ne pas en dessiner un à chaque déplacement d'un pixel, mais à des intervalles plus espacés, 5 pixels par exemple. Tu diminueras ainsi par 5 le nombre de disques, mais ce sera graphiquement moins précis.

Si ça fait toujours trop de disques, une autra approche à tester serait de ne pas dessiner des disques, mais des lignes très larges et arrondies:
graphics.lineStyle(100, 0xFFFFFF, 1, false, LineScaleMode.NORMAL, CapsStyle.ROUND, JointStyle.ROUND);
Si tu dessines l'itinéraire du perso avec de telles lignes, se sera comme si tu l'avais fait en suivant le perso avec des disques de rayon 50 (100/2). Peut-être flash préfèrera-t-il, car il aura énormément moins d'objets graphiques à dessiner si tu t'arranges pour n'avoir qu'une seule ligne pour chaque partie linéaire du déplacement du perso (par exemple un seul lineTo pour un déplacement horizontal de 500 pixels).

Un dernière piste que je vois, sans l'utilisation des BitmapData, serait de calculer, à chaque ajout d'un disque au trajet déjà fait, le contour global de la forme résultante. Puis de dessiner ce contour à coup de beginFill, lineTo et curveTo. Là c'est balaise, mais il existe peut-être des bibliothèques toutes faites qui font cela. C'est pas la piste que j'explorerais en priorité, mais je la mentionne quand même.


Avec BitmapData

S'il n'y a pas de solution satisfaisante avec l'approche du dessin vectoriel, il y a donc la piste bitmap.
L'idée est alors de ne pas avoir à dessiner de multiples objets vectoriels à chaque raffraichissement de l'affichage. A la place on dessine dans un bitmap au fur et à mesure du déplacement.
A moins que je rate quelque chose, la conséquence de ceci est que le bitmap doit faire la taille du niveau. Pas celle de la scène. Je ne vois pas comment faire autrement.

Pour dessiner dans le bitmap tu peux faire des draw d'objets vectoriels. Par exemple un Sprite ou un Shape dans lequel tu fais des drawCircle(). A chaque fois que le perso avance, dessiner le ou les disques de son déplacement dans un Shape puis faire un draw de ce Shape pour l'ajouter au Bitmap.

Tu peux aussi faire des draw d'un autre bitmap qui contient déjà un disque, c'est peut-être plus rapide que de dessiner du vectoriel.

Je pense aussi à ce sujet qui a des points communs avec ta problématique : http://forums.mediab...ase-dun-bitmap/ (regarde eraser4.swf à la fin de la discussion)

J'ai fait un essai en dessinant à chaque déplacement un Shape contenant un disque dans un bitmap, et en utilisant ce bitmap comme masque. Voici ce que ça donne. Faire des cliqués-glissés à la souris pour simuler le déplacement du perso. Presser une touche pour remettre à 0.


- Afficher le SWF -
Fichier joint  Fog.swf   1.68 Ko   1 téléchargement(s)
Fichier joint  Fog.as   3.08 Ko   0 téléchargement(s)
Ici le niveau, le masque bitmap et la scène ont la même taille, mais ça marcherait aussi bien avec un niveau plus grand que la scène, un affichage du niveau optimisé pour n'afficher que sa partie qui est sur la scène, et un bitmap de la taille du niveau. Il faudrait juste faire varier le x et le y du masque au fur est à mesure que le niveau défile.

J'espère t'avoir aidé !



m.spi > Oui, coucou, ça faisait un moment qu'on s'était pas croisé, j'ai bêtement hésité à te saluer personnellement dans une discussion publique, mais voilà c'est rattrapé :)

#13 RolyMix

    Ceinture Orange

  • Members
  • PipPipPip
  • 48 messages

Posté 11 January 2014 - 18:06 PM

Merci Lilive pour tes conseil. je vais analyser tes différentes solutions. Pour m apart j'ai travaillé sur une solution pour optimiser le process.

Pour commencer l'explication, j'ai réalisé mon Brouillard de Guerre a partir de la compréhension du fait qu'en réalité, tout mes cercles que je dessinait en graphics ne "fusionnaient" pas en une seule forme mais restaient des cercles dans un Graphics, et du coup lourds à porter.

Voici les optimisations que j'ai fait pour réaliser mon objectif tout en économisant le process mais engardant un niveau de qualité souhaité.
1) j'ai un timer pou rles mouvements de mon perso, mais j'ai séparé le tracage/mémorisation des zone parcouru en un autre timer 5x plus lents (tout en respectant mon désir de précision).
2) je n'utilise plus de sprite avec son graphics pour mémoriser les zones explorées. Maintenant (comme conseillé) j'utilise un bitmapdata. De ce fait, et comme la vision de mon perso pourra être amené à changé en cours de jeu, je trace un cercle seul vectorisé et mémorise cette forme dans un bitmapdata. Je ne réalise cette opération que si le chanp de vision ne change en cours de route (ce qui n'arrive pas pour le moment, donc ca ne se fait qu'une fois)
3) je ne réalise pas un masque de la même dimension que mon niveau entier. En fait je n'ai pas besoin de cette précision. On a jamais vu du brouillard précis :D de ce fait, j'ai réduit la taille de mon bitmapdata d'exploration par 4. Le masque qui recevra le bitmapdata sera grossit par 4.
4) Pour palier au probleme du draw, et pour éviter de dessiner un niveau complet dans le masque à chaque fois, je redécoupe d'abord mon bitmapdata général avec un copypixel, puis de Draw le résultat dans mon masque.

Avec ces optimisations, le code tourne beaucoup plus vite que sans :). J'ai encore le code de class à réarranger, et voir comme les solution données dernièrement peuvent être pratiques.

Je vais tester ce que Lilive a donné. Pour info, mon niveau fait 64x64 cases et chaque cases 128x128pixels soit des images de 8192x8192pixel :D En tout cas, merci de confirmer que je ne suis pas fou ^^ Mais une fois qu'on sais, ben on fait autrement ;)

Du coup, le probleme du draw est réglé, je mettrai fermer, mais j'oucrirai un autre sujet pour parler de la réalisation, savoir si ca interesse quelqu'un d'autre :D et si on peut m'aider à l'optimiser encore, car je pense que je n'ai pas les bonnes manières pour faire de belle class => mes classes n'ont pas la classe :D



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