Événements liés
  • Démo technique du samedi #9: 15 Septembre 2007
Pages: [1]   Bas de page
Imprimer
Auteur Fil de discussion: [Demo] Pixel Effect  (Lu 3852 fois)
0 Membres et 1 Invité sur ce fil de discussion.
Mollusk Hors ligne
PAlib Guru et
Administrateur
*****
Messages: 3094


Voir le profil WWW
Ne vous posez pas de questions, codez !

« 14 Septembre 2007, 23:45:06 »

À la quête d'une démo intéressante à présenter cette semaine, on m'a suggéré d'expliquer le code de mon effet de pixels. Le voici donc en exclusivité pour vous, nettoyé, commenté, expliqué, décortiqué, mais toujours non-optimal. Wink

Je vous laisse regarder ce que je vais expliquer, et après on bosse !



Pixel Effect : La théorie

Je pense que ce qui sera intéressant dans cette démo qui n'a rien de très intéressant, c'est la construction progressive du code... Je ne vais donc pas tout balancer en bloc, mais juste décrire les étapes successives, le cheminement de ma pensée en le faisant, etc. On ne sait jamais, ça pourrait servir. Smiley Voyons donc les différentes étapes pré-conception, donc SANS AUCUN CODE...


Définir ce qu'on veut faire...

Avant toute chose, on doit donc mettre des mots sur l'effet que l'on veut obtenir. Grâce à cela, on pourra apprécier les difficultés potentielles et voir comment les dépasser...

On cherche donc à faire un effet simple, avec des pixels qui se déplacent pour faire une sorte de transition. Ainsi, chaque pixel de l'image va bouger pour prendre la place d'un pixel dans l'image suivante... Il en ressort plusieurs choses à déterminer :

Sous quelle forme refiler les pixels de chaque image ?
On pourrait se faire un joli petit tableau avec la position de chaque pixel, mais ça sera ultra galère pour ajouter des images !!! J'ai donc opté pour avoir une image 'standard' que l'on va 'balayer' (un simple for fera l'affaire) pour lire les pixels, et ajouter 1 'pixel' à déplacer pour chaque pixel de l'image trouvé... Pour des questions pratiques, j'ai foutu ça en GIF dans mon code, mais ça n'a aucune espèce d'importance.

Sauf que voilà, si on refile une image, on la fait de quelle taille Huh? J'ai voulu faire de 'gros' pixels, donc 4x4, pour qu'ils ressortent bien. À partir de là, il faut au moins 1 ou 2 pixels de chaque côté, donc en réalité chaque pixel va 'occuper' en gros une case de 6x6...
256/6 => 42,666666666667 (en arrondissant)
192/6 => 32
J'ai donc opté pour une taille d'image à refiler de 42x32, pour ne rien avoir qui dépasse. Smiley

Sous quelle forme stocker les pixels ?
Là, à priori, le plus simple sera de faire une bête structure du genre machin[n].x et .y, on n'a rien de spécial à stocker comme informations à part ça. Smiley Il faudra au moins 2 tableaux, un pour l'image de départ, un pour l'image d'arrivée, genre... machin[0][n].x, et machin[1][n].x, etc. Pas grand-chose à voir ici...

Comment faire en sorte que chaque pixel de l'image aille à un emplacement différent ?
Là, on va devoir bien gérer le côté aléatoire de la chose... Si on fait des random bêtement, on va forcément se retrouver avec certains pixels ayant 2 ou 3 positions d'arrivée, et d'autres pixels non pris ! Il faut donc un système pour que TOUS les pixels soient sélectionnés à un moment ou un autre.

Le plus simple sera donc de mettre tous les pixels dans un tableau, de tirer au sort un pixel, puis de mettre le dernier pixel de la liste à sa place, et dire qu'on a un pixel de moins... Comme ça, on a un tableau parfaitement 'continu', et on ne risque pas de prendre 2 fois le même pixel ou d'en oublier. Cheesy

De ce point découle un autre problème : on fait quoi si 2 images n'ont pas le même nombre de pixels Huh? Il faudra donc inclure un système pour faire entrer/sortir d'en dehors de l'écran des pixels si besoin. Smiley


Point de vue technique, on va faire comment ?
On va avoir pas mal de pixels à gérer (au plus 42x32 = 1344, mais on peut limiter en sachant qu'on ne fera pas d'image toute 'pleine'. J'ai choisi de limiter à 256 pour avoir pas mal de flexibilité, mais 512 ça peut être mieux selon les images. Pour ce faire, j'ai choisi d'utiliser les Sprites3D pour avoir assez de sprites, mais on pourrait faire pareil en pixel plot sans trop de problèmes. Mais bon, l'affichage importe peu dans cette démo, c'est plus le cheminement et la construction du code qui est intéressante. Smiley


Préparer les ressources

On va avoir besoin de peu de choses : les images, et un pixel. Azn

Pour l'image, voici le GIF que j'utilise. Pour le pixel, on fera un bête tableau à remplir avec une boucle for, rien de bien méchant. Smiley


http://www.dev-fr.org/Demos/PixelEffect/title.gif




Pixel Effect : La pratique

Et voilà, le temps est venu !


Première étape : initialiser le tout, sans rien faire d'autre...

Tout d'abord, quelques tableaux pour décompresser le GIF, la palette, et foutre le pixel comme il faut :
Code
(c):
u8 pixel[8*8] __attribute__ ((aligned (4))) ; // Pour afficher le pixel :p
u16 pixel_tex; // Texture
 
u8 buffer[42*96] __attribute__ ((aligned (4))) ; // Buffer pour décoder le gif
u16 palette[256]; // Palette du gif

Rien de méchant, non ? Wink

Ensuite, on déclare les tableaux pour les pixels :
Code
(c):
// Structure de position des pixels, rien de spécial
typedef struct{
  s16 x, y;
} pos_type;
 
// Position des pixels (avant-après)
pos_type pos[2][MAX_PIXELS];
u16 npos[2]; // Nombre de pixels
 
// Temporaire pour faire le random derrière
pos_type postemp[MAX_PIXELS];
u16 npostemp; // Nombre de pixels

Le tableau 'temporaire' sera pour stocker les pixels et faire la sélection aléatoire... On a donc le tableau pos, qui a
  • et [1] pour départ/arrivée, un nombre de pixels (256 dans cette démo), et enfin npos[2], qui définit le nombre de pixels pour le départ et l'arrivée. Smiley



Je ne vais pas détailler la fonction void Effect_Init(void), qui n'a aucun intérêt, elle ne fait que charger les images, etc., et c'est assez spécifique PAlib, on s'en tape un peu. Smiley


Deuxième étape : charger l'image à afficher et préparer le terrain

C'est la fonction void Effect_InitBuffer(u8 *buffer) qui va scanner l'image (le buffer) pour chopper les pixels :
Code
(c):
  s32 i, x, y;
  npostemp = 0;
 
 // Regarder tous les pixels...
  for(y = 0; (y < 32) && (npostemp < MAX_PIXELS); y++){  
  for(x = 0; (x < 42)  && (npostemp < MAX_PIXELS); x++){
     if(buffer[x+(y)*42]){
        postemp[npostemp].x = 3+x*6;
        postemp[npostemp].y = 3+y*6;
        npostemp++;
 
     }  
  }
}
 
On ajoute donc dans la structure temporaire les positions des différents pixels, rien de bien méchant...

Ensuite, on copie les anciens pixels d'arrivée à la position de départ (puisqu'on va partir de là maintenant) :
Code
(c):
// On backup l'autre
npos[0] = npos[1];
for(i = 0; i < npos[0]; i++){
  pos[0][i].x = pos[1][i].x;
  pos[0][i].y = pos[1][i].y;  
}
   
Maintenant qu'on a 'libéré' pos[1], on va pouvoir mettre les nouveaux pixels, mais dans un ordre parfaitement ALÉATOIRE. Si on met dans l'ordre "d'acquisition", on aura un résultat médiocre (pixels qui restent trop dans le même coin...).

Code
(c):
u16 random = 0;
npos[1] = npostemp;
 
// Remplir pos[1] de façon aléatoire
for(i = 0; i < npos[1]; i++){
  random = PA_Rand()%npostemp;
  pos[1][i].x = postemp[random].x;
  pos[1][i].y = postemp[random].y;  
 
  npostemp--; // 1 de moins restant
 
  // On fout le dernier à la place de celui utilisé...
  postemp[random].x = postemp[npostemp].x;
  postemp[random].y = postemp[npostemp].y;
}
   
Voilà, comme je disais, on prend un pixel dans la liste, et on place à cet emplacement le dernier, histoire de garder une liste en continuité pour simplifier le choix. Et on pratique ainsi pour TOUS les pixels. Smiley

On a donc, à ce stade, 2 listes de pixels : pos[0] et pos[1], avec les pixels qui vont aller de pos[0] à pos[1]... Reste à régler le problème du nombre différent de pixels : on va comparer le nombre de pixel dans chaque, et s'il en manque dans l'un ou l'autre, on complètera avec des pixels hors écran. Smiley
Code
(c):
u8 small = 0;
 
 
if(npos[1] < npos[0]) small = 1; // Plus d'anciens que de nouveaux, il va falloir en faire sortir dehors ^^
 
for(i = npos[small]; i < npos[!small]; i++){  
  pos[small][i].x = -3 + (PA_Rand()&1)*(45*6);
  pos[small][i].y = 3+(PA_Rand()%34)*6;
}
   
Voilà, tout est prêt, on va pouvoir effectuer la transition en douceur. Cheesy


Dernière étape : se bouger le cul !

Il est maintenant temps de déplacer tout cela. Smiley Dans Mental Games, j'ai mis une dizaine d'effets différents, mais dans un souci de simplicité je n'en ai mis qu'un seul dans cette démo. Libre à vous d'en coder d'autres. Wink

On a donc, déjà, un tableau de pointeurs sur les fonctions des effets :
Code
(c):
// Tableau avec tous les effets, à vous de coder les autres !
fp Effect[N_EFFECTS] = {Effect0};
(j'avoue que l'intérêt pour un seul effet est plus que limite Langue)

Ensuite, on prend un effet au hasard et on l'active :
Code
(c):
u16 random = PA_Rand()%N_EFFECTS; // Un seul effet en vrai ;)
Effect[random](); // Effet au hasard :p
   
À présent, il ne reste qu'un bout de code à voir : la fonction Effect0, que voici rien que pour vous :
Code
(c):
void Effect0(void){
 
s32 i, n;
u16 limit = npos[0];
if(npos[1] > limit) limit = npos[1];
 
u8 ok = 0;
 
for(i = 1; !ok; i++){
  ok = 1;
  for(n = 0; (n < limit) && (n < i*2); n++){
     if(pos[0][n].x != pos[1][n].x){
      if(pos[0][n].x < pos[1][n].x) pos[0][n].x += 6;
      else pos[0][n].x -= 6;
ok = 0;
}      
     else if(pos[0][n].y != pos[1][n].y){
      if(pos[0][n].y < pos[1][n].y) pos[0][n].y += 6;
      else pos[0][n].y -= 6;
ok = 0;
}      
  PA_3DSetSpriteXY(n, pos[0][n].x, pos[0][n].y);
}    
  PA_3DProcess();
  PA_WaitForVBL();
}  
 
}

Elle est toute simple... On regarde combien est le maximum de pixel (limit), et on utilisera ça par la suite.
Puis, on va faire une boucle qui va continuer tant que tous les pixels ne seront pas arrivés à destination... La variable chargée de cela sera 'ok' : par défaut, elle est à 1 (tout est bon), mais si un seul pixel n'est pas en place, elle passe à 0 et on recommence...

Ensuite, la boucle avec 'n' va passer en revue les pixels, les faire bouger horizontalement vers leur destination, et verticalement si X est déjà bien. La petite subtilité, ici, c'est qu'on arrête n à la valeur i*2... Pourquoi ? Ça permet en fait de faire plus progressif. Si on n'ajoute pas cela, TOUS les pixels vont commencer à se déplacer en même temps, ça va faire un peu bouillie rapide et puis ils vont tous arriver globalement en même temps, ce qui rend moins bien. En limitant n à i*2, on aura donc au plus 2 pixels qui bougent à la première boucle, puis 4, 6, 8, etc. Plus le temps passe, et plus on aura de pixels autorisés à se déplacer. Ça évite que tout se passe d'un coup et en même temps, ça rend mieux ! (mais la valeur de 2 peut être changée selon vos convenances, bien entendu !)




Voilà, vous savez à présent tout. Cheesy Sur ce, je vais me coucher et je vous souhaite un bon week-end. Smiley
Journalisée

http://www.palib.info/images/mollusK.png
Smealum Hors ligne
Assassin
Administrateur
*****
Messages: 194


Voir le profil WWW
Assassin

« Réponse #1 : 15 Septembre 2007, 00:03:36 »

Excellent ! Cheesy
Journalisée
Yus Hors ligne
Relecteur et
Administrateur
*****
Messages: 322


Voir le profil WWW
« Réponse #2 : 15 Septembre 2007, 01:11:05 »

J'adore ce genre d'effets. Cheesy
Journalisée

http://iyus.info/images/Yus_signature.png
Il est dangereux de frôler les arbres, mais vous serez récompensé si vous prenez des risques.
f_RED Hors ligne
Jr. Member
**
Messages: 64


Voir le profil
« Réponse #3 : 15 Septembre 2007, 07:58:59 »

Trés chouette cet effet !!! Merci Mollusk !!!  Wink
Journalisée

Read, read code, code ...
Pitt Hors ligne
Administrateur
*****
Messages: 574


Voir le profil WWW
« Réponse #4 : 15 Septembre 2007, 17:15:26 »

Mouais ça a un air de déjà vu Langue
* Pitt est loin
Journalisée
raphzore Hors ligne
Full Member
***
Messages: 103


Voir le profil
« Réponse #5 : 15 Septembre 2007, 17:28:59 »

Démo style, miam  Afro
Journalisée
Alekmaul Hors ligne
Papi codeur et
Administrateur
*****
Messages: 890


Voir le profil WWW
Out of memory error ...

« Réponse #6 : 16 Septembre 2007, 08:56:22 »

Super, merci beaucoup de ce tuto Mollusk, vraiment sympa comme effet Smiley !
Journalisée

Mon site PortableDev : l'émulation sur GBA et sur DS
Adorateur Hors ligne
Newbie
*
Messages: 28


Voir le profil
Une nuit blanche devant l'ordi, ca nous réussi pas

« Réponse #7 : 16 Septembre 2007, 17:22:09 »

C'est impressionnant ce que tu fais.
Un si petit code pour un effet si beau. (Moi qui pensé que c'était méga dur à faire)
Ca m'apprend pas mal de truc ses demos meme si je mets du temps a bien comprendre le code.
Merci encore!!! Wink
Journalisée

Encore un bug!!!
Toiletking Hors ligne
Mega Member
***
Messages: 1103


Voir le profil WWW
"Caca Molluskien et kukulcanien de 1807 à 2008"

« Réponse #8 : 16 Septembre 2007, 23:10:53 »

Dis Mollusk, c'est pas toi qu'a développé demo maker sur Amiga? Wink

Non vraiment, la première fois que j'ai vu ca je me suis dit "putain, ca pète" Azn et j'ai pleuré dans mon oreillé! Cheesy
Journalisée
Mollusk Hors ligne
PAlib Guru et
Administrateur
*****
Messages: 3094


Voir le profil WWW
Ne vous posez pas de questions, codez !

« Réponse #9 : 17 Septembre 2007, 06:29:29 »

Euh... non Azn
Journalisée

http://www.palib.info/images/mollusK.png
Pages: [1]   Haut de page
Imprimer

Aller à: