Friday, July 22, 2011

Futurs pixel-artistes: ne vous découragez pas

Ca me fait toujours sourire quand on me dit que je "dessine bien". J'aime ça, mais faites un tour sur pixelation ou deviant-art, et vous verrez que "dessiner bien", c'est autre chose. Voyons un peu ce que ça donnait en '96

Une vieille (~1997) tentative d'amélioration du graphisme de Bilou. Je venais donc de finir mon "modplayer" 6 pistes en assembleur, j'avais racheté à Pascal son scanner Logitech et je m'attaquais à convertir le nouveau look de la "green zone" en pixels "prêts à l'emploi". Mes nouvelles connaissances sur le mode X aurait dû fournir le dernier élément pour faire de Bilou un jeu MS-DOS capable de rivaliser (hum hum ^^") avec Rayman "himself".

Sauf que n'ayant pas une équipe de 100 personnes avec moi, j'avais encore beaucoup à faire.

Here's an old file dating back from '97 where I saved the steps to convert the output of a scaled-down scan into pixels "ready for integration in a game". As you can see guys, don't feel disappointed if your pixel art is not yet on par with your favourite artist. Mine wasn't either (and still isn't, by a large extent). I'm quite glad I had already learnt that one should not use black artlines at pixel-art scale. A dark color is much better. I hadn't learnt yet that hue shift is the key to nice colours, and I was using "plain value ramps" (where you build linear gradients from a saturated color towards white and black, making everything just look like plastic). And of course, I was overusing automated gradients. That may have saved me from pillow-shading, but it makes the petal looks like spheres rather than having the curve they deserve.

Il y a pas mal d'erreurs de débutants dans ces graphismes. Sur-utilisation des dégradés automatiques de Deluxe Paint qui rajoute une texture granuleuse à tout (que ce soit le bienvenu ou non) et aucun "hue shift" : chaque dégradé part d'une couleur et en diminue linéairement les composantes RVB vers le noir. Point. Pour le jaune et le vert, l'effet est particulièrement raté.
En revanche, déjà là, j'avais appris à fuir comme la peste les "art lines en noir", quitte à refaire entièrement le contour. à la main. Il faut dire que devoir supporter "Skunny the Wild West" m'a ouvert les yeux sur l'horreur que ça donne au final. Et j'ai évité le "pillow shading". C'est toujours ça de pris.

Tuesday, July 19, 2011

update on BG colors.

So that's the tileset as you know it so far. Used in "greenwoods demo" and "Apple Assault". It took me a few more missed attempts while trying to blindly copy Eyecraft's colours but I finally got it working... And what made it work was to build up a mockup first (the small image nearby) with BG, FG and character tiles alltogether in such a way that contrast issues became obvious. Once in that setup, with a 32x32 area to focus on where all the important things were there, I could bring the BG colours down, almost to grey, although keeping the blues with a higher value (blues and purple = dark, cold shadows. That's how our bluesky world and cone-rod eyes work ^_^)
Ingrédients: un tileset pas bien coloré, un sprite de référence, un éditeur capable de cloner des couleurs et une pinte de bonne humeur

  1. Créer une nouvelle Sprite Page et y copier l'intégralité des tiles à recolorer
  2. Copier également les tiles qui doivent avoir un bon contraste et le sprite de référence (personnage principal du jeu).
  3. Superposer l'arrière-plan et l'avant-plan en utilisant sur-chargeant avec (B) en mode curseur plutôt qu'avec (A). Construire un mini-mockup avec les tiles ainsi obtenus.
  4. Sélectionner un des tiles d'arrière-plan, cliquer sur "SCAN"
  5. Clicker sur une zone de la palette encore vierge pour définir la destination des couleurs clonées, puis L-cliquer sur le bouton [+] du sélecteur de couleur (QuickPal). Clicker sur "SYNC" pour valider les nouvelles couleurs.
  6. Passer la grille en 32x32 et charger (R-déplacer le curseur-A) la zone entourant le personnage.
  7. Basculer en mode "édition de palette" (SELECT)
  8. Ramener les couleurs clonées vers du gris. Vérifier sur la zone de preview en gris que le contraste est suffisant.
  9. Clicker sur "okay" pour prévisualiser les modifications sur l'ensemble de la page, puis validez en cliquant sur "sure". Retourner à la grille en pressant à nouveau SELECT
  10. Si nécessaire, pressez "START" et utilisez les boutons "<<" et ">>" pour faire apparaître en vis-à-vis la page avant et après modification. Clicker sur un tile "d'avant" puis L-clicker sur un emplacement "d'après" pour effectuer le transport.

See that horizontal area between [+] and [-] under your spriting grid ? This is the QuickPal widget. when you click [SCAN], all the colours present on the grid are copied there. "cloning" the colors consist of copying those colours at an alternate location in the palette. You first pick that target location by touching the palette widget, then launch the cloning by clicking [+] while holding L shoulder button of your console. All the tiles on your working sheet (on screen) will then use the new colours. All the remaining tiles are unchanged, included the backup copy of your working sheet, meaning that you can reimport some old tiles if you need to do so.


PS: ce blog comprend maintenant exactement 600 posts (dont 55 brouillons :P)

Sunday, July 17, 2011

palette experiments

I owe EyeCraft quite a lot for his edit on my former "level 2", although the Apple Assault milestone did not allowed me to improve my own art to match this level. A first thing I wanted to experiment with -- even back then in 2009 -- was to alter the tint on the background dirt tiles.

EyeCraft m'avait proposé une révision assez épatante de ma petite forêt il y a déjà presque 2 ans. Comme je m'étais lancé dans "Apple Assault", j'avais plus ou moins laissé tombé ce genre de mises à jour pour me concentrer sur le moteur de jeu ... d'autant que de toutes façons, il y avait une "impasse technique" au niveau de l'éditeur de sprites.

Today's attempt was to allow the sprite editor to clone a set of colours so that tweaks done on the clones would not affect the original colours, as there are some shades of brown shared by the background and foreground dirt tiles. It's not yet perfect (it should only upgrade those colours present on the currently edited page), but it allows me to do some tweaking attempt... which turns out to be harder than I expected due to the "granularity" I have on colours. There's a large jump between "pure grey" and the closest "very desaturated hue" I can have nearby ... Having the desired contrast and brightness will need more trials

On voit déjà sur le niveau retouché par EyeCraft qu'au moins une des teintes du fond (terre) est aussi présente dans les "murs" de terre (et dans les morceaux de bois, au passage). Si je veux pouvoir changer la teinte du fond pour la rendre "plus froide" et avoir un meilleur contraste entre les deux, il me faut pouvoir "cloner" des couleurs sur la palette. Ca au moins, c'est chose faite, après plusieurs tentatives plus ou moins foireuses (modifications des pixels de tous les blocs pour utiliser les couleurs clonées ... ce qui rend les couleurs initiales inutiles ^^"). Il me reste à trouver les "bonnes" couleurs pour ce fond, ce qui, compte tenu des restrictions à 5 bit par canal (R/G/B), ne s'annonce pas sans difficulté.

Saturday, July 16, 2011

Implémenter les plate-formes.

Aah. De vraies vacances, ça fait du bien, même si ce n'est que quelques jours... qui se terminaient par un peu de baby-sitting à un mariage cet après-midi. L'occasion de refaire un peu le point sur les problème des plate-formes mobiles. Le point essentiel, c'est que -- tout comme le transport d'objets par Bilou -- ces plate-formes dépendent de la possibilité de lier un gob à un autre temporairement. Un micro-contrôleur "follow", donc, qui déplace p.ex. l'appleman à l'identique de Bilou.

At last some day off ... really off. Not even some Bilou thinking until this afternoon "_". And I had no documentation or current code state with me when doing so, which somehow explained why I focused on a fairly remote problem such as moving platforms. The last thoughts came to the conclusion that being carried would involve a dedicated "follow" micro-controller where a Gob is attached to another.

Be coherent
Comment s'assurer que la plate-forme aura déjà fait son déplacement au moment de déplacer ce qui s'est posé dessus ? Sans ça, on risque d'avoir des effets de "décalage" chaque fois que la plate-forme modifie sa vitesse, comme ceux qu'on peut observer dans Zool.

To make that work, however, it is mandatory that the carrying object has moved before the carried object's movement is evaluated. Otherwise, the player will experience zool-like glitches everytime the absolute speed of the carrier is altered. The FollowController must thus be able to detect whether there is an not-yet-processed carrier situation and execute anticipatively the "animation" of that carrier. That will require some more book-keeping at the core of Engine::animate(), but I think it'd be more flexible than pre-encoded priority levels.

Pour y parvenir, le mieux sera de faire en sorte que FollowController::think() puisse détecter si son objet-cible a déjà été exécuté ou non. On joue là au plus profond du moteur de jeu, qui appelle Animator::play() sur chaque objet qui s'est enregistré via reganim(). J'ai déjà une liste "pending" séparée de "todo", il me reste à noter au niveau de Animator lui-même où on en est. Après ça, il suffira de se faire un petit if (target.state==QUEUED) target.play(). L'objet-transporté voit alors son exécution interrompue le temps que l'objet-transporteur soit mis à jour.

Thou shall not follow the NULL pointer
Avec ce mécanisme, je vais introduire pour la toute première fois une référénce directe entre deux personnages du jeu. Jusque là, je passais systématiquement par le tableau GameScript::gob[] ou je me limitais à une interaction éphémère lors d'une collision. Si je ne fais rien de spécial, une plate-forme détruite pourrait amener les objets transportés dans un état incohérent, voire provoquer de sérieux problèmes au gestionnaire de mémoire ou planter sauvagement le jeu.

The second challenge I addressed is to ensure that we can handle the disappearing of the carrier transparently for the carried object(s). We can do that in different ways, some including list of cross-references, etc. but it looks like the best way around could be to delay actual Animator::DELETE requests by one frame so that the former carrier can be caught in DISCARDED state by FollowController::think() method that would drop then the reference.

Je pourrais exiger du "programmeur gobscript" qu'il règle ça lui-même en prévoyant des états-tampons ou des collisions de désolidarisation, mais ça signifierait qu'une erreur de script dans la machine d'état d'un objet pourrait se traduire en un dysfonctionnement du moteur lui-même... à proscrire à tout prix. Ca risque donc de me coûter un Engine::animate() un peu plus sophistiqué, mais l'idée est de garantir que tout objet "qui s'en va" sera présent au moins pendant une frame dans l'état "DISCARDED".

Wednesday, July 13, 2011

tcsh pour les experts...

Bin moi, mon script de prédilection sous Linux, c'est TCSH. C'est ainsi depuis qu'on me l'a "imposé" pour un TP (base de données ou NS2 ... va savoir). Sous SuSE8.2, ça se passait pas trop mal: bonne auto-complétion, coloration, aliases sympa ... Sous ubuntu, en revanche, c'est quasiment le minimex, au point que j'ai longtemps remplacé les fichiers /etc/profile.d/complete.tcsh et autres d'une vieille SuSE sur mes ubuntu fraîchement installé.

Mais ça aussi, j'ai fini par en avoir marre, donc maintenant, j'ai "fait mienne" toute la super-logique en réutilisant le fichier ".csh.expert" prévu pour permettre à un guru du shell (ou à ses sbires, comme moi :-) de contourner les préférences du sysop. Bin pas de bol, même là, les gars d'Ubuntu ont manqué à leurs devoirs. Alors du coup,


diff -burN /etc/csh.cshrc "/MY/etc/csh.cshrc"@@ -1,4 +1,11 @@
 # /etc/csh.cshrc: system-wide .cshrc file for csh(1) and tcsh(1)
+# Expert mode: if we find $HOME/.csh.expert we skip our settings
+# used for interactive sessions and read in the expert file.
+#
+if (-r $HOME/.csh.expert) then
+    source $HOME/.csh.expert
+        goto end
+endif

 if ($?tcsh && $?prompt) then

@@ -14,3 +21,10 @@
        set prompt = "%U%m%u:%B%~%b%# "

 endif
+
+end:
+onintr
+#
+# csh.cshrc end here
+#
+if ( -r $HOME/.tcshrc ) source $HOME/.tcshrc

Me voilà un peu plus chez moi dans cette ré-installation du Lynx sur mon disque de 2To :P

I've never done anything very fancy with MS.bat scripts, nor with Unix scripts while I was a student. I started really understanding what I was doing in scripting with TCSH, in my master years as a student, while running SuSE8.2. It was quite a good system to have TCSH on, with auto-completion, coloring, aliases and all. We don't get half of that on Ubuntu, to the point I usually tweaked /etc/profile.d on any UBuntu I installed.

But at some point, you grow tired of fixing that on every system you clone your home directory. And why would you when it turns out .csh.expert in your home directory allows you to override the sysop's preferences...

Saturday, July 09, 2011

per-socket statistics

Think twice before you pick the source of information about network behaviour…
Especially, /proc/$PID/net and friends are _not_ process-dependent as you might think. At least, not until your process got a dedicated _namespace_ to work with. So yes, every `struct sock` has a sk_net field that eventually leads to some statistics (`sock->sk_net->mib.net_statistics`), but if you look closely enough, e.g. by hooking one of the less frequent system calls and insert the following:

      {  // DEBUG CHECK
    struct net* n = sk?sk->sk_net:0;
    struct inet_connection_sock* ics = inet_csk(sk); // see net/inet_connection_sock.h
    struct tcp_sock* ts=tcp_sk(sk); // see linux/tcp.h

    printk(KERN_ERR “socket %p (#%i) has net@%p and stats@%p\n”,
    sk, index, n, n?n->mib.net_statistics:(void*)0xdeadbeef);

    }
You’ll see in your logs reports of various sockets addresses that all share the same net@xxxxxxxx and stats@yyyyyyyy … in other words, whatever you read there is not specific. Only if two sockets are in different namespaces (which could occur due to virtualization, chrooting and similar effects), they will have distinct statistics … if they’re from distinct process, that is. All the connections from your firefox browser will still share the same “fate” here.

So what should we look for ?

Well, give a look at the `tcp_get_info()` function, for instance. At the request of some `getsockopt()` call from user-land, it starts filling a `tcp_info` structure with information coming from the different “layers” of the `sock` structure. TCP retransmissions, for instance, are present in `inet_connection_sock.icsk_retransmits`. Running estimation of the RTT (yes, TCP does that for you) can be found in `tcp_sock.rcv_rtt_est`, and so on.

Always hunt for first-hand information.

Friday, July 08, 2011

moins rigolo, le développement DS ...

Redbug est passé sous iPhing, Quirk fait du portage de jeu Spectrum (?) sous Androïd, Stravingo ne s'occupe plus que de faire des produits dérivés de Hordes.fr et Mia s'est tourné vers la Dingux ... Ca sent la fin du développement DS tout autour de moi. Tout dev-fr.org est plus ou moins en hibernation depuis le passage de la PALib en mode "communautaire".

One former DS homebrewer turns his focus towards iPh*, another focus on Spectrum games port towards Androïd, another towards Dingux ... A few remain, but with projects that seems weird in my eyes. I have to face it: developing for the Nintendo DS is becoming a thing of the past for most people around me. The whole dev-fr forum is almost hibernating since the maintainer of PALib announced he'd now let "the community" deal with evolution of what was (despites its shortcomings) the corner stone of French NDS homebrew titles.