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).
Tuesday, June 09, 2009
Interesting Platformer
Tags: designclass, donkey, english, game, level design, state machine
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.
Tags: aouch, coding, english, funky funghi, state machine
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)
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.
Tags: english, poll, traduis-moi, translate me
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 |
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.
Tags: choice, collisions, core, dumblador, english, features, inkjet, pendat, school zone, shoot-m-up, sidescroller, sketch, state machine, ultimate game maker, y2k, y97
Thursday, May 28, 2009
sendall
J'ai toujours été intrigué par cette fonction "sendall" du Beej Network Programming Guide. Selon Beej, un appel à send pour transmettre des données pourrait ne pas nécessairement envoyer autant de données que prévu. J'ai toujours trouvé ça un peu surprenant et je n'avais personnellement jamais été témoins d'une telle chose jusqu'à aujourd'hui.
Corrigeant les TP de programmation de BitTorrent, j'ai un strace ouvert aussi sur le client initial dans un "environnement de test contrôlé" et voilà que j'aperçois la séquence suivante:
send(10, "\0\0\200\t\7\0\0\0(\0\0\0\0\2619\r\222\265\315r\264a\363"..., 32781, 0) = 32781
send(10, "\0\0\200\t\7\0\0\0(\0\1\0\0\340\306\335\246\17\r\377\265"..., 32781, 0) = 32781
send(10, "\0\0\200\t\7\0\0\0(\0\2\0\0\233\257\314\25P\322\2363\316"..., 32781, 0) = 32781
send(10, "\0\0\200\t\7\0\0\0(\0\3\0\0002\235\311\36yyzt\322\363$"..., 32781, 0) = 28790
send(10, "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"..., 3991, 0) = 3991
Le programme tente d'envoyer des données de 32K accompagnées de 13 bytes de contrôle. Le plus souvent, celà signifie qu'un buffer de 32781 bytes est passé à l'appel-système send (cf. le 3eme argument) et que 32781 bytes ont effectivement été envoyés (cf. la valeur de retour, après le '='). Sauf pour les deux derniers, où si on a bien 32K+7 soumis, seuls 28790 bytes ont pu effectivement être "envoyés" ... Je devrais plutôt dire "pris en charge par le protocole TCP". Effectivement, TCP a sa propre mémoire-tampon dans laquelle il conserve les données à envoyer, mais elle n'est pas infinie. Si a un moment donné elle "déborde", TCP prend le plus de données possibles mais signale à l'application qu'une partie n'a pas pu être traitée.
On aurait pu préférer une approche "l'appel va être bloquant jusqu'à ce que tous les bytes soumis soient traités". Bin ce n'est pas forcément le cas, et ce n'est pas plus mal: l'application a peut-être quelque-chose de plus utile à faire avant de ré-envoyer le reste.
Comme j'ai de temps en temps des fichiers qui "passent mal" avec les transfers wifi de runme, je regarderai à l'occasion si je ne ferais pas bien de rajouter un "sendall" ici ou là.
PS: au passage, Beej a finalement fait une version "bouquin" de son superbe tutoriel.
Monday, May 25, 2009
Lâcher prise ...
Zeblackos (concepteur du jeu "Plop" et fan the "Thing on a Spring") m'a ému en écrivant l'autre jour:
D'ailleurs, c'est bien ça qui m'épate le plus : depuis le temps que tu es sur ce projet, je ne sais pas comment tu as fait pour ne pas lâcher prise O_o
C'est très impressionnant.
Je ne me l'explique pas. C'est à la fois chouette et effrayant. Chouette parce que bien que l'idée ait été lancée il y a près de 15 ans, je continue à bosser sur le projet -- toujours changeant, mais toujours présent. Effrayant, parce que c'en est presque une malédiction: tout "déclic créatif" finit par produire une idée pour le projet Bilou. Je me nourris des jeux auquels je joue, des bouquins que je lis, des pubs que je subis, des musiques de mon frère, de petits dessins, etc. Puis à un moment, ça donne ceci:
Non, je ne mettrai pas de plate-formes volantes comme dans Keen. C'est trop simple, éculé. Ni de haricot magique. Pourtant il faut bien que quelque-chose permette au joueur de "revenir sur ses pas" après qu'il soit tombé sans avoir recours à un passage secret par-dessous l'arbre. Puis s'esquisse un drôle de champignon au chapeau tout plat qui se transforme en ascenseur involontaire sur une feuille de papier brouillon. Une sorte de mini-strip qui prend forme, sans grande prétention pour aller rejoindre la série des "on the road". Mais ça va faire un titillement de plus qui ne me lâchera pas tant que je ne l'aurai pas transformé en "cyber-réalité" faite de jolis pixels tout colorés.
Ainsi, chaque fois que je vois la bannière sur mon blog, mon esprit prolonge le mouvement et Bilou atterri sur la souche dans un nuage de fumée qui fait "pouf". Un jour ce sera codé. Idem pour le saut d'encrier en encrier. Je changerai alors ma bannière pour y intégrer une nouvelle "toudouliste".
Tags: bilou, dev-fr, guest star, history, translate me, who am I



Vote for your favourite post

