dimanche, juillet 31, 2011

Sonic Camera Management

Le suivi de Sonic par la caméra fonctionne donc sur le principe d'une "zone" de focus dans laquelle le centre de Sonic est confiné. Selon qu'il est au sol ou non, la taille de la zone change, mais surtout, dès qu'il en sort, la caméra se déplace d'autant qu'il faut pour y ramener Sonic (avec tout de même une vitesse limite qui permet parfois à Sonic de "larguer" la caméra... et le joueur).

La grande différence avec le code dans Bilou, c'est que jusqu'ici, quand Bilou sortait de la zone de la caméra, j'accélérais le déplacement de la camera, pour ne le décélérer que si Bilou débordait à nouveau de l'autre côté, ce qui amenait des "oscillations" particulièrement gènantes en cas de saut, car l'écran allait continuer à monter de plus en plus vite alors que Bilou commençait déjà à redescendre.

Voyons un peu si ça colle avec mes "désidératas".

A state-dependent "allowed onscreen window" and a speed limit. That's all you need to get sonic-perfect scrolling. That is, the area of the screen where your character is allowed to appear vary depends on whether you're on the ground or on-airborn. I cross-checked with my requirement list for an ideal scrolling, and everything is there (further translation to come soon).

Centré au sol et sur la chute
Presqu'automatique si la zone de tracking est "applatie" à une seule coordonnée lorsque le personnage est au sol. La caméra enchaînera "presqu'immédiatement" pour suivre la chute si le bas de la zone autorisée pendant une chute est très proche de cette position de référence au sol.
My first requirement is that the camera tracks precisely Bilou (vertically) when he's walking so that climbing slopes happens smoothly, but that it starts scrolling down quickly when a fall starts. This achieved by a very narrow Y windown for "on-ground" state, and keeping bottom limits close to each other when falling.

garder un oeil sur ce que l'on évite en sautant
Ce sera le moyen n° 1 de se sortir d'une situation délicate dans un "platformer": sauter par-dessus. Ce mouvement doit pouvoir se faire sans déconcentrer le joueur. On doit pouvoir passer de plate-forme en plate-forme (alignées horizontalement) au-dessus d'une mare de lave sans attraper le tournis. Simple aussi : il faut qu'un saut tienne en entier dans la hauteur de la "zone de non-scrolling" à partir de la position de repos (au sol). Ce n'est qu'en enchaînant les sauts que l'on parvient en haut de l'écran (et donc que l'on active le scrolling), mais dès qu'on retombe d'un de ces sauts, le scrolling cesse de monter jusqu'au saut suivant (objectif #5).
Jumping is the #1 action in a platformer like Bilou (that is, where the hero has no weapon). It must be possible to hop from one platform to the next one without having the scrolling "bounce over and over" as well (which would give the player nausea). That's achieved with a fairly high "top limit" when the player is "on air". Yet if the player is "climbing up", he'll be kept on-screen. always.


Recentrer sans à-coups
De nouveau, ça tient au fait que le scrolling va ajuster la position de la caméra à la position du héros mais avec une vitesse limitée. Quand Bilou arrive au sol, on "rétrécit" à une ligne la zone de tolérance verticale de la caméra. Celle-ci va donc recentrer Bilou, mais en donnant une vitesse verticale maximale relativement basse (de 6px/fr pour Sonic alors que la limite horizontale est de 16px/fr), ce recentrage semble plus naturel.
Now, that's okay to have the scrolling "keeping an eye on the danger from below", but when the action "moves on" on the new platform, it's annoying to press UP or DOWN just to move yourself back in the center of the screen. This can be safely achieved by a narrow "on-ground" Y window, but a slow speed limit on the camera (in my case, "slow" will be 1px/frame).

Chouette. Tout y est. Plus qu'à coder tout ça. GravityController ou WalkingController auront donc la responsabilité de définir la "fenêtre autorisée" et la vitesse maximale, la caméra s'occupe du reste. Andiamo!

mardi, juillet 26, 2011

DSertyuiop

Parce que je vais re-travailler AnimEDS pour éviter les pertes de données, et que pour ça, je dois utiliser plus largement ABXY et un peu moins L et R, voilà un petit pense-bête de leur emplacement sur desmume-cli... quand on a un clavier AZERTY.

I'd really be happy if people using QWERTY keyboard around the world and developing e.g. games or emulators could take note that other keyboards also exists. Playing Doukutsu, Frogatto or Iconoclasts with W and Z swapped or Q and A swapped is a pain. It's obvious desmume-cli was also targetted at QWERTY given that if I swap things, I fall back to a "logical" layout with A nexto B, X nexto Y and L nexto R. But I have an AZERTY keyboard, hence the cheat sheet.

Sonic Physics Guide

ça faisait un moment que je n'étais plus tombé sur de la lecture chouette comme celle-là. "pentes & blocs à pousser", "collisions", "gestion de la camera", etc. Ca va me faire de la lecture ... Attendez-vous à ce que je vous en dise d'avantage dans les prochains jours.

A neat pick: the Sonic physics Guide starring handling of slopes, curves, camera movement ... knowledge likely generated from years of hacking the good'old Sonic 1 and Sonic 2 roms (for SEGA Genesis). Expect some more detailed posts when I'll be done with my readings.

samedi, juillet 23, 2011

Entering the Shadowlands ...

Episode 4 of Commander Keen is not far from fascinating me. That's likely the game I played the most (excluding beta-tests of PPP Team productions), and likely the game I'd love the most to replay once again. Of course, I'd love that some people would once say that about Bilou adventures as well, and somehow I hope I can learn something from CK4 that would help me reaching that goal.

Si je reprends la carte de Commander Keen 4 et que j'y reporte les différents éléments, on se rend finalement compte qu'il n'y a que 2 niveaux "gratuits" (c.à.d. qui n'apportent rien au niveau de la quête). Autre élément intéressant, dès les 2 premiers niveaux passés, la quasi-totalité du reste du jeu devient immédiatement accessible. Et ces deux premiers niveaux peuvent être franchis d'une traite par le joueur habitué tout en servant d'introduction à la majorité des mécanismes du jeu pour les autres. Une chouette manière d'éviter les "tutoriels".

I think one interesting aspect of CK4 is its overworld. It's mostly a select-your-level menu, and your goal should be to focus on those levels where a Guardian of Wisdom is being held captive. Of course, you don't initially know where they are, so there's some exploration dimension. The overworld is large enough so that you'll rarely see more than 3 levels on screen, but small enough so that you can travel it end-to-end in something a dozen of seconds.
During some spare time, I lost some time plotting which levels contain what "quest items". Guardians to be saved (red wizzrobes), hints from Floating Princess Lindsey that ultimately guide you to the Mighty Foot of Travel which can take you to the Pyramid of the Forbidden -- the otherwise unaccessible level. The major item is the swimming suit, hidden in Miragia, the disappearing desert city, which unlocks access to the three islands. Only the Isle of Tar and Sand Yego are "free fun levels" where you've nothing to earn (quest-speaking), not even a lot of lives or ammunition. They're rather an extra challenge, with fun-but-dangerous elements.

Beside this "swim lock", there's two gate levels (villages) at the start of the game, that can imho be seen as the frontier between basic skill acquisition and real danger. Both can be zoomed-through by the experienced player (just move to the left, hop over some monsters or spikes and you're done). It's interesting to see how the novice player is even -guided- through the sandbox area either by a friendly slope or an open door vs. a set of spikes.

For the novice players, they're a great introduction to the basic mechanics of the games, in a free-exploration fashion. Poles, doors, shots, switches ... everything is there, except key gems. It's interesting to see that a sandbox works as well as a tutorial to that regard. Of course, Commander Keen does not learn new moves during the game (vs. Rayman) and his move set is nuanced (ultra-pogo jump or rope-hop-while-blasting-downwards being examples) rather than complex (vs. Yoshi's Island or Super Princess Peach -- complexity arising from the number of possible actions and negative rewards from doing the wrong action at the wrong time).


Tout qui a joué à un Keen sait qu'un niveau ne peut être visité qu'une seule fois par partie. Sortir d'un niveau sans avoir trouvé le secret qu'il contient, c'est perdre définitivement ce secret jusqu'à ce qu'on recommence le jeu. Evidemment, pour éviter de bloquer le joueur, les mages seront le seul moyen de sortir d'un niveau quand ils s'y trouvent. Ce ne sont donc que les indices de Lindsey et le méga-pied-transporteur de la pyramide de la lune qui pourraient être loupés. Mais le résultat, c'est qu'on aura d'avantage tendance à explorer les moindres recoins du jeu, là où, dans Super Mario 3-*, j'ai davantage tendance à filer vers la sortie dès que les choses se compliquent, entre-autres parce que je sais que je pourrait refaire ce niveau-là plus tard (avec un meilleur power-up ou un peu plus de vies ...). Pourtant c'est bien Keen 4 le "platformer" auquel j'ai le plus joué et celui auquel je serais le plus succeptible de refaire une partie prochainement ^_^

Through the Commander Keen series, levels can be completed only once in a game: you never re-enter a completed level. That means if you complete a level holding a secret without finding it, you won't have the chance to get that secret before you replay the whole game. Imho, this is a key component that puts the player in an explorative mood, while you'll rather play a SuperMario game trying to zoom to the exit as soon as you don't feel easy in the level. The overworld reinforce that explorative mood by being itself open (once you've complete the two gates level at the start, 11/14 remaining levels become immediately accessible).

The overworld of the next opus -- the armaggedon machine -- is much stricter, with virtually every level being a gate to something else. There's very little room for exploration, just sufficient alternate ordering between the tunnels/balls pair to allow the player who faces a challenge beyond his skills at one place to try something else somewhere else. I spot that the two "mission-less" levels of Commander Keen 2 are also the ones where important plot/gameplay hints are unveiled. I'll investigate that further on another time.

vendredi, juillet 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.

mardi, juillet 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)

dimanche, juillet 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é.

samedi, juillet 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".

mercredi, juillet 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

samedi, juillet 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.