Wednesday, June 24, 2009

Je vous parle d'un temps que les moins de 20 ans...

Bien avant Internet, du temps où les diskettes étaient molles et que les écrans étaient le plus souvent monochrome, je programmais déjà. Petit retour sur la version "BASIC" de Bilou en image, stimulé par le fait que j'ai utilisé la "distribution" de Bilou pour QEMU comme .torrent de test dans les TPs de mes étudiants (pas de téléchargement proposé, parce que je me suis rendu compte que certains paramètres y étaient encore incorrects :P)

There was a time were the network wasn't interconnected yet and had to travel the real world enclosed in sectors of floppy disks that were actually floppy. Screens had usually no colors but I was already coding. Here are a few shots of those coding sessions, with the BASIC version of Bilou's adventure.

Tout d'abord, construire le niveau lui-même. A grand coup de "Data" qui sont lues séquentiellement à partir d'un autre point du programme. Une sorte "d'Embedded File System" avec ses avantages et ses inconvénients, l'inconvénient majeur étant sans doute l'impossibilité de construire son niveau autrement qu'au clavier.

Vous reconnaissez la scène dont la capture d'écran est sur ce blog depuis des mois ?

Venait ensuite la 'routine de gestion de l'écran', quelque-part au coeur d'une énorme boucle du genre:

  • lire les DATA et les afficher
  • select CASE ecran
  • ajuster la position de Bilou et boucler

A level was built out of 'DATA' lines that encoded pixels or tile arrangement for a room. But that wasn't quite enough. Over the background layer, I had small chunk of code that placed interactive graphics, and a loop that animated every object on the room, tested for collisions with bonuses, bumpers, etc. and "doors" area towards the next screen. Yes, every room with its own game loop. Never do that again, kids.

Cette "routine" va dessiner tout ce qui n'est pas décor (ennemis, bonus, dégradés, etc) avant de traiter en boucle le comportement de ces objets avec des sous-routines (ouf. Il y en a quand-même) genre "move" pour Bilou, "pomme" pour tester si bilou touche une pomme, "gold" pour tester s'il touche une pièce d'or, etc. Vous avez vu cet horrible hack ? répéter n fois ce qui ne dépend que de Bilou avant de passer au test de timer histoire que Bilou puisse effectivement se déplacer plus vite que le petit ver. On a échappé au 'FOR I = 1 TO 1000 : NEXT', ceci dit.
Des choses aussi élémentaires que "suis-je mort" ou "faut-il passer à l'écran suivant" devaient être répétées pour chaque écran. Wéééééé. :P

Allez, un autre petit écran. Je me suis demandé en reparcourant le code "mais enfin, pourquoi diable ce "if ... then mort". Bin parce que sinon, Bilou pouvait se promener allègrement dans les pics dessinés un peu plus haut. J'aurais pu trouver autre-chose... une couleur mortelle, par exemple.

Au passage, il n'y avait plus de sprites dans le QuickBasic (pas plus qu'en EP-BASIC). Tout fonctionnait par copier-coller entre l'écran et des variables tableau (ce que les amigaistes appelaient sans doute le "blitting", mais qui en l'occurence était du PUT et GET. Concept farfelu, ces "copies" pouvaient faire appel à un opérateur booléen pour combiner les pixels existant et ceux du buffer. Je suis parti de jeux monochrome où j'effaçais le personnage du fond noir en le "XOR"ant avec lui-même pour finir par utiliser un système de masque en deux passes (AND/OR). En l'occurence, dans ce jeu-ci, j'avais droit à 15 couleurs de fond (respectant le pattern xxxx1111) et 15 couleurs de sprites (0000yyyy) de sorte qu'un "AND" bit-à-bit entre un sprite et le fond affichait le sprite, et une couleur automatiquement transparente (255). De tous mes hacks, je crois que ça reste le plus tordu. Toute tentative de programmer sa propre routine d'affichage en BASIC étant évidemment inimaginable, car d'une lenteur affligeante.

Figurez-vous que même les tests étaient lents. Je veux dire les "if-then-else" Du coup, j'essayais dans mes routines de comportement (appelées plusieurs dizaines de fois par seconde quand tout allait bien) d'en utiliser le moins possible. En témoigne ce genre d'expression optimisée -- surréaliste aujourd'hui -- qui utilisait le signe de la différence de position pour ajuster la vitesse du "Bubble Bat". Il m'en reste encore des séquelles aujourd'hui (cf. Coding Funky Funghi).

Monday, June 22, 2009

*deline

Ce blog risque fort de prendre quelques "semaines sabbatiques". Après 9 mois de préparation, la petite "*deline" devrait montrer le bout de son nez dans les jours qui viennent. Je suis plutôt accaparé par les derniers préparatifs.

My blog is likely to slow down for a few weeks. We've been waiting for *deline during almost 9 month and she's likely to pop up in the next few days. All my 'free' time is somehow absorbed into painting, cleaning, shopping and similar activities.

Wednesday, June 17, 2009

Bilou on DS [en FAQ]

p1370647 I guess it is time to state clearly what the ultimate project "Bilou Homebrew" is. For a couple of years, I've been blogging development progress in an attempt to build a game dubbed "Bilou's adventure" on the nintendo DS. It is actually a revival of my teenager project (on PC/Amiga by then). I compiled a little FAQ list that should cover the big idea, trends and similar stuff for those who just stumble upon the blog and want to know more about the background and motivations.

Les lecteurs francophones ont déjà la version originale de cette FAQ dans un article d'hier.

Who is Bilou ?

A fictional character -- a blue ball with googly eyes, and "surrounding" feet and hands -- used as the heroes of my video games. I usually consider that I borrowed his initial design from my friend Pierrick, and over the years, I've built a whole little cartoon world around him, with influences from Lewis Carrol's Wonderland, early cartoons together with Commander Keen and SuperMario. Oh, by the way, it's a French name, to be pronounced "Bee-loo" (/bi.lu/).
Bilou is around since 1993, and a good deal of previous games (including a QuickBasic platformer and an RPG) are also covered in the "history" thread of this blog. Bilou teams up with Bouli, the yellow guy with a red hat, as a freelance space explorer for the Stellar Convention. Bilou is the AstroCruiser's pilot and Bouli the ship's engineer.

Why on nintendo DS ?

The DS is the computer I'm dreaming of since my early C64. It has hardware sound mixing, multi-layer graphics with hardware sprites, and much more. It has a touch screen just precise enough to allow graphic applications and game controls inherited from the SNES (imho, the best game controller ever). WiFi and SD cards (when homebrew-ready) makes it ready to communicate with the rest of the world, too. It might be a little scarse on resources with "only" 33+66MHz and 4Mo RAM, but imho, it just puts the coder at challenge to make things efficiently and to thing small enough in a world where over-provisioning kills the beauty of optimization. Still, unlike former 2D game consoles (SNES/Genesis), it still allow a nice environment and do not *require* optimization of every little bit.
And most of all, it does exists. If i was to code my stuff on anything else, chances are that i would first re-implement a virtual DS.

Admittedly, the hardware will turn old-fashioned before I'm done, but I bet there will still be people enjoying 256x192 display when that will happen. The game logic is platform-agnostic and most of the game engine equally is, btw.

What kind of game ?

My favourite genre is side-scrolling platformer, so Bilou will be a platformer. The initial vision is a grand adventure game, with a whole planet to explore, myths, mysteries and epic fights. That being said, I'm still undecided on the gameplay I'd love to give to the game. Will it be a Sonic-like race ? A Fury-like puzzle or a Johny Biscuit arcade puzzle ? Will it provide Keen-like exploration or Cave Story-inspired RPG ? I cannot tell. Chances are that I'll be investigating all those aspects in the future years, through standalone demos showing only a few levels at a time. Not only it will deliver fun to fans & players, but it allows me to keep on moving. The arcade-inspired Apple Assault is such a first milestone on the long road I'm walking on. I will divide and conquer, one area at a time. I will experiment and, hopefully, draw lessons.


Can we give it a try ?

Yes, we can. Small "milestone" demos are proposed along the path and advertised in the "downloads" section of this blog. Keep in mind that these are always "work in progress". I don't have a very large testers community at hand (only a few friends have homebrew-ready devices, often the exact same hardware as myself), so a fresh look at how things should be is always of interest. It might not always be possible to follow such advice, though. For those of you who don't have a DS, demos should run in emulators as well (they're tested on Desmume).

Where can we find some videos ?

With running demos at hand, i'm less and less inclined to take the time to shoot pictures or videos of my work. It takes an awful lot of time that I could instead give to programming the game, and it's incredibly difficult to have a decent quality with the tools I've got. That being said, I'm interested in any picture/video you've shot and put online here or there. Just link to them with a comment in the corresponding demo.

How do you realise such a game ?

I implemented a fresh game engine in C++, and reused 0xtob's C++ modplayer (NTXM, from the NitroTracker project), thanks to the "devkitpro" environment in Linux. A few tools on the PC side are developed in PERL, as well.
I intend to keep the game logic apart from the engine thanks to a custom scripting "language" inspired from former successes such as Another World or SCUMM. The script only take care of "events", letting the C++ part handling the boring work of detecting collisions, moving sprites, etc.
One of the corner stone of my development kit so far is runme, a home-made swiss knife that let me transfer files between DS and the PC over wifi, view them or hear them ... It also contains the game engine so that it can run a level, launch the editors, transfer some altered ennemies behaviour and run the level again. If the game engine itself needs an upgrade, runme (and the editors, SEDS and LEDS) can download a new copy of itself and "reboot" in a few seconds.

Will I ever find Bilou in a store ?

Unlikely. I'm not targetting a commercial release and I don't think it would make my life any better if I did. Nintendo is virtually throwing independent developers away and the video game industry has become a world where economic efficience dictates the rules. This is the opposite of "garage coding" I've known and cherished during my childhood. I prefer follow outsiders such as Cave Story or Qwak, do something i'd love to have. Maybe I will one day have a few units produced somewhere in Asia and sell them on Internet, like what happened for Qwak on GBA. Time will tell.

What about PC / Wii / Xbox ? What about 3D ?

Equally unlikely. I've never been seduced by the control of a virtual avatar in a 3D environment as a player, nor by the look of a 3D world (My favourite episode of Rayman ever is episode #1 starring Mr Dark). Moreover, 3D games requires another set of programming techniques, development tools and animation skills that I don't have. I usually find PC game development libraries as boring. I've tried that path for some 10 years and it just resulted in yet another revolutionary Operating System project that fell asleep.
That being said, i'm curious of how i could possibly add 3D items in a 2D platformer. New Super Mario Bros has opened an interesting path in that direction.

Open Source ?

The game engine and game development tools I'm working on in the "Bilou on DS" game project are open source and freely available in the "dsgametools" sourceforge project. My goal is to get as close as possible to a "game maker" suite fully running on DS: level edition, graphic editors, character / ennemies behaviours, etc. In a few years, you could use them to write your own games as well.
Yet, I keep my intellectual property (that is, copyrights) on Bilou's universe and all my pixel art. I don't want that X starts working on a "follow up" game, or see any graphical content in a Flash game by Y ... That's the reason why I put demos online in a "all-in-one .nds binary package", to avoid having my work ripped. I kindly ask you to respect this choice. If you find yourself fan of my work so hard that you want to provide fan-made level packs, the "dsgametools" should let you sketch your ideas on a "sketched-by-yourself" version of the environment. We can later discuss your prototype/ideas and see whether a cooperation is possible.
I know the true fan will understand and agree with my choices.

What experience of video game do you have ?

I've never been a professional game developer, if that's what you meant. Yet, it's now been over 20 years that I'm developing games, starting with simple Quizz in C64 Basic, then including two platformers and one "RPG" in QuickBasic for DOS. Two of these were featuring Bilou as main character. I entered 100k game competition on the Belgian demoscene with two arcade games embedded in my own x86-assembly module player, in 1999 and 2000, and got the 1st place each time -- mostly due to the lack of serious competition, I must say. That's for programming. With a few friends, collectively (un)known as "PPP Team", we also designed a dozen of platformers for MS-Dos using Recreational Software's Game-Maker. If you're curious about this, just read the "pppteam" thread of this blog.

How can we contact you ?


Just leave a comment on any of these posts. Comments aren't shown publicly until approved by myself and I get periodic notifications when new comments are awaiting for moderation. If that's not quite fine for you, feel free to send your mail to pype_1999.geo @ yahoo . com

gun.shoot(this)

Tout comme les contrôleurs, les "générateurs-d'objets-en-cours-de-jeu" (baptisés iGun, pour faire simple) sont des classes C++ paramétrées par le script et associées à certains états ou transitions pour ajuster le comportement du jeu.
Pour les contrôleurs, je les avais associé à l'état courant. Facile (presque trop). Pour les "guns", j'hésite. Le script ? une transition ? un objet graphique ? Qui aura accès directement à la classe, et qui n'y accède qu'à travers les méthodes d'un autre ? C'est le genre d'indécision que je déteste en POO, typiquement parce qu'en programmation impérative, le problème ne se pose pas.

  • C'est au niveau des transitions que les guns sont utilisés. Je peux éventuellement capturer ce qui m'intéresse lors du parsing.
  • Je n'ai droit qu'à 16 guns dans une 'palette' d'effets
  • Je veux éviter de devoir ré-instancier des guns identiques (contrairement à ce qui se passe pour l'instant avec les contrôleurs où je dois avoir autant de GravityController que d'états affectés par la gravité).
Not quite "analysis paralysis", but still not enough time after lunch to figure out the best way to add iGun into the object-oriented game engine. It is used by Expressions (after all, guns are used to shoot new GOBs into the playground), but it's only one of the 16 guns an expression has, and it is likely to be shared by many states. What scope should it have ? which object will 'host' it, etc.

I'm undecided. I'll have to leave it for further code refactoring.

Bref. Je devrai revoir cette partie-là un autre jour: mon temps de midi touche à sa fin...

Tuesday, June 16, 2009

Bilou sur DS

p1370645 Ce billet est une tentative de FAQ reprenant une partie de mes discussions ou de réaction aux commentaires récents de nouveaux arrivants qui ne m'ont pas vu gribouiller des p'tits Bilous dans tous les coins depuis des années.
An English translation of this FAQ is available here.

Qui est Bilou ?

Un personnage imaginaire -- une balle bleue avec de grands yeux, des pieds et des mains "en orbite" -- servant de héros à mes jeux vidéos, que je tiens initialement de Pierrick, et autour duquel s'est construit tout un petit monde "cartoon" où l'on retrouve des influences de Lewis Carol et des premiers dessins animés, mélangées à du Commander Keen et du SuperMario.

Pourquoi sur DS ?
Parce que la DS est la plate-forme dont je rêve depuis que j'ai un C64. Mixage son en hardware, graphisme multi-couche et support des sprites en hardware. Elle combine un écran tactile assez précis pour des applications graphiques et des contrôle de jeu à la SNES idéaux. Elle a des possibilités d'interconnexion avec le reste du monde et du stockage local pour peu qu'on se donne la peine de les faire fonctionner. Elle est un peu restreinte en resources (33+66Mhz et 4Mo de RAM), ce qui oblige le dévelopeur à chercher des solutions plutôt que de coder comme un bourrin, mais elle a quand-même assez de puissance pour ne pas nécessiter que _tous_ les aspects ne nécessitent une micro-optimisation pour pouvoir faire quelque-chose d'intéressant comme une SNES ou une Genesis. Ses possibilités 3D ne sont pas extraordinaires, mais si jamais je veux m'en servir, elles sont mieux documentées que les nvidias.
Enfin, elle existe. Si je devais coder mes jeux ailleurs, il y a de fortes chances que je commencerais par me donner l'équivalent de la DS en machine virtuelle.

Quel genre de jeu ?
J'ai un faible pour les jeux de plate-forme, donc ce sera un jeu de plate-forme. Ceci dit, j'ai encore des hésitations sur le type de gameplay. Course rapide à la Sonic ? Puzzle à la Fury ? Arcade à la Johny Biscuit ? Exploration à la Commander Keen ? Avec une composante RPG comme dans Cave Story ? Il y a de fortes chances pour que je touche un peu à tout avec des petits groupes de niveaux d'une sorte ou de l'autre, sans chercher à intégrer le tout au départ. Ca permet aussi de continuer à avancer et à proposer du fun aux joueurs.

Peut-on l'essayer ?
Oui. Les démos sont annoncées au fur et à mesure dans la section "downloads". Il s'agit de travaux en cours : je n'ai pas une équipe de béta-testeurs très étendue, et un regard neuf est toujours apprécié, même si certaines propositions ne seront pas adoptées telles quelles.
Pour les faire tourner, il vous suffit d'une DS "homebrew-ready" ou d'un émulateur.

Homebrew ?
Le développement officiel sur console Nintendo est très fermé. Ca va plus loin qu'un simple SDK coûteux ou qu'un droit de regard sur ce qui est publié et ce qui ne l'est pas. Sans une structure commerciale pré-existante et bien reconnue, c'est l'impasse. Heureusement, il existe des outils permettant malgré tout de programmer la console sans passer par le kit officiel. On dit que les jeux et programmes faits de cette façon sont "fait-maison" ("home-brew" a un petit côté "je distille mon alcool de framboise en effilant les tubes de verre moi-même" qui colle assez bien au résultat comparé au développement sur d'autres plate-formes plus ouvertes). Si vous savez ajouter des jeux sur votre DS via une carte-mémoire type SD ou micro-SD à l'aide d'un gadget acheté à un ami ou sur Internet, vous avez ce qu'il faut pour faire tourner les jeux homebrew. Bravo. ^_^

Où sont les vidéos ?
Je ne fais que rarement des photos d'écran et encore moins souvent des vidéos. Celà demande un investissement en temps colossal pour avoir quelque-chose de correct à montrer. Si vous mettez en ligne des photos ou vidéos de mes productions, je vous invite à l'annoncer à tous via un commentaire sur la démo en question.

Comment est-il programmé ?
Le moteur de jeu est en C++, de même que le modplayer responsable de la musique, le tout dans l'environnement libre "devkitpro" sous Linux. Le côté "PC" de certains outils est généralement bricolé en PERL parce que je le vaux bien. Je tente de garder au maximum la logique de jeu séparée via un langage de script spécifique, dans la veine du micro-basic d'Eric Chahi (Another World) ou de SCUMM (Maniac Mansion et successeurs). Tout ce qui doit être invoqué en permanence (moteur de collision, déplacement des sprites, lecture des touches, etc.) est gardé en C++ de sorte que les scripts n'interviennent que lors d'évènements (changement d'état d'un personnage, collision, fin d'une animation, etc.)

Verra-t-on Bilou dans les magasins ?
C'est très peu probable, et ce n'est pas mon but. La politique commerciale de Nintendo laisse très peu de place aux petits développeurs indépendants. C'est un autre monde où un jeu doit être rentable et disponible dans les bacs à Noël. Je m'appuie sur l'expérience de Cave Story et de Qwak. Je cherche à faire un jeu amusant, et il sera prêt ... quand il sera prêt. L'aventure a commencé il y a 15 ans. L'expérience de Qwak, ce jeu GBA homebrew produit en série limitée et vendu sur Internet, ouvre aussi des perspectives intéressante pour passer du stade "petites démos" à un "produit fini". On verra en temps utile.

En 3D sur PC / Wii / Xbox ... ?
C'est très peu probable également. Je n'ai jamais été séduit par le contrôle d'un personnage dans un environnement 3D. Celà demande également des techniques de programmation, d'animation et de développement très différentes de ce que le "développement-dans-le-grenier" permet. J'ai exploré cette piste pendant une bonne 10aine d'années sans réel succès, et l'arrivée des nouvelles consoles ne change pas fondamentalement la donne.
Intégrer des éléments 3D dans le jeu pique ma curiosité, mais pas au point de quitter l'approche "pixel art" que j'ai choisie.

Open Source ?
Le moteur de jeu et les outils que je développe dans le cadre du projet "Bilou sur DS" sont disponibles dans le projet sourceforge "dsgametools". Mon ambition est de m'approcher autant que possible d'un système de "game maker" entièrement présent sur DS: édition des niveaux, des graphismes, du comportement des personnages, etc. Vous pourrez donc les réutiliser pour créer votre projet complet.
En revanche, je conserve la propriété intellectuelle intégrale sur l'univers et les graphismes de Bilou. Je ne souhaite pas que X fasse une "suite" au jeu, ni que Y réutilise tel ou tel élément dans son jeu. Pour cette raison, les démos disponibles et à venir sont distribués comme un .nds "tout-en-un". Je vous demande de respecter ce choix. Si vous êtes fan de mon travail au point de vouloir proposer un "level pack", les "dsgametools" vous permettront de "schématiser" vos idées et de produire un prototype sur lequel on discuterait d'une collaboration.
Le vrai fan comprendra et respectera ce choix.

Monday, June 15, 2009

Critical Thinking

Sacré professeur Layton! Le voilà qui renouvelle l'intérêt pour la petite DS et le genre "aventure/énigme" avec une bonne dose d'humour. Les vieux de la vieille (comprenez, ceux qui ont bricolé une roue-de-la-mort avec des pièces LEGO pour pouvoir jouer à Monkey Island) regretteront peut-être le côté "déconnecté" des énigmes par rapport à l'environnement du jeu. Les vrais fondus d'énigmes devront se contenter de leurs rubriques de Sudokus, puisqu'ils connaissent probablement déjà la moitié de celles qui sont proposées dans le Village Mystère (si je vous dis, rivière, chou, chèvre, loup, ...)

Mais l'un dans l'autre, c'est une recette gagnante et à mon avis plus agréable que les "Objections!" de l'autre avocat ou les Elite Beat Agent. Il va falloir que je me mette en chasse : plus je l'essaie plus je le trouve à mon goût.

Pour les connaisseurs, une petite savoureuse : Le professeur Layton revu et corrigé par Spartine - la fille que ce soir elle dîne en enfer, et qui anime le fanzine du forum "dev-fr" sur lequel je traine de temps en temps.

Wednesday, June 10, 2009

Non, ça n'ira pas...

Je bricole ces derniers jours la partie du GameEngine qui permettra d'appeler du code C++ à partir des transitions : les GunFactories (essentiellement parce que c'est une généralisation du mécanisme destiné à permettre aux sprites de tirer d'autres sprites). Comme premier test, évidemment, j'essaie de créer une action externe capable de charger un nouveau niveau (ou de recharger le niveau en cours).

Je voulais gérer tout ça directement depuis le GameScript, mais ça ne marche pas... Ca ne peut pas marcher. Le GameScript ignore d'où lui proviennent les lignes du script, et il contient tout l'état en cours du niveau. Tant que le GameScript existe, les sprites se déplacent, se collisionnent, etc. On n'a pas envie que Bilou se fasse toucher alors qu'il vient de finir le niveau.
Je dois remonter plus haut dans la logique du programme, et faire de "l'inter-niveau" un état de fonctionnement indépendant (c'est à dire une fenêtre différente, vu la structure que j'ai donnée à mon "GuiEngine").

At some point, i want my little scripts to be able to invoke some more sophisticated C++ code. It is clearly needed to fire lasers, for instance. The C++ code will create the new GOB, adjust its location, register it and so on. It will also be useful to trigger sound effects or any kind of graphic effect you can think of in response to collisions or "level events". But most of all, samy is my hero I need it to load levels in-game.

Yet, it isn't as straightforward as i thought it'd be in first place. I need an intermediate "state" where the loading is already taking place but the game's logic is suspended. Something like an IntermissionWindow where i can play a little animation while the script is being parsed, etc. Morukutsu uses something similar (a StateManager pattern) in Inside the Machine, and i have such a "menu -> level1 -> intermission -> level1 (you'll better watch your steps this time) -> intermission -> level2" system in my "Gui Engine" used in SEDS and LEDS. So despite I find it weird to involve the GuiEngine within the game, that's actually the way to go.

  • LevelGun.shoot() modifies some state in GameScript that indicates a new level should be run and that's level2
  • it will then send an event to the GameWindow that in turns activate IntermissionWindow
  • by leaving one window, we invoke cleanup such as killing the gamescript and so on.
  • We're on intermission window. we can play a little animation, showing the score or a "retry/abort" question. Some background loading could happen here, though i not quite don't know how exactly.
  • When the level is ready, we return to GameWindow where the gobs are actually instanciated, the level music launched and game control restored.
It requires a little more thinking as the GameScript will actually be shared by both windows (in order to do some preloading). I think i could live with something that do all loading in the GameWindow.

Grosso modo LevelLoaderGun.shoot() devra provoquer qqch comme GuiEngine::setwindow(intermissionWindow), qui nous mettrait un (retry / exit to main menu)

Dans "Inside the Machine", Morukutsu nous avait fait quelque-chose dans cette veine-là via son "state manager". Bref, il y a un peu de re-design à faire mais je n'ai pas encore le recul nécessaire.

edit: c'est fait. Voir LoadingWindow dans game.cxx et LevelGun dans guns.cxx

Tuesday, June 09, 2009

Interesting Platformer

Je retombe sur un plan de niveau de Donkey Kong Country 2: "Web Woods" dans le monde "Gloomy Gulch", l'occasion de voir un peu quel genre de positionnement des monstres peut rendre un niveau intéressant, la simple "topologie" des niveau de Donkey étant généralement assez plate.

In some platformer, the level design is mostly maze-driven : you have to chose between different directions in order to progress. This is definitely not how the Donkey Kong series makes the game interesting: if you strip monsters away, you'll end up with something completely boring in most cases.

La manière la plus simple de procéder est de construire une situation intéressante avec des ennemis ordinaires. Dans ce cas, les souris avancent simplement en tombant, mais mise en série dans un escalier, elles rendent la plupart des emplacements au sol dangereux. On suggère ainsi au joueur qu'il est préférable d'enchainer des sauts, bien que c'est principalement un élément de surprise: une fois la situation mémorisée, il n'y a plus de réelle difficulté puisqu'il est même possible d'attendre gentillement en bas des "escaliers".
C'est une méthode qui ne nécessite pas de fonctionnalité particulière de la part du Game Engine.

Some monsters are hardly interesting when put alone on flat ground, but become a good building block when put in sequence in an environment that increase the "area harzardness" by exposing part of the character's body that are "harder to defend", such as the head in DK. You're basically stimulating the player's memory here: once the hazard is known to the player, dodging it is obvious. Plus, game-engine speaking, they virtually require no special care.

D'autres ennemis comme ce Kutlass sont principalement intéressant par leur comportement et le timing qu'ils impose au joueur. Ses grandes lames empèche le joueur de s'en débarasser dans la plupart des cas, mais il est temporairement vulnérable après avoir tenté de vous trancher. Il impose au joueur un comportement assez récurrent dans les jeux de plateforme : apâter - esquiver - contre-attaquer. La mémorisation n'est plus le seul point, il va falloir un peu d'adresse à chaque passage.

Other monsters are useful by the fact they can be defeated only at specific times. This is very common in sidescrolling-shooters such as Megaman, and can be even refined by making the "weak state" following an attack pattern, forcing the player to enter a tease-dodge-counter danse. Exactly the kind of behaviour that made me opt for multi-state behaviours and state-dependent collision areas in the game engine.

C'est typiquement le genre d'ennemi qui m'a conduit à utiliser les machines d'état et évidemment des zones de collision séparées par état.


Plus évident, le mouvement d'attaque des ennemis peut les rendre intéressants également, en les mettant temporairement hors d'atteinte du joueur.
Dans certains cas, on aura recours à un état d'attente pour conserver l'avantage stratégique de ce pattern. L'idéal est alors de disposer un groupe d'ennemis de sorte qu'en esquiver un rapidement risque d'en "activer" un autre.

A possibly more obvious approach is to create movement patterns that makes the monster alternatively reachable and unreachable to the player (and respectively make the player exposed/safe to the monster alternatively). High-jumpers are found in that category, as well as "standby-then-attack" airborn guys. They can further be refined by breaking the "homogeneousness" of their behaviour, the same way Funky Funghi uses internal counters to make one high jump after three little jumps.

Enfin, il y a bien sûr les "campeurs", qui ne bougent pas, sont généralement placés dans des emplacements peu accessibles, protégés par leur propres tirs ou par des tirs croisés. L'idée reste toujours de réduire le nombre d'emplacements "sûrs" afin de forcer le joueur à enchaîner les risques.

And finally, the level has been spiced with "camping" shooters, and the level designer has decided to be creative by varying frequency and speed of shots, rather than by making e.g. shots affected by gravity as seen in Kirby Squeak Squads.

A noter que Donkey 2 est un peu léger ici: uniquement des tirs à vitesse constante, là où le dernier Kirby nous propose des "bombardiers" dont les projectiles sont affectés par la gravité et donc plus difficiles à esquiver. DKC2 compense par le fait que les projectiles doivent aussi servir de plateforme, et que le niveau va faire varier la séquence où la vitesse de chaque tireur (un tir rapide, un tir lent, un temps d'attente, un tir rapide, ... )

By the way, of course, DK2 increase player's interest by varying the gameplay experience with those friendly animals. In Web Woods, a large part of the level is played with the spider who shoots ephemeral platforms. It also has different jump height or movement speed from the monkeys and brings welcome diversity of experience (somehow merging several games into one storyline).

Saturday, June 06, 2009

tromp ... tromp ... tromp

Entre deux couches de latex au plafond, je profite d'une heure de répit pour bidouiller la machine d'état de Bilou et affiner les collisions. Comme prévu, en court-circuitant la lecture de la croix directionelle lorsque Bilou se fait toucher, il n'y a plus moyen de passer "à travers" Funky Funghi.

J'en ai profité pour ajouter aussi un test "écrabouillé" qui bloque Bilou sur place un moment. Autant dire que dès que j'aurai commencé à décompter les points de vie, ça va faire mal ;)

I took a little hour off while the painting in my bathroom is drying, and reviewed collision transitions in Bilou's state machine. By giving "hit" its own state and disabling the D-pad there, it's no longer possible to "walk through" Funky Funghi.

He's now even more dangerous than ever thanks to the new "stomp" collision area that will flatten Bilou and make him stunned for a while. Needless to say that it's gonna hurt pretty much as soon as "hit points" will be coded.

Friday, June 05, 2009

Sésame, Ouvre-Toi !

Bion. Eh bien les choses n'avancent pas trop mal. En bricolant un peu la gestion des collisions sprite-monde et à l'aide d'un système de contexte pour les GobExpression utilisées dans les collisions, je me rapproche de mon objectif intermédiaire "un jeu où il faut juste ramasser les bonus pour finir le niveau".

En particulier, j'ai maintenant la possibilité de créer des blocs solides qui s'effaceront ou non au contact de Bilou en fonction du résultat d'un prédicat évalué lors du contact. Oh, j'ai bien dû tricher un peu, notamment en forçant le bloc en question à détecter la présence de Bilou sur une zone qui le dépasse, pour compenser le fait que la "zone active de Bilou" est, elle plus petite que son image. Cerise sur le gateau, certains sprites (en l'occurence un petit panneau) peuvent aussi être sensible à ce genre de "compteur de pommes", et peut passer de "retourne chercher des pomme" à "par ici la sortie" lorsque le compte est bon. Il ne me reste donc plus qu'à faire en sorte de pouvoir déclencher la lecture d'un autre niveau une fois la fin du niveau en cours atteinte, et on sera bon pour une "petite démo vraiment jouable".

Piet, j'attends tes p'tits sons ...

I like how the Game engine is doing great progress lately. With some tweaking in GobExpressions and in world-to-sprite collision processing, i managed to have "doors" unlocked when Bilou retrieves a sufficient number of apples in the level. This gets me yet a bit closer from the Qwak-like "grab-the-stuff-and-reach-the-exit" simple kind of game which should be my next release.
I also had the opportunity to check blocks can have "test area" larger than their size, which here was mandatory to compensate the fact that the "collision area" of Bilou was narrower than his "cando" area.

Thursday, June 04, 2009

Lost in Translation ?

J'ai l'ambition que ce blog soit bilingue anglais-français. La communauté "dev-fr" est pas mal active et je m'y sens mieux que sur gbadev ou un autre forum du genre, mais d'un autre côté, la communauté d'artiste sur Pixelation est franchement internationale. Je poste donc parfois en français, parfois en anglais. J'essaie de "rester à jour" de sorte que les thèmes principaux soient couverts dans les deux langues, mais le plus souvent, la traduction arrive après le post initial, ce qui gène peut-être certains abonnés au flux RSS (on peut rêver, j'ai peut-être des fidèles lecteurs dans mes 30-40 visites quotidiennes ;)

Je vous annonce donc que mon dernier post sur la gestion des collision a été traduit avant-hier en attendant que les collègues de Bâle arrivent à la réunion, et que je lance un petit sondage pour savoir ce que vous préférez:

  • "notify through a dedicated post" : faire un petit post de 3 lignes disant "machin a été traduit"
  • "notify in the next post" : la prochaine fois que j'ai quelque-chose d'intéressant à dire, j'en profite pour mentionner les dernières traductions disponibles
  • "i can read both languages" : ça ne vous tracasse pas: quelque soit la langue d'origine du post, vous savez le lire.
  • "i don't care" (p.ex. vous n'êtes pas abonné au flux RSS)
My wish is to make this a two-languages blog. I have friends in real-life and programmers on dev-fr that are likely interested to follow the development of the (15-years-old) project of Bilou's adventure, but i also receive help from international coders/artists and as such, i want most of this blog to be readable in English as well. Posts are initially written in the language i'm the most comfortable with at the moment of writing, and (eventually) translated afterwards, but often find myself publishing the post while it's not yet translated, and "translating" it later in the day. It's unlikely to change, since i usually post in the evening or during lunch times.

Still, i admit that this might be annoying for people following this blog through a RSS, who might be notified of the appearance of a post they cannot read and miss the notification that the post have been translated. Does it happen to you ? Does it prevent you from enjoying the wonderful (:roll eyes:) of this blog ? The poll is yours, let yourself heard.

Of course, if you have the feeling (looking at the pictures, tags, etc.) that you'd like to read a post but can't read French, feel free to request a translation through the comments.
By the way, a couple of tags that you might find handy below:
  • "english" = the post is available at least in English, usually both in English or French
  • "translate me" = the post is available in French, and requires a good deal of work to produce a high-quality English post. I'll do that later during a week-end.
  • "traduis-moi" = the reverse situation: i wrote a big blurp in English and wish i had the time to translate it in French ... when i'll have the time to do so.
Vous l'aurez remarqué, chers lecteurs francophone: je ne signale jamais par les tags qu'un post est en Français. Au pire, vous verrez quelques posts en Anglais uniquement estampillé "traduis-moi", généralement sur des sujets plus pointus pour lesquels je suppose que mon audience voltairienne n'est probablement pas en mesure de suivre.

Monday, June 01, 2009

Les collisions

Les collisions, c'est probablement un des éléments les plus important de la gestion des sprites dans un moteur de jeu. Outre l'aspect purement technique "y a-t-il ou pas collision" et l'aspect d'optimisation "comment tester les collisions entre N sprites (potentiellement (N*N-1)/2 calculs) en un temps raisonnable", il y a le côté "logique du jeu": comment vont réagir les différents objets en cas de collision.

Mes premiers jeux étaient assez élémentaires de ce point de vue là: collision = touché sauf si ybilou+8<ymonster. une tentative un peu simpliste de simuler le "pogotage de cafetière à la SuperMario".
Mais ça, c'était du temps du BASIC. En 1997 (déjà programmeur en assembleur et bricolant mon 'mod player à l'époque), je suis à l'unif le cours d'algorithmique de PaDM ou je découvre les joies des listes liées et où SJ me guide tout doucement vers la compréhension que "tes 'registres de sprites' ressemblent à des variables-membre en POO".

Collisions play a major role in game engines. So far, in my former game attempts, i mostly focused on "how do we know there is a collision?" or "how can i support many sprites without slow-down due to O(N²) collisions detection effort?". This time, i'm rather focusing on "what shall we do once a collision is detected?". Collisions are event that will affect the state machine of our sprites... of both sprites that are involved in the collision. My first "serious" design effort on collisions management dates back from "Out'm'Up" game for the 100K-game competition at Inscene'2K (a demoparty in Belgium), the distilled wisdom resulting on my effort to build an "Ultimate Game Maker" between '97 and '99.

Rétrospectivement, ça fait un peu peur: après 10 ans de programmation, je n'avais pas encore terriblement évolué dans ma manière d'approcher les problèmes de quand je programmais mon "Calimero" en BASIC C64 à grand coup de Gotos. Seule grosse différence, je tentais de reproduire en Software (via des "registres" pour la position, la vitesse, la puissance, etc.) le hardware idéal pour mon moteur de jeu. Un variante non-déclarée de la machine virtuelle donc, mais qui me poussait à sur-définir des éléments tout à fait accessoire du genre "combien de bits pour le niveau d'attaque et le niveau de défense du sprite ?"

Out'm'Up, mon dernier shoot en date
Bref, les années "Ultimate Game Maker" sont loin, maintenant. Si je reprends de temps en temps la farde bleue à carreau où tout cela est consigné, c'est plus par nostalgie que pour son contenu. Ou pour dater une technique ancestrale. En juin 2000, par exemple, j'expérimente dans "Out'm'up" une technique dont je ne me séparerais plus: les castes de sprites. Dans toute collisions, il y a toujours un sprite actif (qui cherche la bagarre) et un sprite passif (qui subit la collision). Le sprite passif appartient à une des deux castes possibles (hero ou evil) et le sprite actif ne cherchera pour ses collisions que dans la liste de la caste correspondance. En clair, celà signifie que les tirs de mon p'tit vaisseau ne testeront jamais que les "ennemis" (et pas les autres tirs ou les bonus) et que les ennemis ne se testent pas entre-eux (mais uniquement vis-à-vis du joueur). L'un dans l'autre, la technique s'est montrée tout à fait satisfaisante vu la quantité d'objets à l'écran (on est encore loin d'un Bullet Hell, bien sûr, mais n'empèche).

Active/Passive
The core concept is that in a collision between two sprites, one is *active* and the other *passive*. I.e. the passive sprite has just registered itself in a list of "sprites that accept collisions", but the active sprite is the one who will scan that list for a match. Together with that mechanism comes the idea of *casts*. We only have a limited number of such "lists" where sprites can register -- one per sprite cast. And so far, in all games two casts seems to be enough: heroes and evils. In a laser-ufo collision, for instance, the UFO is passive evil and the laser is active. That means that the laser only checks UFOs for collisions, not other lasers or explosions, bonuses, etc. In the UFO-spaceship collision, the spaceship is passive hero and the UFO is active. You'll note that the cast of the active sprite is irrelevant in a collision. So far, it has proven much more efficient and flexible than adjusting "power levels" (in RSD Game-Maker, all sprite had a power level, and when the collide, the one with the highest level kills the other one. period)

Si ça peut paraître un peu annecdotique dans un jeu de plate-formes, ça n'en reste pas moins la base de la gestion des collisions dans ma dernière démo. J'y ai ajouté le système des masques inspiré du code de Jill of the Jungle qui, une fois que j'aurai bricolé l'évaluateur d'expressions, permettra de faire réagir les personnages différemment selon la "source" de la collision. Petit exemple ici avec le "pendat" et ses réactions possible en cas de collision avec un objet lancé, avec un encrier bloquant ou avec le personnage.

In a platforming game like Bilou, this needs to be extended. Not only the cast of the 'hitter' is important, but also its nature, which i intend to implement through collision flags, drawing inspiration from Jill of the Jungle source code. Every "active area" defines a set of flags that identify "what it is" while "passive collision areas" indicate "what they are sensitive to". You can then have a penguin monster that takes a single hit unless it is hit by F_FIRE, which kills it instantaneously.

While the cast of a sprite never changes, each state can define various areas, with different flags. That allows us to have e.g. a special "strike" move with an additional, powerful attack area or a move that unveils a weak point. The game script defines transition on a per-area basis, so what happens when you hit one weak point can be different than what happens when you hit another area. Now, i still have to "make it so" in the code, and make the result of "area masks" visible to my "GobExpressions". In an attempt to separate the concerns, each sprite will take care of its own state manipulation: we won't have the pencil 'killing' anyone directly, but i expect that i might need some information about "the other guy" anyway (relative position, speed, etc.)

J'aurai donc bientôt réglé le bug qui "blesse" Bilou à chaque fois qu'il ramasse un bonus et un état dédié 'blessé' me permettra de rendre plus facilement Funky Funghi infranchissable. Par contre, rendre certains ennemis "solides", même quand Bilou est invulnérable temporairement, ça reste un challenge.

2024 #choice reality check: Well, now that I have access to countless hitbox reveal (mostly by Upsilandre), it turns out that having separate active and passive boxes (typically called hitbox and hurtbox) is the de-facto solution. Having a separate list for player and foes also is. But the more I progress, the more it shows that additional casts will be required for bridges and things alike. Nothing has been coded so far, though.