Tuesday, August 31, 2010

New to Blender ?

Petit kit de survie dans l'environnement Blender, qui est a peu près aussi convivial au novice que ne le serait la ligne de commande d'un routeur pour quelqu'un équippé d'un clavier kanji ...

Mais les possibilités du soft par rapport à Wings3D ont l'air de valoir le coup ...

I'm pretty sure there are plenty of good blender tutorials around. It's just that since I've managed to got a few things done when trying to model Pendat, I thought it could be worth taking a few notes. The first contact with the software sounds a bit like CLI-commands for network configuration typed through a kanji keyboard, but I'm slowly getting into it.

It's not as comfortable as Wings3D, but the amount of available options seems to be worth the effort.

Saturday, August 28, 2010

-Weffc++

Le compilateur C++ peut s'avérer pénible par moment, et n'aide pas vraiment le pauvre programmeur du midi que je suis à éviter les bourdes. D'où l'intérêt (théorique) d'un peu plus d'avertissements pour vérifier qu'un certain nombre de "règles de bonne pratique" sont appliquées. Voyons un peu ce que ça donne dans le "code de Bilou"...

base class 'class UsingScript' has a non-virtual destructor
Le but de ces classes "UsingXxx" est de partager entre plusieurs classes un pointeur statique (en l'occurence, l'instance de GameScript) tout en ayant la possibilité d'être averti lorsque ce pointeur n'est plus valide ou de le mettre à jour. Aucun élément par instance, donc. Rien à détruire. Je n'ai pas écrit de destructeur, mais celà ne semble pas suffisant au compilateur: ce code serait plus "efficient" si mon non-destructeur était virtuel :P

'class iGobController' has pointer data members
Oui, en effet, et c'est bien naturel. "efficient C++" voudrait dès lors que je surcharge iGobController(const iGobController&), le constructeur de copie, ni operator=(const iGobController&), l'opérateur d'affectation. Moi, je voudrais surtout pouvoir dire en au plus une ligne "gueule un coup si et seulement si j'essaie de copier un iGobController qui n'est pas prévu pour ça". Mieux, j'aurais voulu dire "SpritePage implements Copiable", et ne rien dire pour les 90% de mes classes, mais ça, c'est du java et je ne code pas en Java sur DS ...

Et qu'on ne s'y trompe pas: ce sont bien les deux que C++ réclame: constructeur de copie et opérateur d'affectation. Si le second peut facilement être remplacé par du "faux code privé" uniquement destiné à produire une erreur si j'essayais de copier quelque-chose de non-copiable, coder le premier nécessite de donner une valeur à tous les membres, ce qui tue complètement l'intérêt de la chose... mais il y aurait tout de même une astuce: donner uniquement le prototype des copieurs en tant que variables privées, sans leur donner d'implémentation.

#ifndef NOCOPY
#define NOCOPY(__x) __x& operator=(const __x &that);\\
__x(const __x &that);
#endif

'ListOfFiles::tnext' should be initialized in the member initialization list
C'est sans doute la fonctionnalité qui m'intéresse le plus dans -Weffc++ : détecter les membres non-initialisés dans les constructeurs. A développer une demie-heure par jour ou entre la mise au lit de *deline et la mienne, c'est le genre de bourde qui est courante et qui passe inaperçue sous émulateur (dont la mémoire est joliment pleine de 0 quand le programme démarre). Notez qu'ici, j'avais ListOfFiles() { tnext=tank; } alors que -Weffc++ aurait voulu me voir écrire ListOfFiles() tank(), tnext(tank), lof() {}. Pas convaincu.

warning: 'ListOfFiles::lof' should be initialized in the member initialization list
Ah oui, parce que, bien sûr, l'initialisation par défaut de mon vecteur n'aura lieu que si je le précise explicitement. Bjarne, tu me rappelles la signification de "par défaut", stp ?
Allez, reconnaissons-lui quand-même que si tank est un tableau plutôt qu'un vecteur, : tank() a au moins l'avantage d'être plus concis (et pose moins de risque d'erreurs) que memset(tank,0,sizeof(tank)); ... et curieusement, celui-là, personne ne s'est plaint que je ne l'avais pas initialisé, tiens!

base class 'class std::vector<...>' has a non-virtual destructor
Argh. C'était peut-être une très mauvaise idée de construire quelque-chose qui soit une sous-classe de vector dès le départ mais ... On fait quoi, dans ces cas-là ? Help! Stack Overflow !

happyhttp::Response::m_Length' will be initialized after 'std::string happyhttp::Response::m_VersionString'
et ?

En conclusion: pas bien concluant, tout ça. Seuls certains des soucis couramment rencontrés sont pris en charge et je me retrouve avec plus de nouveaux problèmes que de nouvelles solutions. Bref, il y a peut-être un cas de figure qui justifie l'existence de C++, mais jusqu'ici, je ne l'ai pas encore vu. 'vais continuer de chercher, hein. ++A.

Wednesday, August 25, 2010

Manoeuvre d'évasion β-1 !

Autre petite expérience en cours: les manoeuvres d'évasions. Afin d'éviter les chocs enchaînés, Bilou dispose à présent d'un court laps de temps au moment où il touche le sol pour mettre à profit l'entraînement physique draconien des pilotes de la Stellar Assembly et se mettre hors de portée des coups suivant.

Une variante à l'invulnérabilité temporaire, inspirée des commentaires de KirbyKid (eh oui, encore) qui met l'accent sur les réflexe et le timing du joueur pour échapper à son destin.

Following KirbyKid's advice, I'm also giving a try to an alternative to the typical "temporary invulnerability" of platformers game that -- I think -- would negatively impact challenges I intend to put into Bilou's Adventure. Welcome evasive moves: by pressing A or B with the proper timing when Bilou hit the ground after being hurt, you can dodge the next hit by doing a sommersault that brings you to a safer place. Of course you won't be hurt again while dodging.

It isn't clear, however, that such things do not suffer the very same defects as "regular invulnerability": you could use an evasive-B-move to quickly hop through a funky funghi after being hit by an appleman, for instance.


L'ennui, c'est que même comme ça, un contact volontaire avec un "petit" ennemi (p.ex. un appleman) permet d'utiliser une manoeuvre d'évasion (horizontale) pour passer à travers un ennemi-obstacle (p.ex. Funky Funghi), puisqu'aucune zone de collision n'est définie pendant l'évasion >_<

todo: ajouter une sélection du niveau de difficulté (easy/normal) dans le "niveau 0" ...

Tuesday, August 24, 2010

They do clump a lot

I've been an attentive reader of KirbyKid's writings on level design for a couple of years, now, so I sent him a copy of Apple Assault v1.1, asking for advices on a few points where my "regular consultants" and I tend to disagree. I was happy and honoured to find his in-depth reply in my mailbox this morning. I'll sure have things to think about and experiments to run in the next weeks.

A force que la plupart de mes "consultants réguliers" (comprenez, CJ, Cyril et Gédéon) me contredisent sur certains choix de design pendant les préparatifs d'AppleAssault, et vu les commentaires peu instructifs récoltés sur dev-fr, j'ai fini par profiter que j'avais un binaire prêt-à-l'emploi pour demander l'avis d'un quasi-pro du level design, j'ai nommé Krazy Kirby Kid. Après quelques échanges de commentaires et d'e-mails, j'ai reçu ce matin sa réponse détaillée dans ma boîte aux lettre, que j'ai ouverte avec une excitation assez proche d'une réponse-du-Père-Noël. Il faut dire que je suis le blog de KirbyKid depuis près de 2 ans et que ses essais sur les "blind-boxes", les "core mechanics" et autre DKART. Un des aspects qui me turlupinait, c'est cette tendance qu'ont les applemen à "s'agglutiner" pour former des chenilles

I had issues with the "wiggler/lemmings" aspect of applemen behaviour, and my initial idea was to make "fall of cliff" somehow probabilistic. KirbyKid pointed out that

I like working with simple predictable enemies. They [Applemen] do clump up a lot though. The issue I have with this is that they can clutter the game (...) [and it] makes it easier for an appleman to sneak behind a stunned appleman and hit you.


La bonne nouvelle, c'est que l'avis extérieur de KirbyKid me motive à faire des expérimentations alors que j'ai plutôt tendance à justifier ma position vis-à-vis de mes "voisins de VTJ". Ou alors il a juste réussi à trouver les arguments vraiment convaincants. Bref, voyons ce qu'on peut faire pour éviter les "grappes" d'applemen, en particulier compte tenu du fait qu'un tel attroupement rend plus difficile l'identification des applemen encore "en course".

Let's experiment another strategy, then ... something more bio-inspired, such as generators that "sense" how many applemen exists in their surrounding and self-inhibit the generation of applemen if a certain density is reached.
state1->state2 on done
state2->state1 on found0
state2->state1 on done [c#applemen# 0 >] (x#throw_appleman# d#applemen#)


Petite idée "bio-inspirée" pour éviter ça: rendre le générateur d'applemen plus sensible à son environnement ... par exemple l'empêcher de générer un nouvel appleman s'il en a vu un récemment. J'ai déjà des zones-de-détections (cf la boîte bleue sur le schéma) que l'appleman utilise pour "voir" Bilou et l'attaquer, c'est le même principe. Sauf qu'ici, celà provoquera le retour anticipé de l'état "sonder l'environnement" (state2) vers l'état "attendre" (state1) alors que la génération d'un nouvel appleman se fait normalement à la fin de l'animation de state2 (à condition qu'il y ait encore des applemen à générer, bien-sûr).

It is actually surprisingly easy to tweak: an additional state (state2) is added to the FSM ruling the generator, which has an "active test area" (depicted in blue) and it will remain in this state only for a few frame, while state1 covers the majority of the inter-applemen delay. We can leave state2 either because an appleman has entered the "sensitive area" of the generator (in which case nothing happens) or because the delay for state2 has expired, and only then we throw a new appleman and decrement the number of applemen still to be generated.

Pour peaufiner, je peux faire en sorte que l'appleman soit à son tour "sensible" à l'annulation d'une génération et du coup "quitte le nid". Ca marche assez bien sur le niveau 1, en tout cas.

An additional tweak allowed the applemen to "sense back" such detection and, when this happens when they're on a cliff, make them jump off. In this way, applemen no longer "stick in their trunk hideout forever" on map #1: after a short time, they will leave the safety of the hollow trees and explore the surroundings. That should make them clump much less, although it's not yet a full-proof solution.

edit: I only gave these new game rules a few tries, but it looks like it doesn't make difficulty increasing with levels. At least, it doesn't make me feel that the game is getting harder, just that the levels are longer and longer to play, and I've been tempted to stop playing when I reached the map #1 with >60 applemen to attract out of there trees. I may need to find something else so that running applemen cannot ambush behind stunned applemen. The fact that the priority of two applemen solely depend on the OAM registers they use doesn't help 0_o


Au final, l'approche n'est pas vraiment concluante. Le niveau 1 est rasoir (sauter pour attirer 60 applemen ou les voir approcher au compte-goutte ... bof. Le niveau 2 déséquilibré (certains "trous" n'auront généré que 2 applemen, et un bug fait que certains restent "cachés" dans leur arbre. Les niveaux 3 et 4 ont leurs générateurs trop loin des applemen et ne sont donc de toutes façons pas affectés. Bref un "compteur global du nombre d'applemen présent à l'écran" aurait été tout aussi (sinon plus) efficace. Je devrais avoir moyen d'arranger les niveaux 1 et 2 avec un "nombre de saut autorisés" défini pour chaque générateur, par contre.

Sunday, August 22, 2010

"640K should be enough for everybody"

L'an dernier, déjà, Gédéon (eh oui, encore lui) attirait mon attention sur quelques pavés disgracieux dans le bas de l'écran de jeu. Rien de bien grave, pensais-je alors: j'ai juste oublié de supprimer les boutons "beam in/beam out" dans mon "runner".

Sauf que dans Apple Assault, c'est un bon quart de l'écran qui disparaît (un peu moins sur DS), rendant les instructions peu lisibles sur émulateur.

You sure have noticed that ugly purple area in the bottom of the "menu screen", and maybe you've noticed it doesn't show up (or not as much) on real hardware. I initially thought this was due to some GUI code from runme that was still hanging around, but a debugging session proved the painful reality: I'm overspending my 128K VRAM budget. By a mere 4K, but that's unfortunately enough to doom the visual output -- or to trash the precious ASCII character set when the game is run by runme-on-R4.

J'ai donc ressorti mon kit du parfait débuggueur histoire de voir un peu où les choses tournent de travers... pour me rendre compte qu'apparamment tout va bien. Ouais... Sauf que:

  • 980 tiles pour les troncs, branche, terre, tout ça: 64Ko
  • 587 tiles pour aahowto.spr (l'image de fond) : 36Ko à vue de nez.
  • 4 maps 64x32 pour les faire le scrolling : 16 Ko
  • Les caractères ASCII et les "layers standards" du GuiEngine (surtout utile pour runme): 16Ko
  • total : 132Ko alors que la VRAM n'en offre que 128 pour ce genre de graphismes.
Bref, les tiles constituant l'image "aahowto.spr" sont placé en-dehors de la zone de mémoire normalement prévue pour eux. Sur hardware, les bits de poids fort sont ignoré (le hardware adore faire ça: ça veut dire que si vous mettez une barette de 1Mo et que vous demandez à écrire à l'adresse 1Mo+42K, vous écrasez en réalité ce qu'il y avait au début de votre mémoire, à l'adresse 42K) et les majuscules sont écrasés par le fond d'écran (rendant délicate l'utilisation du reste de runme :P).

L'émulateur, lui, ne fait rien de tout ça. Les données que j'ai écrites au-delà des 128K sont perdues, et le contenu de l'écran utilise à la place des "pavés tout vides" -- donc du mauve qui fait tache.

La bonne nouvelle, c'est que aahowto.spr n'utilise que 14 ou 15 couleurs. Je devrais donc pouvoir le convertir en un layer 16 couleurs (au lieu de 256) et diminuer de moitié sa consommation de VRAM. Il y aurait bien d'autres solutions également (après tout, je ne déborde "que" de 75 tiles, or j'ai 44 tiles inutilisées sur les avant-plan et l'équivalent de 64 tiles libre dans la zone "maps pour le scrolling". Le hic, c'est que les jeux de tiles d'un plan doivent obligatoirement commencer à une adresse multiple de 16K, ce qui tue toute possibilité de "compression" de ce genre. L'idée étant que quand la puce vidéo de la DS rencontre le tile numéro 345 sur une map, elle s'empresse de calculer données_en_vram = 345 x 64 + TILE_BASE[no_de_layer] * 16K ...

Bon, je ne suis pas au limites des possibilités de la DS, loin de là (j'utilise 512K de VRAM sur les 640 disponibles), mais je frôle ce qu'il est possible de faire sin mô d'tchiesse.

There are a couple of options to fix this issue, but none of them could be hacked in a few hours. The best approach would likely to take advantage of the 16-colours background screen and to trim it from ~36K to ~14K, which would perfectly fit the available VRAM. I could alternatively try and map more memory on tiles (I'm only using 512K out of the 640 available on the DS), but that could have strongs implication later on, that would prevent me from doing 3D or using different palettes on the different planes. The DS hardware offers some flexibility on how you use the VRAM, but you quickly realise that there are more constraints than degrees of freedom :P

Bienvenue dans le monde réel >_<
edit: c'est fait

Saturday, August 21, 2010

Et maintenant ? La 3D ?

Un jeu a été produit. Selon mon "code de conduite personnel", j'ai maintenant le droit de regarder un peu vers d'autres lieu du monde de Bilou, scribouiller de nouveaux environnements, tout ça. L'occasion donc d'attaquer un peu la school zone qui a bénéficié d'un important travail de level/monster design ces dernières années. On pourrait même avoir le mini-jeu "deep-ink-pit" à la clé.

Pas mal de personnages de la school zone se prêtent mieux à un rendu 3D qu'à du pixel art classique. Enfin, c'est mon impression. Bangbash, les Pendatz, les gros bouquins écraseurs ... L'idée serait de mélanger "sprites 3D" et environnement 2D façon "new super mario bros.".

L'ennui, c'est que je crains un max, question modélisation 3D. Il m'a fallu près d'une heure pour arriver à cette "ébauche" de Pendat que je ne parviens même pas à ajuster. Et il y a aussi le fait que je n'ai pas encore la première ligne de code pour tester ce genre de chose. Bref, on va rigoler :P

Friday, August 20, 2010

Ho capito

Pas forcément évident de suivre les réactions des gens qui testent un jeu, avec le "homebrew distribution channel" (à savoir, machin lit un post sur le forum de truc, le traduit comme il peut et le reposte sur son forum à lui).

Ceci dit, la remarque de DjGG était assez prévisible: l'écran mauve quadrillé en bas, ça ne met pas précisément l'eau à la bouche. voilà donc une petite tentative de "head-up-display" pour Apple Assault, les "courbes bleues" étant destinées à montrer la barre de vie et de punch, respectivement. Avec des "animations de palette", plus que probablement.

I'm trying to give my little game a more appealing and readable HUD than the current one. Since it's a DS game, I can use the whole "bottom screen" for stats & score. The text in white will be code-generated and offers little artistic liberty, but I thought the "health bar" and the "punch bar" could be worth a more visual interpretation : the blue curves.

The intent is to lit on/off part of these curves (palette-wise) to reflect the current status of the hero. I'm not yet satisfied with the double-size Bilou and Appleman though.

    SpriteRam hud((u16*)BG_TILE_RAM(1)); // leave GUI resource untouched.
SpriteSet hudset(&hud,BG_PALETTE);
hudset.Load(filename);
u16 w,h;
memcpy((u16*)BG_MAP_RAM(GEYOURMAP),hudset.getmap(&w,&h),32*32*2);
// BG3 is already loaded with GEYOURMAP and TILEBASE(1)

PS: I was expecting something tedious and complicated to load a new picture on the bottom screen, but apparently, my Sprite* classes do this pretty easily -- given that I'm happy to assume a 32x32 map, of course :P

Thursday, August 19, 2010

Apple Assault v1.1

v1.1, dite "GDX" (19 aout) : désactivation des dernières fonctions "InspectorWidget", (Merci à Alekmaul) légère modification des sons, corrige les bugs liés aux shurikens (Merci à Gédéon) et aux "apple-hit-combo".

J'attends l'avis d'un critique semi-pro avant d'aller plus loin sur le projet.

bad IDEa

on dira ce qu'on veut, moi quand je croise

Logger.getLogger(DimLocalHandler.class.getName()).log(Level.INFO,
"Further improvement possible.");
dans du code, je me dis que même si ça a été facile à écrire (auto-complétion-power), ça reste lourd, c'est moche et ça encombre l'esprit de celui qui raisonne à propos du code. Et encore, il aura fallu importer java.util.logging.Level, pour pouvoir "raccourcir" en Level.INFO, j'imagine

On a pas droit à @INFO("Further improvement possible") ?

Wednesday, August 18, 2010

Apple Hit Combo

Un petit jeu comme Apple Assault, c'est l'occasion de tester des "choix" de game design plus ou moins intentionnels, histoire de voire s'ils sont viable ou non pour le "vrai jeu". En l'occurence, lors de l'introduction de Funky Funghi, je me suis rendu compte qu'il fallait ajuster la gestion des "blessures" de Bilou de manière à éviter qu'il ne puisse le traverser.
Dans un jeu comme Mario ou Sonic, les éléments essentiels du gameplay sont les réflexes et la dextérité. Continuer à avancer malgré un choc avec un ennemi est donc naturel, voire exploité dans des speedruns. J'ai l'intention de centrer davantage "Bilou's Adventure" sur l'exploration et la résolution de puzzle, sans pour autant aller jusqu'à un Braid ou Johnny Biscuit.

Dans ce genre de jeux (y compris Fury of the Furries et Commander Keen), il est fréquent de rendre le moindre contact fatal, de sorte qu'un monstre puisse faire office d'obstacle incontournable et forcer le joueur à résoudre le puzzle proposé. Pour Bilou, je souhaite garder le côté "incontournable", tout en laissant au joueur la possibilité de commettre des erreurs et de corriger le tir. Je suis donc parti sur un nombre relativement élevé de "points d'énergie" (5 -- plus proche de Kirby que de Mario) mais sans période d'invulnérabilité en cas de choc. Au contraire, Bilou est projeté en arrière et le joueur ne peut reprendre le contrôle avant qu'il n'ait touché le sol.

Dès que les applemen ont fait leur apparition, un soucis est alors apparu: un appleman ayant repéré Bilou fonce vers lui bien plus vite que le déplacement d'un woodworm, ce qui lui permettait alors d'enchainer les coups portés à Bilou sans laisser au joueur aucun espoir de réaction : c'est le Apple Hit Combo. D'une certaine manière, ce type de comportement me plaît: ça fait de l'appleman un ennemi hargneux, plus dangereux qu'un simple goomba. A condition que sa hargne puisse être tempérée.

En raffinant le moteur de gestion des collisions, j'ai pu faire en sorte que l'appleman soit "repoussé" lors de collisions. Ouf! Le joueur gagne ainsi un court répit qui peut lui suffire à échapper à la charge suivante s'il est assez vif et attentif. Les coups enchaînés sont donc toujours possibles mais deviennent un challenge et plus une fatalité "vraiment trop injuste". Le combo inévitable existe toujours, lui aussi (par exemple quand la première collision a lieu alors que l'appleman tombe sur Bilou, ou si celui-ci est acculé contre un mur ou pris en tenaille par plusieurs applemen. Là aussi, le résultat me plaît: par simple application des règles de base, on obtient des "dégâts critiques" qui forceront le joueur à développer une stratégie dans laquelle il évite les situations à haut risque.

Sauf qu'il reste des situations où le "bon vieux hit combo", imparable, continue à réapparaître, comme l'ont mis en évidence, Gédéon, Nathan et Laurent, moins habitués au contrôle de Bilou. J'ai me fais tout doucement ma petite idée sur les raisons à la base de ce petit bug ... affaire à suivre.

PS: si vous avez essayé le jeu, un petit sondage vous invite à partager votre réaction face aux "hit compote" : fun (71%) ? challenge (26%? frustrant (33%) ou franchement gonflant (20%) ?

(PPS: votes non-exclusifs sur 15 participants)

Saturday, August 14, 2010

You've said "BSOD" ?

Hola !? Che passa ?
Inspector widget
Mekwa ? Kesseksa ?
hou houuu~

Yauntruk kivapa !
Inspector widget
Et ça s'arrête làaaa
hou! hou!

Despite my attempts to get InspectorWidget (the internal debugger of my game engine) out of the way for the Apple Assault release, you may encounter a screen like this if you really hunt for easter eggs. The game hasn't crashed: it is just waiting for you to press the (Y) button so that it can proceed with execution of the game script. press it repeatedly so that you go through all the initialisation steps, and you should be able to keep playing. Thanks fly to Alekmaul for pointing that out.

Même si ça en a fort l'air, Apple Assault n'est pas planté même si vous voyez ce genre de choses à l'écran. Rappelez-vous: au départ, l'interpréteur de gobscript traitait les commandes une par une, attendant une pression sur (Y) chaque fois qu'un "print" apparaissait dans le script. Cette fonctionnalité existe toujours, désactivée, et en pressant sur START, on active le mode "débug" dans lequel cette "initialisation pas à pas" est à nouveau présente.

Mais c'est clairement quelque-chose que je devrai éliminer pour la prochaine release.

Friday, August 13, 2010

Apple Assault v1.0


Bilou et Bouli viennent juste de se crasher sur une planète étrange. Pendant que Bouli répare l'écran protecteur de l'Astrocruiser, Bilou doit faire face à une horde d'applemen déchainés !
Le jeu se joue sur 4 secteurs (nord-sud-est-ouest) que vous devez nettoyer avant de passer au suivant. Sautez d'abord sur les applemen avec (A) pour accumuler de la force de frappe que vous utiliserez pour les expédier au loin avec (B). A chaque vague d'assaut, les applemen sont plus nombreux. Jusqu'où tiendrez-vous ?

Bilou and Bouli have crashed on a strange planet. While Bouli is repairing the Astrocruiser's repellor shield, Bilou (the blue ball) has to keep hordes of crazy applemen away ! The game feature the four fields surrounding the cruiser that you must clean up so that Bouli can finish his job and provide you a safe retreat for further exploration. Stun the applemen by stomping them (A). That will provide you more punch power to dispatch the assaulters further away (B).
In each assault wave, the applemen will come back with reinforcement. How long will you stand ?


trucs & astuces
  • pour faire progresser la barre de punch plus vite, rebondissez sur les pommes (en réappuyant sur A au moment du choc)
  • les petits vers ne peuvent pas être éliminés, mais vous pouvez aussi rebondir dessus pour augmenter votre punch sans pour autant approcher les pommes.
  • dès que votre barre de punch est remplie (elle vire au jaune et une musique spéciale se fait entendre dans la release 1.3 (em)) , Bilou peut lancer un shuriken dévastateur. Par contre, celà vous videra de tout votre punch de moitié.
  • plus vous enchaînez les rebonds sans toucher le sol, plus votre score grimpera vite !
  • choisissez votre niveau de difficulté en mangeant des pommes dans le menu.
  • ne poussez pas sur START, sauf si vous voulez tester le mode "debugging" (retour au jeu avec L+START) corrigé dans la release 1.1 (gdx).


gameplay tips
  • you can build up your punch power faster if you press (A) again when bouncing on applemen
  • yellow worms can't be dispatched, but they can "help" Bilou building punch power while staying at safe distance from apples
  • once you've gathered enough power (your punch bar turns gold and the music change), you'll shoot a devastating shuriken instead of merely punching.
  • the more bounces you chain without touching the ground, the more you'll score.
  • check out the gameplay teaser video if you still can't beat level 2.
  • grab apples on the menu screen to select harder game difficulty.
  • do not press start, unless you want to enter debugging mode (which you leave with L+START but remains dormant) -- fixed in release 1.1 (gdx). If you want to pause the game, just close the lid.

If you think the gameplay choices are odd or bizarre, think of Apple Assault as a game in the lineage of Mario Bros. -- the initial, non-super one or Bubble Bobble


File mirrored at nintendomax.com
latest release: v1.3

Thursday, August 12, 2010

AppleAssault : 98% done

  • [done] level maps
  • [done] soundtrack
  • [done] punch behaviour
  • [done] instruction screen
  • [done] credits
  • [done] game over
  • [done] game state display
  • [done] one-file packaging
  • [done] compute score
  • [wish] reward style with 1UPs
  • [wish] funghi-applemen interaction
  • [done] check "damned-jump" bug disappeared
  • [done] mario-compatible jump = A ; punch = B
  • [done] try appleassault.nds on real hardware
  • [wish] random level ordering
  • [done] investigate buggy background screen.
  • [done] fix buggy background screen
  • [mouais] sound effects
  • [done] remove "state xxx !SET" and similar debug message
  • [done] remove the "ghost appleman" of level 1
  • [wish] home/visitor highscores.
PS : vous vous demandez sans doute "si ça, c'est 98%, à quand remonte le 2%". Excellente question. Pas à l'introduction du nom, en tout cas, ni même à la proposition de l'objectif du jeu. Je dirais plus volontiers qu'il s'agit des premiers tests avec l'appleman aggressif (qui vous saute dessus quand il vous voit), donc début août 2009. Quoi que ce serait déjà plus 10%.

Maybe you find it odd that I claim this is 98% ... 98% of what ? when was 50% when was 0%. I'm not fully sure, of course, but I'd say we started moving towards AppleAssault when applemen started to attack Bilou in the green zone, that is, when I implemented detection of Bilou's position to trigger behaviour change for the applemen.

Ne vous posez pas de question: codez.

Quand j'annonçais la semaine dernière qu'il me restait à mettre en place le système d'affichage du score et de l'état du jeu (niveau de vie de Bilou, barre de punch, etc), je vous avoue bien que j'étais sur le point de partir dans un trip "variante du GameScript pour faire un HudScript" avec des considérations du genre "oui, mais comment mettre en place l'affichage de clés de couleurs 'à la Commander Keen'" ou "et si je détournais la commande 'delay' des GobAnims pour indiquer sur quel intervalle de valeurs il faut utiliser telle ou telle image. Etc.

Bref, Apple Assault se serait transformé en "Wake me up when september ends" :P Puis j'ai laché à mon collègue Bruno le "Ne vous posez pas de question: codez" du Docteur Mollusk (créateur de la PaLib si répandue sur DS) et ça m'a fait réfléchir moi aussi ...

Earlier this week, I started thinking at the display of status and score for Apple Assault. I have to confess it started looking like variation of the GobScript to describe the HUD, express which GOB and game variables should be used, how they should be transformed into displayable items or how they should select one image in a collection.

It could have cost me weeks of analysis and development if I hadn't remembered Dr Mollusk master words : "do not wonder: code" -- somehow close to Yoda saying "do not try : do it ... or do not". Clearly, all I need is a display of the internal dials in a readable form. The aesthetics are secondary, as they are on a different screen from the game itself. Not that they are irrelevant, but definitely secondary. So I went straight away, coded the crudest thing that just do the job, that can't be reused or customised, but that works and that's done. I can move forward and think about the scoring itself or the feasability of an online leaderboard system.


Alors voilà: le code qui fait l'affichage (tout comme l'affichage lui-même) n'est pas beau: il est minimaliste. On ne peut pas changer la position de tel ou tel élément sans recompiler, et j'ai fait un rendu des barres d'énergie (et même de Bilou) en ASCII art par ce que j'ai la flemme d'écrire le code qui chargerait les zolis graphismes dans la VRAM de la DS. Eh oui, point de "rect (0,0)-(power,8), BLUE" ici. Il faut tout convertir en tiles de toutes façon.

Tuesday, August 10, 2010

Lamers don't die ...

Take a neat concept (programmers answering programmers), put devotion and work on it so that it becomes StackOverflow.com. Make sure credit goes where it is due and that helping out people turns into something that can help you being helped later on.

And for some reason, some soulless ruleless content-rippers that dub themselves "weask.us" will scavenge the net with their bot, present the "question/answers" as if they did all the job, not giving the slightest hint that the content comes from another source. This is the most disgusting thing I've come across since ... Windows ME. No, it's even worse than that.

A wise friend once taught me "there's no big Sauron. There's no epicentre of evil we can fight". True. But sure, there isn't any limit to the amount of dishonesty and dishonour some sort of people can be made of.

Monday, August 09, 2010

C'était bien ça ...

J'avais pris une approche un peu trop "frileuse" en choisissant d'exécuter l'expression accollée à un changement de niveau avant le changement de niveau lui-même. Cela signifie en particulier que l'action "changer le niveau" aura le dernier mot quoi que l'expression puisse tenter de faire, rendant impossible des construction du genre si (vies=0) alors gameover() sinon réessaie(ce_niveau).

Let's put it somehow back in perspective. First, my friend Pierrick suggested that the amount of apples to defend against would be based on the #level we're in. Then I realised that some tweaking was required to let the game engine distinguish "you're dead. try again?" from "well done! next level!". That tweaking took the form of a GobExpression attached to the action "level(greenX.cmd)", which compensated the automatic "level++" incrementation I added in the game script.

In that setting, it should have been fairly simple to "code" the game over. When the hitpoints counter reach zero, it automatically executes the action "level(greenX.cmd)" and evaluate the expression "level--" to restore balance in the world. Unfortunately, I coded that a bit timidly by evaluating the expression first, and launching the new level next. The side effect is that when I replaced "level--" by "level--; lives--", the effect of 'lives reached zero. Game over. launch level0' were immediately undone by the setNextLevel("greenX.cmd") that took place afterwards. Although that looks odd, I am allowed to swap them to get it fixed, as the "setNextLevel" just stores the level's name and the actual world destruction / loading / re-creation cycle is deferred and happens between regular "game frames".

Pretty neat, uh ? no matter how many GOBs try to trigger world termination : they won't testimony it anyway because it happens out of their timespace. It's just a pity I couldn't get this sorted out last Saturday (I should have typed "cvs commit" before I powered down that workstation for the week-end) and I only know how my alpha-tester reacts in face of an endless challenge.


En réalité, exécuter setNextLevel() en premier est correct parce qu'il s'agit d'une opération qui se déclenche à retardement. Pour raisons techniques, il n'est pas possible de changer de niveau (ce qui inclus une destruction de tous les objets en cours pour reconstruire le nouveau niveau à partir du script donné) pendant la phase "animation/détection de collision" qui provoque en cascade l'exécution des actions.

L'important, c'est que le problème est réglé: on peut donc être "game over" dans Apple Assault.

PS: En général, j'évite de m'étendre sur ce genre de graissage d'engrenages, mais peut-être que ça permettra de me l'ancrer un peu plus définitivement dans la caboche. Quelqu'un a une cheville de 8 à me prêter ?

Sunday, August 08, 2010

L'avis des alpha-testeurs

Petit passage chez mes n'veux hier, avec l'inévitable (et attendue :) scéance de test du Bilou en cours ... sans même m'avoir laissé le temps de faire une démo à mon beau-frère, cette fois-ci, soit dit en passant. Le côté le plus positif, c'est sans doute qu'Apple Assault a facilement tenu les gamins 2 heures avant qu'ils ne me réclament un autre jeu.

J'avais voulu ajouter rapidos la gestion du "game over" (le jeu fonctionnant pour l'instant avec des vies infinies). Ca n'aurait pas dû être plus compliqué que de transformer

let c2 = 5  # hitpoints
c2.action = level(green2.cmd)
en
let c2 = 5  # hitpoints
# let c3 = 3 # lives, only in green0
c2.action = level(green1.cmd,d3) # try again
c3.action = level(green0.cmd) # game over
l'idée étant que l'action associée à un compteur est exécutée lorsque celui-ci atteint 0. Le code de bilou.cmd est déjà rempli de d2 pour décrémenter le nombre de hitpoints chaque fois que Bilou se prend une mandale, il restait à ajouter le retrait d'une vie chaque fois qu'on recharge le niveau avec d3, qui aurait à son tour provoqué le chargement de l'écran-menu (green0.cmd) lorsque toutes les vies ont été consommées.

Sauf que ça ne marche pas.

C'est à dire: les vies diminuent bien, mais on reste malgré tout sur le niveau en cours sans jamais faire le retour vers le menu. Je subodore quelque chose du genre
eval_expression(d3)
 decrement_counter(c3)
  execute_action(level(green0))
   setNextLevel("green0.cmd")
   switchToLoaderWindowAsap()
  action_performed
 expression_performed
execute_action(level(green1))
 setNextLevel("green1.cmd")
 switchToLoaderWindowAsap()
action_performed
ce qui écraserait la demande de passer au "niveau 0" lors de setNextLevel("green1.cmd"). Je ne peux malheureusement pas le vérifier avant lundi: le CVS n'est pas à jour et mon PC du bureau est actuellement débranché dans le labo vu qu'on change la moquette la semaine prochaine.

Plus gênant, on dirait bien que le problème du saut-qui-ne-devient-pas-une-chute (et donc qui n'écrase pas les pommes) a réapparu. Difficile pour moi de le traquer: il semble lié à la manière dont on se sert des boutons (durée de la pression, etc). Et clairement, je dois revoir les contrôles: X = sauter, B = frapper, rien à faire, les gamins s'y perdent. Autre option pour rendre le jeu un peu plus intéressant: permutter aléatoirement les niveaux 2 et 3 à chaque partie (à la Qwak).

Thursday, August 05, 2010

How to play ...

Un petit gribouillage qui répond à pas mal de question en suspens. Comment faire en sorte que les joueurs "comprennent" le fonctionnement du jeu (y compris ceux qui le téléchargeront sur un forum en italien :) sans passer des semaines à bricoler des écrans de menu ou ce genre de choses. J'étais parti sur une série de dessins, mais finalement, le plus simple sera encore de gribouiller des annotations directement sur le décor et d'avoir un "niveau" en guise de menu ...

J'ai vu la chose en action dans Spelunky, et je dois dire que c'est tout à fait convaincant... enfin, à moins d'avoir des options dans tous les sens, s'entend.

Let's forget about instruction screen, start menus and fancy credits/highscore things. People will need to know how to play, and I don't want to spend weeks on secondary artefacts. On the other hands, I know how the homebrew distribution channel works: people won't get README file nor will they know where the game comes from unless it's included in the binary itself.

Here comes a simple way to achieve those goals : a spelunky-like "level 0" where the background has hints scribbled on it, and where you can experiment with basic moves or start the game. I'll just have to ensure my bros. don't end up stuck on the level 0 as he did in Still Alive DS :P


edit: C'est tout ce qu'il me reste à faire, d'ailleurs. Les 4 maps sont ok, à part peut-être quelques aspects cosmétiques sur la dernière. Avec un peu de chance, je pourrai "emballer" le jeu ce week-end ^_^

todo

Tuesday, August 03, 2010

Blues Brothers -- Trivia

Le saviez-vous ? Bien avant la SNES, les Blues Brothers de Titus Interactive avaient été adaptés sur NES. Mais j'ai bien l'impression que les exigences de "politiquement correct" de Nintendo ont eu un effet néfaste sur le concept du jeu. Point de caisse à jeter à l'horizon. Exit les personnages loufoques tels que Mamie-Caddie ou la serveuse-championne-olympique ... Pas même de disques à collectionner. Bien pauvre. Ca n'a pas dû aider le succès du jeu.

Dans ce speedrun, le jeu est terminé en 4 minutes chrono. Pas de gamin armé de lance-acide dans l'usine ... pas de Docteur-Maboule armé d'un lance-seringue dans la prison ... Que diable s'est-il passé? Il ne reste qu'à traverser des maps vides. Le monde impitoyable du jeu grand public ne cessera de m'attrister.

Le rétro-gameur averti boudera donc la cartouche pour se concentrer sur la version amiga qui ne souffre pas des habituels problèmes de scrolling rencontrés dans les portages PC des jeux Titus -- même si les bande-son Adlib me manqueront.

(Bilou progress report: une 3eme map scribouillée pour Apple Assault, polish des 2 premières maps ... tout ça malgré un Level Editor qui aurait encore bien besoin d'être débuggué...)

Monday, August 02, 2010

Le vieux Synopsis.

Petite scéance d'archivage: voici en comparaison ce qui tenait lieu de "Synopsis" à Bilou après la scéance de "brainstorming" pour le concours de 1994: tout juste un nombre de niveaux, l'ordre dans lequel il est possible de les parcourir (notamment via les téléporteurs triangulaires) et les pouvoirs gagnés après le combat contre les boss.

C'est bien sûr ma version "synthétique" de travail, mon frère m'ayant confectionné quelque chose de plus graphique avec une grosse tortue et une île volcanique ...

Bo - Look. This is an ancient map of the planet we've landed on.
Bi - Yep, it's curious that they had technology to build teleporters, but still named their lands along some primary elements such as "Fire", "Water", "Green"
Bo - I wonder what these "pyramid" and "turtle" places could look like.
Bi - Feel like doing some exploration, at last ?
Bo - Don't be stupid ! Archeological curiousity has nothing to do with your "exploration" stuff!

PS: eh oui, finies les "vacances 2010". Fini les coups de rouleaux sur le plafond, les tuyaux qui fuient et les tiroirs à monter ... ma cuisine est à nouveau prête à l'emploi (peu s'en faut), ce qui signifie que je peux à nouveau déployer un laptop dans mon salon :P