Wednesday, May 29, 2013
The takeout is a lie!
Earlier this week, someone on pixelation wondered how one could earn money from flash games, e.g. on Konglomerate. I knew I read something about that from a blog, some time ago. Of course, googling for "flash money blog" won't produce anything meaningful. Searching "flash money" in google reader did provide a handful of posts and in one minute checking summaries, I retrieved http://www.lostgarden.com/2009/07/flash-love-letter-2009-part-1.html
Realising that, I figured out I'd like to capture *at least my starred items*, which tiny tiny rss didn't properly imported, afaik. Cross-posting them into
Tuesday, May 28, 2013
extractanim.pl
But that screencast tool (byzanz-record) shows its limits when one wants pixel art-level animation, and not merely a gameplay teaser: it's introducing dirty pixels all over the place.
Voilà un moment que je vous mets l'eau à la bouche avec "mon crayon qui marche", et vous n'avez toujours pas eu droit à une petite animation. Pourquoi ? eh, ben parce que je n'ai pas encore d'exporteur d'animations. Je m'en suis tiré jusqu'ici avec des animations bricolées à partir de screenshots ou des 'screencast' convertis en .gif animés tandis qu'AnimEDS tournait dans l'émulateur desmume. C'était sympa, mais l'outil de sreencast n'est pas complètement convaincant pour du pixel-art: il a trop vite tendance à rajouter du dithering dans tout les coins et des pixels parasites.
Du coup, je reprends ma doc de Imlib2 histoire d'extraire les différentes images d'une animation en .png à partir de ses descriptions puis on construira l'animation dans un 2eme temps.
Stay tuned ...
hmm ...
edit: Well, the tools have progressed, that's sure. I still need to hack support for 32x32 blocks, though. On the other hand, it's pretty screwed for the pendat: I used pencil tiles as a quick replacement for pencil sprites content, but that's not a valid approach in the game engine, and that isn't either for the extractanim.pl tool :P
But with the "apply change to this frame and all the following" button, replacing the page and recovering graphics is a matter of 5 minutes with the DS in hands ^_^
Comme vous l'avez deviné, le truc-machin en haut est tiré d'un des premiers essais de l'outil. Il a fait de son mieux, le brave, mais j'avais été pécher mes morceaux de crayons dans la spritesheet 'blocks' plutôt que dans une spritesheet 'sprites'. Et extractanim.pl
ne charge pas les blocks, évidement. Heureusement, j'avais ajouté une option "appliquer les changements à cette frame et toutes les suivantes" dans AnimEDS, du coup j'ai pu corriger tout à en 5 minutes DS en main.
Friday, May 24, 2013
Un grand pas en avant ...
Souvenez-vous: dans Apple Assault, si les pentes fonctionnaient presque correctement, il me restait une certaine probabilité que Bilou se "bloque" arrivé au sol, et ce n'est qu'avec la révision du moteur de jeu en Septembre dernier que le bug fut corrigé. Mais on était pas tiré d'affaire pour autant...
A force de refaire des tests pour m'assurer que Bilou ne puisse plus se bloquer dans les murs suite à un atterissage en catastrophe (en mode "pas à pas"), je finis par me rendre compte il y a quelques semaines que lorsque Bilou arrive au bord d'un livre (terminé par un tile pentu en bordure), il oscille quelques fois entre "marcher" et "attendre" avant de finalement se décider par sauter. C'est la plupart du temps presqu'imperceptible pour le joueur qui ne notera peut-être qu'un temps de retard, mais je me suis mis avec ce projet d'avoir un moteur de jeu irréprochable. Exit l'à-peu-près et les excuses bidon: si je ne peux pas être le moteur candidat pour Super Mario World 3 sur SNES, alors le côté "documentation de comment on aurait sans doute pu faire les choses avant de passer à la 3D" perd sa raison d'être.
Commençons par un rappel du fonctionnement des pentes dans mon environnement découpé en pavés (les "taïlze/tiles"). Bilou n'y est qu'un rectangle qui doit pouvoir naviguer sans rentrer dans les tiles solides (noirs). Il possède en plus un point de référence (hot spot, en rouge vif sur l'image) qui doit rester en contact avec le sol lorsqu'on suit une pente. Pour avancer d'un pixel vers la gauche, le hot'spot passe d'abord dans un tiles complètement vide (gh=0) où il n'y a pas de sol à suivre. La fonction do_slopes détecte ça et teste du coup le tile situé juste en dessous. Son pixel le plus à droite correspond à une hauteur gh=-8. On se retrouve ré-aligné sur le prochain pixel ... tout va bien.
Le problème apparaît seulement lors d'une transition pente/trou, comme j'en ai ajouté sur le bord des livres. Ici, lorsque'on regarde sous le tile vide, il y a ... un tile vide. Pour do_slopes, celà signifie qu'il n'y a plus de pente à suivre. Idéalement, on devrait donc juste se retrouver "le long de la ligne bleue" inférieure, qui empèche Bilou de tomber jusqu'à ce qu'il l'ait complètement quitté. Mais voilà, à ce moment-là, on est pas encore sur le sol! Du coup, lorsqu'on teste s'il est possible de tomber d'un pixel, la réponse est "oui" et le contrôleur envoie un FAIL.
Seulement, voilà, l'échec du contrôleur n'implique pas forcément une chute. C'est à travers les transitions du comportement de Bilou que les choses vont maintenant se jouer. Tomber d'un pixel ne serait pas impeccable, mais celà nous conviendrait. Seulement, pour celà, il faudrait que le point-test placé au sol indique du vide ... et étant décalé sur la droite, il est toujours dans la pente! Du coup, c'est vers l'état "do_slopes. C'est ainsi qu'on parvient finalement à quitter le livre après ce qui semble être un instant d'hésitation.
à l'arrêt" (idle) que Bilou passe. Mais puisqu'on a toujours le DPAD incliné vers la gauche, on quitte dès l'image suivante cet état pour tenter à nouveau une marche... qui échoue encore. Heureusement, à chaque échec, on avance d'un quart/demi de pixel qui correspond au "mouvement entammé".
Une fois que l'Inspector Widget est devenu assez précis pour faire ce genre d'analyse, il devient assez évident que la solution est de forcer le personnage à s'aligner sur le sol quand on quitte une pente, et de ne considérer qu'on chute que s'il est possible de descendre alors que le personnage est déjà aligné sur ce qui devrait être du sol.
Pour faire bonne mesure (et pour éviter que Bilou ne se retrouve en suspens sur un bord de livre parce qu'il n'a pas sauté assez haut), j'ajuste enfin le saut à travers une plate-forme à sens unique (les branches de Apple Assault): on ne peut plus atterir sur ce type de plate-forme que lorsqu'on vient d'au-dessus de la plate-forme.
Tuesday, May 21, 2013
Animating with AnimEDS
L'animation du crayon soldat est assez longue -- 16 images -- et n'aurait clairement pas eu ce nombre d'étapes avec le moteur d'Apple Assault. Pourtant, il n'y a que 4 étapes-clé (mise en évidence sur l'image) qui sont celles que j'aurais eu tendance à dessiner sans AnimEDS. Vous noterez aussi assez rapidement qu'un grand nombre d'images sont très proches l'une de l'autre.
En fait, pour donner un air "martial" au crayon, je passe le moins de temps possible entre deux états quasi-fixes "pied au sol" et "pied en l'air" (comme l'image 01). Par contre, pour éviter que ça ne se traduise en une animation saccadée, il y a un léger "temps de freinage": 3 étapes donc, ou le pied bouge d'un seul pixel d'avant en arrière. J'avais utilisé le même style d'approche avec la course de Bilou, et je crois que ça va devenir peu à peu mon style préféré.
Thursday, May 16, 2013
"J'vais chercher mon frère à la guerre"
replace emulator screenshots with mobile phone real shots --
replace emulator screenshots with mobile phone real shots -- I shall not ...)
Tuesday, May 07, 2013
One must FAIL.
The behaviour of Bilou and NPCs in the game is ruled by some
iGobController
classes, such as DPAD processing, momentum, gravity and the like. At each frame, each of them think() to evaluate the current game state and adapt the character's state accordingly (e.g. make it fall faster in the case of gravity controller).The outcome of this "
think()
" function in turn defines whether some transitions on the state machine should be evaluated. It may either be NONE
(no transition involve, we happily stay in the current state), EVENT
(something special happened, like the direction reverting or some key being pressed, for which an change in the animation might be desirable), and FAIL
(impossible to hold the current state for longer, e.g. we've landed on the ground, there's no further falling possible).
All in all, the remaining code is killing speed where it should better not, and obviously not killing it where it would have to. That smells like some legacy code that need to be re-thought from cleaner base.
[done]
FAIL
=> can't do more than this frame in this state. (but move isn't necessarily cancelled)[done] no longer sticks on walls
[todo] hspeed is reset to 0 when Bilou leaves a platform. That should only be the case if forced so by a
GobExpression
[think] smoother slope-to-fall transition required.
Wednesday, May 01, 2013
Inside the Inkjet
En premier, il me fallait un moyen de forcer les sprites à clignoter de manière sélective, et dont la définition se ferait au niveau des scripts. C'est pour un test, je ne fais pas dans la dentelle et j'introduis simplement un nouvel opérateur dans les expressions de changement d'état qui modifiera cette propriété de clignotement. En quelques essais, je peux rendre Bilou semi-transparent lorsqu'il s'est fait touché. Encourageant.
Mais je ne veux pas que l'entièreté de Inkjet clignote: seulement le "patch" destiné à masquer Bilou lorsqu'il est à l'intérieur. Je change donc la sémantique de cet "opérateur-clignotant" pour qu'il reçoive plutôt des bits de contrôle et laisse le rôle de "compteur" à une variable accessible depuis les expressions du script. Je peux du coup faire clignoter seulement le corps de Bilou ou une de ses mains ... tout va pour le mieux.
Par contre, le code permettant de modifier l'ordre d'affichage des composants d'un sprite (les OAMs du hardware, donc) n'offrait jusqu'ici qu'une modification interne au sprite, faisant passer une main devant le corps, etc. mais sans permettre de placer un composant devant Bilou et un autre derrière. Or, c'est essentiel pour "entrer" dans l'encrier.
C'est le comportement "global pull" qui utilise les mêmes commandes "pullmask" qu'auparavant, mais qui force maintenant un changement de zlist pour les composants tirés vers l'avant-plan. Le code du moteur de jeu n'était pas tout à fait prêt pour ça.
That being said, I definitely want to take the time to clean up the interface and provide some coherent way to define the (pullzero?, flickermask, pullmask, flickertime) variables in a cleaner way.
Il m'aura encore fallu un ou deux temps de midi pour comprendre pourquoi ce damné encrier refusait de tenir compte de mes commandes pour continuer à clignoter. En fait, il restait un bug datant de l'introduction des évènements-par-contrôleur. Qu'un seul contrôleur voie sa liste d'évènements prise en compte lorsque deux (ou plus) disent simultanément "évènement", je pouvais m'y attendre. Qu'un "évènement" et un "impossible" se traduise en "réinitialisation", franchement, je ne l'avais pas vu venir. Il aurait été plus "pro" de concevoir des cas de tests suffisament riches au moment où cette nouvelle fonctionalité a été ajoutée. Seulement voilà, un pro code aussi en-dehors des temps de midi :)