Mais pour mettre des graphismes dans un jeu, faire des graphismes ne suffit pas: il faut aussi qu'ils puissent être intégré au jeu. Il faut que l'artiste puisse le plus librement possible retoucher son animation, y compris faire grandir ou rétrécir certaines images.
L'idéal pour ça, comme l'expliquait hier Eric Zmiro -- concepteur avec plus de 20 ans de métier -- c'est d'avoir le système le plus automatisé possible. Voici donc ce que j'ai compris du système qu'il décrit.
From his explanations, I started to think about such automated framing tool, which is mostly a matter of separating lines that contain pixels from lines that contain none -- and having some conventions such as "an animation is bottom-aligned horizontally". Of course, there is still need to identify a reference position for each frame, so that e.g. the character's nose stays at the same position on screen while scrolling at constant speed, but a mere line of lone pixels could do the trick.
Sur base de la première planche de sprites, le premier algorithme à faire tourner est un système qui identifie les zones dans lesquelles il y a des images des zones vides. Une ou plusieurs lignes horizontale ne contenant aucun pixel (gris foncé): c'est une nouvelle animation. Une ou plusieurs lignes verticales ne contenant aucun pixels (gris clair) à l'intérieur d'une tranche d'animation: c'est une séparation entre deux étapes d'animation. On peut ensuite raffiner pour rétrécir verticalement la zone active d'un sprite.
1) on découpe les animations par planche (1 planche = 1 animation)Si elle me semble contre-productive, la décision "une animation par fichier" trouve tout son sens dans une équipe soumise à une date de sortie: sur une planche "prehistorik-man.png", on peut effectivement laisser le logiciel découper les bandes d'animations et faire référence à prun = pman[0][*]; pcrawl = pman[1][*]; pswim = pman[2][*]; pjump=pman[3][*]; etc. Mais imaginons qu'au terme de la production on décide finalement de laisser tomber le niveau aquatique. L'animation de nage tombe aussi et pjump devient maintenant pman[2][*]. Tous les autres animations doivent également être re-numérotée. On s'en passerait bien vu qu'il faut livrer la ROM fin de semaine pour que le jeu soit prêt début décembre...
2) on a un outil qui capture les sprites, les convertie en bloc
3) quand un bloc existe déjà dans la banque des bloc, il ne le rajoute pas.
4) pour chaque sprite indique : [x, y, flipXY, color_index, bloc_number, size] (j'espère n'avoir rien oublié)
Si deux sprites sont identiques, ils seront partagés. Mieux, certains blocs peuvent être partagé même si ils appartiennent a des sprites différents
Encore mieux, des blocs peuvent être identique apres flip, ou être identique a une transposition de palette prêt..
Yet, having the art split into multiple bitmaps doesn't mean that the tool importing them all cannot identify shared frames (or blocks) across the whole dataset. The final touch, for every frame the auto-cropper found, will be to isolate solid pixels into a minimum set of hardware-compatible blocks and pack the pixels of those blocks into tiles. The DS video chip, for instance, will only work with 8*W x 8*H blocks with W/H ratios of 0.5, 1 or 2 and a maximum size of 64*64 pixels. Even with a simple greedy algorithm, we can easily shrunk the amount of video memory required for a sprite to 1/3rd of the brute-force 64x64 sprite approach. Each batch of tiles has a companion set of OAM data that can configure hardware sprites' size, ratio position and indexes so that your 7 sprites actually look like a single, seamless cro-magnon protagonist to the player.
Reste à faire manger ça par le hardware d'une console comme la DS, qui ne fonctionne qu'avec des sprites hardware de 8x8, 16x8, 8x16, 16x16, 16x32, 32x16, 32x32, etc. jusqu'à 64x64. Sa mémoire est limitée, aussi donc avec un seul bloc de 64x64, le nombre de sprites affichables simultanément est sérieusement réduit. Ici, p.ex., on a un cro-magnon de 37x49 pixels qui prendrait 64x64. Avec un simpe algorithme de découpe "glouton", on peut le couvrir avec 4 blocs de 16x16 et 3 blocs de 8x16. Soit 6/16 de la mémoire nécessaire pour l'afficher d'un seul bloc.
AnimEDS travaille par petites éditions successives. Mais AnimEDS suppose que le contenu de la mémoire vidéo est constant. Ici, il n'y a aucun intérêt à garder un morceau de cro-magnon en mémoire si cette image-là n'est pas à l'écran. Du coup, tout ce qu'il faut c'est allouer un bloc de N tiles de sprites -- avec N assez grand pour la plus grosse étape d'animation -- et assez de blocs 'OAM' pour les contrôler. Pendant la synchronisation vidéo, c'est un nouvel ensemble "VRAM+OAM" qui est transféré simultanément pour changer les pixels que la console peut afficher et l'information sur quoi afficher où (les OAMs).
Mais je ne suis pas graphiste. Il y aura encore pas mal d'eau coulant sous des ponts avant que je n'accouche d'un personnage pareil ... donc je vais garder SEDS et AnimEDS encore un petit moment. Par contre, si mon moteur de jeu évolue dans le bon sens, on pourrait envisager un outil d'importation des .png en .spr qui ferait ce genre de travail pour faciliter l'importation de graphismes non-SEDS dans un jeu tournant grâce à libgeds.
chose amusante: le travail de la première passe ressemble en fait à un parser graphique et peut se construire sur base d'une machine d'états:
ReplyDeleteblank->data on non-transparent-pixel
data->blank on end-of-line [pixels == 0]
data->self on end-of-line [pixels >= 0] (pixels := 0)
sur base de ça, on peut imaginer utiliser p.ex. une ligne de "data" isolée comme "meta-data" (p.ex. pour les hotspots) alors qu'il faut 2 lignes ou plus pour que "data" devienne "sprite graphics"