
Maybe some blocks should be marked "free" and they aren't, maybe I really need to optimize so that I lower the allocation watermark ... or maybe it's something else.
At first, I thought some visualization of the spriteset content (preferably on SEDS's file window) would *definitely* help to figure out. Something like a 32x32 sprite can providing one-pixel-per-spritepage. But that'd be a bad idea.
I mean, *I* would find it handy, but it shouldn't be the problem of the sprite artist to organise the tileset so that sprites fit in. Doing something like "move this page to 'draft', click 'optimize' and then move the page back to 'sprite'" is a hack, not something I want to read (and definitely not write) in any user manual for my software.
The root of the problem is in the tiles allocation: it prefers allocating what has been recently freed, while it should prefer what has low values and would lead to a compact enough tileset. Think about it.
Wednesday, June 13, 2012
Repaired, and still not working
Tags: allocation, choice, coding, data recovery, english, sprite editor, todo
Thursday, May 17, 2012
Fuuuuuusiooooooon

This post will collect thoughts and battle plan for this huge refactoring attempt ... I won't get into it before I have a first school zone demo running.
| SEDS | AnimEDS |
|---|---|
| * drop the dumb 'animation editor' * leftTable could be dropped between two "restore()" of the fileWindow | * make onion skin possible, but keep OAMs for the rightTable * solve the memory management bug. |
Jusqu'ici, avoir AnimEDS et Sprite Editor dans 2 exécutables séparés était plutôt bénéfique. Je pouvais complètement foirer une "release" AnimEDS sans pour autant perdre la possibilité d'éditer des p'tits sprites sur ma DS (ou pire, de corrompre les fichiers .spr). Mais à l'usage, il est clair que devoir basculer d'un programme à l'autre est fréquent pour avoir une animation correcte. Rien que sur Dumblador (pourtant élémentarissime), j'ai du faire des va-et-vients pour re-centrer les pieds, histoire que l'animation passe sans heurts. Alors commencer une animation avec des boules en guise de pieds et mains puis "affiner" en redessinant quelque-chose de plus rayman-esque ... je n'ose même pas imaginer 0_o
Je vais donc devoir fusionner les deux ensembles de "fenêtres" dans un seul et même programme, ce qui va nécessiter un fameux effort de mise à plat du code (UML? ô UML, pourquoi es-tu UML?) histoire de garder un état cohérent entre les "feuilles de travail" pour les sprites.
Je pense que ce sera pour les grandes vacances, dès que j'ai une démo du niveau "school zone" qui est partie sur dev-fr.org.
(PS: ce post est en "brouillon" sur le blog depuis septembre 2011 ...)
Tags: animeds, choice, coding, lost projects, sprite editor
Thursday, September 29, 2011
blocking GOBs
Vous vous souvenez peut-être d'un commentaire de mon frère CJ comme quoi "il faudrait quand-même à un moment donné qu'on puisse avoir des ennemis à travers lesquels on ne passe pas même si Bilou est invincible... une chose que je n'avais pas vraiment prévu de rendre possible dans le moteur de jeu de "Apple Assault": Bilou ne sait pas traverser Funky Funghi simplement parce que dès qu'il le touche, il est blessé et repoussé en arrière. Mais pour les encriers et les plate-formes mobiles, ça risque de causer bien d'autres soucis.

Du coup, pourquoi ne pas simplement prévoir quelques actions "de base" d'alignement au niveau des expressions gobScript ? Plus besoin de définir des flags "sprite solide" ou "non-solide", "solide uniquement de haut en bas", pas besoin de venir bricoler les contrôleurs de comportement: on prévoit simplement qu'un des personnages impliqués dans une collision puisse donner l'instruction "repousse l'autre hors de ma zone de collision verticalement" ou "centre l'autre sur ma zone de collision horizontalement". Même la création d'un lien "transporteur/transporté" peut s'y retrouver. Chouettos.
on found [...] (...) is thus not allowed to use any of the 'extra context' information.
Par contre, en relisant le code, je me rends compte que seul l'objet "passif" dans une collision aura la possibilité d'ajuster sa position ou celle de l'autre objet. Toutes les notes on found sont donc en réalité impossibles à moins de changer aussi le coeur du système de gestion des collisions: il faut les ramener à on hit[]. On garde les plate-formes passives même si c'est elles qui contiennent le code qui aligne les objets.
Conséquence: si je veux qu'à la fois l(a plupart d)es ennemis et les personnages puissent déclencher une réaction avec des plate-formes "passives", il me faudra une classe "Solid Moving OBject" en plus de "HERO" et "EVIL".

Small battle plan:
Tags: attach, choice, collisions, features, newcollide, platforms, sketch, todo
Sunday, April 11, 2010
Une bière fraiche ! Dans un verre propre, nom de ... !
Avec le départ du professeur Ribbens, c'est sans doute une page de l'histoire de Montef' qui s'est tournée, quelques années (mois ?) seulement après le début de ma carrière. Comme pour beaucoup des cours que j'ai suivi, j'ai peut-être bien eu la chance à avoir reçu du maître du Scheme en personne une initiation aux techniques des continuations et du "data-driven programming". Et sa rétrospective sur les LISP machines et techniques de garbage collection était tout simplement magistrale.
In my 3rd year at University, I've finally been taught a programming language that forced me to re-think everything I thought I knew : LISP (and I practiced mostly its Scheme dialect). Beyond the charismatic character of Pr. Ribbens whose motto could more or less be translated in "brew sana in f**ing bottlore sano", it introduced me to "data-driven programming" and design of language-specific processors. To make a long story short, "data-driven" is what you feel you should be using when you start going beyond 10 rooms in a Lone-Wolf game : keep the code short, simple and generic, and have it proceed through structured data in order to obtain the desired result.
C'est de "Data-driven" justement, qu'il est question ici, puisque j'ai décidé de ne pas directement *coder* la logique de mon jeu, mais de la décrire par des structures de données traitées par un moteur qui reste plus simple et plus générique. Une approche qui me ralentit peut-être par moment mais qui me titille : je veux en avoir le coeur net et vérifier par moi-même si oui ou non il sera possible de construire un jeu de plate-formes sophistiqué de cette manière.
Les "livres dont vous êtes le héros" sont sans doute le meilleur exemple possible de "data-driven programming": tout programmeur qui a un peu roulé sa bosse "sent" bien qu'il y a moyen de faire mieux que
. Programmer une fonction par salle / évènement (en fait, par numéro de "chapitre" dans le livre) serait extrèmement pénible, la logique du jeu se retrouverait noyée par des éléments de second ordre comme "est-ce que le clic se trouve dans la zonesub Salle42
print "au détour d'un couloir obscur vous entendez un bruit sourd ..."
print "1. vous dégainez Voleuse de Vies"
print "2. vous vous avancez dans les escaliers"
input "votre choix"; choix
if choix = 1 then Salle44()
if choix=2 then VousEtesMort()
s'avancer dans l'escalier ou dans dégainer la Voleuse de Vies?" Sans parler de la difficulté à gérer les modifications du scénario. On préfèrerait de loin pouvoir stocker toutes les "données" du jeu dans un format à part ... un fichier texte avec des "macro-commandes" pour les vieux patchs de mon genre (et leurs mentors) ... un document XML pour les afficiandos de l'UTF-8 et autres codeurs post-moderne. Une S-expression pour les fans de "recueil de petits problèmes en Scheme", je présume. Peu importe, finalement, la forme: ce qui comptera, c'est le fond, la sémantique, le modèle sous-jacent.My "game script" and the state-machine-based-monsters is deeply influenced by this technique. Unlike your regular scripting language (Lua ?), building a data-driven game engine means that you're building with line of code a software microsystem that will process data in a specific context and for a specific purpose. You are free to define the line between code-bound function and data-driven function, and placing that line at the right place will be the key to efficient processing.
Bref. Pour mon jeu de plate-forme, le "modèle" de données est un peu plus complexe, fait en partie de pixels et de commandes qui décrivent les machines d'état des différents intervenants. Une réminiscence du cours "Ingénierie du Logiciel Orienté-Objet" et de ma confrontation avec l'UML, et dans une moindre mesure, avec le formalisme des automates à états fini. La frontière entre data-driven programming et langage de script complet (cf. microLua pour DS) est sans doute ténue ... Elle explique sans doute la décision parfois curieuse de garder certaines choses "en-dehors du script". La détection des collisions, notamment, ou la prise en charge des animations. J'ose espérer que cette volonté de "rester juste un cran en-dessous d'un DS-Basic" me permettra de garder un moteur de jeu suffisamment efficace.
Avec mon "InspectorWidget" désormais opérationnel, on se rapproche aussi d'un éditeur graphique pour ces machines d'état ... Et certains "défauts" du modèle actuel deviennent flagrant. Par exemple, Bilou saute, cours, nage, attend ... autant d'états que je dois déclarer et que je relierai ensuite les uns aux autres par des transitions, p.ex. "lorsque les boutons changent, si le bouton 'saut' est enfoncé, alors modifier la vitesse verticale et passer dans l'état 'saute'". Par contre, pour que l'appleman puisse être assomé quand Bilou tombe dessus, il faut que tous les états de l'appleman mêne vers l'état "appleman assommé" si la 'bonne' collision se produit. Il est facile d'en oublier l'un ou l'autre en cas de modification, et c'était d'ailleurs en partie la raison pour laquelle il était si difficile de s'en défaire dans les démos précédentes du jeu.
UML state machine formalism was one of other things I learnt that very same year, and it looked like a powerful way to express behaviours while avoiding repetitive boilerplate code to be described ... So my current data model for GOB behaviours is mostly implementing that. So far, it has the limitation that a transition is always flowing from exactly one state (to exactly one other state). It's expressive enough (that is, there isn't a behaviour you cannot implement with that), but it's not code-friendly in that when you actually want to implement a transition that should apply from (almost) all other states to a specific state, you can quickly forget something. It actually occured with the Appleman that couldn't be stomped when walking to the right simply becaused I missed some
state20->state33 on hit0 [t] statement.
Je cherche donc depuis quelques semaines à ajuster le modèle de manière à pouvoir justement exprimer "depuis tous les états, ..." ou "pour tous les états où X est au sol, ..." et donner une transition unique qui s'applique à un groupe d'état d'entrée. Comme prévu de longue date, d'ailleurs (cf. ce schéma du comportement de l'Appleman). Outre l'économie de temps de parsing au démarrage du niveau et de quantité de mémoire (à mon avis négligeable), celà permettrait de pouvoir d'un seul clic dans le débuggeur forcer un point d'arrêt sur "je vais me faire blesser", quelque soit l'état actuel de Bilou. Pour l'instant, avant le debugging, il était nécessaire de passer tous les mouvements de Bilou en revue pour cliquer sur "transition vers l'état n° 15" depuis chaque état. Pas franchement folichon.I haven't added such "group transitions" yet, but at least I made a nice step towards it by letting states and animation be defined in the context of .cmd files, so that each monsters' state machine can be written without having any knowledge of what other monster do and how many animations they use. Then, only a small amount of the states are "imported" by the master (level) script.
I'll have to adapt the level editor accordingly, but it should make InspectorWidget more friendly to use as the 'kind' of monster is now part of the state's name. Moreover, once the group transitions are in, activating a breakpoint on "jumping->hit" should equally activate "standing->hit", "walking->hit" etc. as long as you used a group transition for "{jumping, walking, standing, ...} -> hit"
Un premier pas dans cette direction, ç'a été de donner à chaque fichier ".cmd" (généralement un par personnage ou ennemi) son propre "répertoire" d'états et d'animations, désormais indépendants de ce que font les autres. Seuls certains de ces états seront "importés" par le script qui définit le niveau en cours pour placer les intervenants sur la map. L'éditeur de niveau devra être adapté, bien sûr (eeh oui. Faire et défaire ... that's the question), mais ce sera pour un mieux: plus besoin de passer en revue toutes les positions de Bilou, ni de savoir lequel des états de l'appleman convient comme état initial. Les groupes de transitions devraient s'y greffer plus agréablement.
Et InspectorWidget lui aussi en bénéficie, puisque les monstres sont maintenant nommés "fu01", "ap07" ou "wo00" plutôt que d'être représentés avec un simple nombre.
Thursday, January 14, 2010
Bilou Dreams : Clone it !
Bilou sera un jeu de plate-formes. 8 zones différentes à explorer, des batailles épiques contre des boss à pics. Mais je sais aussi que je dois me mettre des étapes intermédiaires pour éviter que le projet ne tourne à rien, et j'ai la sensation que c'est à travers des mini-jeux permettant de tester les différents aspects sans exiger la cohérence de l'ensemble que j'y arriverai. "gedsdemo-999" en était un exemple. Ca me permettra aussi de m'assurer que les "gedstool" pour le game-making sur DS ne se limitent pas à un éditeur de niveau spécifique à un seul jeu. Voilà donc les quelques idées qui me trottent en tête pour la prochaine release sans que je ne puisse me décider. Mais vous avez voté, comme en témoignent les nombres à côté de chaque "titre de jeu".
Bilooyan (1/8) -- [forget it]

Biloubulus (2/8) -- [forget it]
Apple Assault (3/8) -- [released in 2010]

Deep Ink Pit (4/8) -- [work in progress, 2011]

Rescue Mission (1/8) -- [forget it]
Inspired by vvvvvv, since Bilou and Bouli are space explorers, and given that game deserves better graphics. pros: provides some backstory. I'd love to tell a story about a space station in distress where things have turned wild since ... Blork Carnage. Simple (seemly) mechanics. Nice puzzles cons: would be fun only if I've got similar maps and if you can discover the plot. Graphics & Sound ?
Bin, finalement, Bilou et Bouli sont des explorateurs de l'espace. Donc un jeu comme
vvvvvv, ça pourrait marcher. pour: ça permettrait de construire un peu les personnages (qui étaient-ils avant de se planter sur la planète étrange ? etc.) Et puis j'ai toujours révé de raconter une hist:oire sur une station spatiale à la dérive. Les mécanismes sont plutôt simple. contre: il faudra des maps, et encore des maps. et une histoire. Et je n'ai rien au niveau graphisme & son.
Bilou "Mining" (6/8) -- [mid-term development]
Some sort of a "manic miner : lost levels" clone, with a more complete collision and physics engine. And "32-bit" pixel art. I'd love to achieve it since i've seen Arachne's mockup (just on your left. Yep, that one). This is straight continuation of the current "gedsdemo" I already provided, where you'd add conveyer platforms, ladders and the like. pros: in progress. cons: Needs work on the level editor to be continued. Needs some "real stuff to collect" (not just apples: I cannot come with a coherent backstory for "collect apples so that you can pass through the trunk"). And golden keys and doors would be completely misplaced in Bilou's woods.C'est la continuité du "gedsdemo" déjà disponible. Récolter des items pour que la porte s'ouvre et pouvoir passer au niveau suivant. C'est le principe de Qwak, de Manic Miner et de Johnny Biscuit. pour: déjà en cours, contre: pour rester intéressant, il faut régulièrement introduire de nouveaux objets/mécanismes au fil des niveaux. Le jeu demande un éditeur de niveau plus costaud. Je n'ai pas de "clés & portes" adaptées à la forêt de Bilou.
Tags: apple assault, bilou, brainstorm, choice, deep ink pit, english, game design, greenzone, mybrew, NutsnBolts, poll, school zone
Monday, July 13, 2009
Les plate-formes

Le prochain "gros challenge" pour mon moteur de jeu (sur lequel je cogite actuellement, en tout cas), c'est sans hésitation les plate-formes. Tous ces objets mobiles, assenceurs, radeaux, etc. qui tout en ayant leur comportement propre altèrent celui du personnage ou agissent comme des parois solides. Bref tout une série d'objets qui vont venir perturber les belles règles de gestion des collisions du moteur de jeu.
Technically speaking, the next challenge for my game engine is platforms. Those moving objects you'll find in any decent platformer that have their own pattern and that alter the player's behaviour (and sometimes ennemies' as well) to create interesting levels and gaming experience. It's still very sketchy in my mind, in "look for use cases" stage that happens to take place by drawing gaming moments in this project -- fairly funnier than rationale UML, if you ask ;)
J'en suis encore fort au stade "conceptuel", qui est franchement sympa dans ma manière de travailler, puisqu'il s'agit de "mises en situations" à coup de crayon pour voir si la solution que j'ébauche tient suffisamment compte des différents aspects. Du style "non, on ne peut pas lier définitivement un contrôleur à une plate-forme, parce qu'il pourrait y avoir aussi des petits vers sur les champignon-ascenseurs".
Après deux ou trois tentatives peu convaincantes, j'en arrive à la conclusion que mon système "un contrôleur par état" est un peu limitatif. De la même manière que j'essaie de faire de la composition de micro-programme dans mes recherches sur les protocoles réseaux, pourquoi ne pas faire de la composition de contrôleurs dans le moteur de jeu ?
Encore trop tôt pour commencer à faire des changements dans le code, mais on dirait bien que ce genre de mécanisme qui transforme la fonction de contrôle unique en "lire_le_pad -> pousser_un_objet -> suivre_une_pente -> marcher" s'appliquerait aussi bien aux objets déplaçables ou aux pentes fixes (en plus des plate-formes mobiles). Le même micro-comportement "marcher" pourrait prendre comme "input" la lecture du pad directionnel ou la position relative de Bilou par rapport à un appleman ...
Plein de choses intéressantes, donc, que je continuerai à creuser dès que j'aurai un peu dompté la forêt vierge qui me sert de jardin.
The thinking about supporting moving platforms also questions the way i'm coding controllers, that is, a large chunk of code that does everything a specific state needs, including reading Dpad, adjusting speeds, checking that the current move is possible and make it possible if speeds need to be aligned to avoid entering walls. Such an approach doesn't let me easily alter behaviour due to a lift or a pushable ennemy.
Instead, i'll investigate the option of a chain of controllers, each of them doing only a simple action such as reading the pad, or updating speeds according to momentum. It could also be "zero speed if character pushes into the moving block's direction" or "prevent the character from falling as long as he's on a platform". Still a bit early to change all the code base, but ...
walker or dpad got completed by some motion-smoothing momentum or all-purpose decrease that turns any variable of your entity into a timer/counter.
Tags: bilou, choice, collisions, greenzone, iGobController, inkjet, platforms, pushing, school zone, sidescroller, sketch, slopes, spongebop, woodworm
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
Friday, May 22, 2009
Moon walk ?

TODO: moving Bilou through the animation isn't the right way to go. Maybe "delay x" in the animation sequence to have the next frame triggered only when we move to the next pixel would be better
La solution que je pensais y apporter était relativement simple: intégrer les déplacements dans l'animation à coup de "move x y" entre deux images. C'est comme ça que je déplace le wooworm et ça réussit plutôt bien. Le hic, c'est de combiner ça avec une vitesse éventuellement variable et le test des collisions qui a été ramené dans le contrôleur (qui ignore tout de l'animation en cours).
C'est Pierrick qui m'a donné la solution a mon problème en racontant comment du temps du CPC il faisait faire des sauts "réalistes" à son petit bilou en modifiant la durée d'affichage à chaque emplacement vu qu'il lui était impossible de placer le bilou (eh oui, c'était lui) entre deux tiles (pas de sprites en CPC basic ?). Plutôt que de chercher midi à 14 heures (du genre "modifier la vitesse d'écoulement du temps pour que Bilou coure plus vite"), j'ai juste changé l'interprétation de "move x y" en "ne passe à l'étape d'animation suivante qu'une fois que le contrôleur aura 'accumulé' le décalage suffisant. Traduit en code, ça donne :
inline bool trymove(int dx, int dy) { forcechecks=false; if (selfmove) { int cflags = cast==HERO?F_PLAYERTHRU:F_MONSTERTHRU; bool notyet = (dx>0 && cdata[4]<dx) || (dx<0 && cdata[4]>dx) || (dy>0 && cdata[5]<dy) || (dy<0 && cdata[5]>dy); if (notyet) return false; if (cando(dx,dy, cflags)==cflags) { x+=dx; cdata[4]-=dx; y+=dy; cdata[5]-=dy; return true; } else { cdata[4]=0; cdata[5]=0; return setstate(state->dochecks(x>>8,y>>8,world, cdata)); } } else { x+=dx; y+=dy; return true; } }
- selfmove est défini par état : true pour monter à l'échelle, false pour tomber, etc.
cdata[STEPX]et[STEPY]accumule les valeurs decdata[XSPEED]etcdata[YSPEED](vitesses définies par le contrôleur) en mode "selfmove" (normalement, on a directementx+=cdata[0])trymove(dx,dy)est appelé lorsqu'une étape d'animation utilse la commande "MOVE"- condloop et check permettent de vérifier les testpoints sur des frames données (p.ex. quand le personnage a de nouveau les pieds au sol).
cdata[])will thus be used to accumulate some intended move until that "step" size becomes large enough for the move x y instruction found in the animation list. The trymove(dx, dy) tells whether such a move is possible right now or must wait until more motion has been accumulated.
Friday, February 27, 2009
World Collisions
Left alone, testpoinst are not really a satisfactory way to handle collisions between objects of a game and the world.
First they don't cover enough and cannot catch all the odd situations. Testpoints are points and they cannot detect the case where you're jumping through a corner unless you have many of them (up to one per tile your character covers).
Je ne suis plus trop emballé par les tests-points tels que je les avait décrit précédemment. En premier, ils ne capturent suffisamment toutes les collisions. A moins de les multiplier, on risque toujours d'avoir une partie du personnage qui rentre dans un coin de mur lors d'un mouvement, et en donnant à chaque état sa propre liste de testpoints, on ne fait qu'augmenter les possibilités de bugs.
Second, they are only boolean. They don't let you express that a fish can only move into water, that a bird can only move in air, and that a frog can move through both, but that only the ghost can also move through walls (and i'm sure we can come up with a negative space creature that can only walk through/over walls). This is a situation much more common that one could think. As soon as you add ladders to your levels, you want to express that climbing the ladder "is only possible over ladders tiles". That was a quite strong restriction of Recreational Game Maker where the only way you could create a ladder for a platformer game was to set gravity=0 on ladder tiles ... As a result, your character zooms through ladders if you press the 'jump' key and miserably 'fail to climb up the air' if you press the 'upwards' when he's not over a ladder.
Le deuxième souci avec les test-points, c'est qu'ils sont simplement binaires. Alumés ou éteints. Point barre. Pas moyen de préciser qu'un poisson ne peut se déplacer que dans l'eau ou qu'un oiseau ne peut aller que dans l'air, mais que la grenouille peut évoluer dans les deux milieux. Si on y réfléchit bien, ce genre de situation apparaît bien plus souvent qu'on ne pourrait le penser. Le simple ajout d'échelles dans nos niveau oblige de définir que "escalader une échelle" ne peut s'envisager que s'il y a une échelle à escalader. Le Recreational Game Maker sur lequel j'ai développé (entre-autres) la série des badman ne permettait pas ce genre de subtilité: une échelle était tout simplement un bloc sans gravité, de sorte que l'on pouvait y monter en sautant -- à une vitesse franchement irréaliste -- et on aurait vu le personnage ridiculement patauger dans l'air en faisant de petits bonds si on avait appuyé sur la touche "monter à l'échelle" alors qu'il n'y avait aucune échelle.
The approach of "the nature of a tile" is thus interesting: it can express (through flags) who can do what over that tile. This is a concept used both in Xargon and (afaik) in the side-scrolling Rayman game, where you have "monster-walls" that monsters can't cross (though Rayman don't feel them). This is imho an elegant solution to many level design problems. Let's take the walking block of "Inside the Machine", who wanders around its initial location, "guarding" a virtual spot even if it could theoretically walk further. Morukutsu relied on a "distance" counter that is increased/decreased through the animation code, which won't let you alter the 'coverage area' of the ennemy through level design, but only through implementation ... not so fun.
L'approche "nature du tile", telle qu'elle est utilisée dans Xargon est intéressante à plus d'un titre, et je soupçonne les concepteurs de Rayman (la version 2D) d'avoir suivi une approche similaire. En particulier, elle permet l'introduction de "grillage à monstres", des blocs invisibles et non-bloquant pour le personnage et les projectiles, mais qui sont assimilés à un mur par certaines plates-formes mobiles et par certains monstres. Comme je l'avais déjà précisé lors de l'analyse des maps de Commander Keen 5, je trouve que c'est une technique élégante pour construire les niveaux. Si je compare à "Inside the Machine" (l'autre jeu dont j'ai étudié les sources pendant mes longues soirées d'hiver en Suisse), l'alternative consiste à programmer les monstres de sorte qu'ils ne s'éloignent jamais de plus de n pixels de leur position initiale, en maintenant une variable supplémentaire "distance" qui les force à faire demi-tour. C'est exactement le genre de "pré-câblage de code" que je cherche à éviter. Décider jusqu'où un monstre va, c'est une question de level design, pas de programmation du comportement des monstres.
The final argument is that some testpoints are typically more important than others, and there is a strong relationship between which testpoint is triggered and which movements you can do. A problem i have with the current implementation of collisions in Bilou is that as soon as one of the testpoint checks fails, the whole move is cancelled. If you fall along a wall, you don't expect your character to "stick" on the wall because you keep pressing the D-pad in the wall's direction. Yet, that's exactly what Bilou does right now.
Instead, we'd like something like
if cando(dx,dy) then (x,y) := (x+dx,y+dy)
elsif cando(0,dy) then (x,y) := (x,y+dy)
else {
(x,y) := (x,align_on_tile(y+dy));
trigger_testfail_event
}
where cando(deltax, deltay) checks the whole area covered by the character at a new temptative location. But still, you don't want such behaviour for all your monsters, and it's certainly something that is specific to jumping. You wouldn't see this ever in a Zelda game : instead you'd have the character "walk around" a block if he's sufficiently near to the edge of that block. So this logic is really controller-related rather than being something that belongs to the game engine itself.So, do we still need any testpoint ? after all, Xargon's cando() used in controller-specific code seems to be fine. Actually, there's something that cando() cannot catch: mixed situations. The controller will be able to tell the game engine that it's no longer possible to fall, but it can't tell whether we are now landing, diving, or bouncing against a corner. This is where we *really* need test-points, and you'll notice that testpoints are only used for state transitions.
I still have to figure out the best way to convert my codebase to this new approach -- the fact that i'm busy working on the real-world house not really helping -- but i think it might be what i've been looking for since the start of the project.
Enfin, certains testpoints sont clairement plus important que d'autres, et le déplacement indiqué par le contrôleur doit être ajusté en fonction de la situation. J'ai le problème dans le saut de Bilou, actuellement: quelque soit le testpoint qui détecte une collision, le mouvement complet est annulé. Résultat, si Bilou rentre dans un mur lors d'un saut, il va rester "collé" au mur jusqu'à ce que je relâche la touche de direction. Celà n'arriverait pas si je pouvais donner une priorité, ou une description plus complète du rôle des différents testpoints (p.ex. puisque c'est un test-point "mur", il n'annulerait que le déplacement horizontal, etc.)
Mais en fait, ce genre de logique est commune à tous les déplacements guidés par la gravité dans un jeu de plate-forme. Ce n'est pas quelque-chose de spécifique au comportement de Bilou, et celà pourrait sans difficultés être introduit dans le contrôleur du sprite. En tout cas, ce n'est clairement pas à introduire dans la classe GameObject elle-même, puisque si on veut faire un r-type, un boulder-dash ou un zelda, on en aura pas besoin.
Mais alors, à quoi servent encore nos test-points ? eh bien, à décider de quel sera le prochain état lorsque le contrôleur signale qu'il n'est pas possible de continuer. Ca veut dire aussi que le contrôleur devra évidemment ajuster la position du sprite de manière à respecter les "cando()" mais pour que les testpoints puissent détecter quelque-chose quand-même :P
- [done] extract persistent state (found in map and accessible by other classes)
- [done] iGobPubstate::cando(x,y,flags)
- [done] manipulate tile_flags array (that translate tile type into tile flags) from the script
- [done] controllers report "fail" when no "cando()" can be applied
- [done] who updates coordinates ?
- [done] update GameObject::play() and trymove()
Tags: choice, climbing, coding, collisions, core, done, english, features, iGobController, sidescroller, testpoints, tiled, xmllint
Tuesday, January 20, 2009
analysis paralysis ?
Près de 3 mois loin de chez moi, maintenant, et toujours pas de petit homebrew à vous mettre sous la dent. Okay, mon laptop est un char à boeufs comparé aux PC que j'ai à la maison. Okay, je n'ai pas de Wifi dans mon k0dek et le laptop n'est pas configuré pour faire base station pour la DS mais il y a autre chose.
Je sais qu'il y aura pas mal de choses à mettre en place pour parvenir à réaliser Bilou sur DS ... je veux dire par là, "le jeu de plate-formes tel qu'il avait été prévu dans les années '90 et prototypé sur BASIC", mais revu et corrigé pour la DS, bien sûr.
Par exemple:
- [just started] la gestion des bordures. Même si je garde au départ un système tile-based, les plate-formes mobiles et autres sables mouvant devront être capable d'altérer le comportement des sprites
- [ongoin] les pentes. Je veux que Bilou puisse se déplacer sur des plate-formes qui ne soient pas uniquement horizontales.
- [done] l'animation modulaire. ou "à la rayman", qui me demande un éditeur d'animations spécifique en plus des ajustements dans le moteur de jeu
- [done] les collisions perso-bloc qui soient plus maniable que juste le "petit bonus" que j'ai pour l'instant. Plate-formes qui disparaissent, échelles, etc.
- [done] des réactions aux collisions perso-monstre. Avec une question pertinente "et comment fait-on perdre une vie, au fait"
Je vais devoir me fixer une "roadmap" jallonée de petits projets intermédiaires, et attaquer les difficultés techniques une à une, sinon je vais finir par recommencer à programmer sur Clicker :P
Bonne résolution 2009 #1: on garde Bilou. Quelque soit le petit jeu envisagé
Bonne résolution 2009 #2: un nouveau thème graphique si et seulement si un jeu est produit.
Bonne résolution 2009 #3: s'il n'y a pas d'image de fond (ou si l'image est fixe, etc.) ce n'est pas un problème pour l'instant.
Bonne résolution 2009 #4: faire la vaisselle ce soir
edit: côté "base station", j'ai au moins pris la peine de recompiler le module de mon stick WiFi. M'en vais aller manger une soupe et tester ça @home, tiens.
edit++: toujours pas de connexion Internet au k0dek, mais au moins, j'ai su modifier runme pour qu'il puisse démarrer un programme 'runme-compatible' après l'avoir téléchargé.
3 monthes in Switzerland and still no updates. I'm not focused enough and therefore I'm missing the #1 golden rule of game development : *release*. Having no Internet access in my Swiss flat doesn't help, so I modified runme so that it can launch the last compatible .nds it received from a WiFi transfer. Using my laptop as an access point, it allows ad-hoc development.
Allez, je vais essayer de me faire un petit "ramasse toutes les pommes pour passer au niveau suivant". Ce sera déjà un bon début. Après tout, c'est fort le principe de base de Qwak, Bubble Bobble et même Still Alive DS :P
Friday, May 02, 2008
*bounce*
Un nouveau pas important pour la réalisation de Bilou a été franchi: j'ai mon évaluateur d'expressions, et son intégration au moteur de jeu est quasi-parfaite. En clair, cela signifie que je peu maintenant indiquer via mon scripteur de niveau que lorsque Bilou arrive sur le sol, il doit:
- rebondir si sa vitesse est trop élevée
- s'arrêter si la vitesse n'est pas trop grande.
Côté BASIC, c'était la liberté totale pour les algorithmes et les comportements des monstres, mais les possibilités graphiques restaient restreintes -- même sur Pentium '90. Côté GM, c'était le constat inverse: scrolling dans des niveaux relativement grands, un nombre d'animation et de monstre présent à l'écran quasi illimité (au point que Pascal s'est même servi de monstres pour faire les bonus de notre remake de Pop'n'Twinbee). En revanche, le GameMaker était totalement incapable de gérer un saut un peu potable. Le simple fait d'avoir un sprite différent pour la montée et la descente du saut était impossible. Alors faire faire une cabriolle au perso quand il arrive au sommet de sa parabole (comme dans Bilou sur Basic), vous pensez bien ^_^.
About 10 years ago, i decided that neither QuickBasic nor recreational software's GameMaker could still fullfil my needs for creating games. I wanted parallax scrolling, compound sprites (think of rayman, but i already had it for Bilou in QB), and most importantly, more flexibility in creating monsters attack patterns.
I don't want to have to program them at the lowest level of machine code, i want their behaviour to be part of the game data, not part of the game engine.
When restarting game development on the DS, i decided to opt for something that would be state-machine inspired, but capable of reacting to the level map, the hero's position, etc. How to actually make it working came up later while reading the "making of Another World" .
Bref, à part des fioritures comme le parallaxe ou des sprites composés (à la Rayman), mon projet "Ultimate Game Maker" devait surtout permettre une plus grande souplesse de programmabilité : un scripting des actions : ne pas se limiter à des transitions "d'un état à l'autre" pour la gestion des monstres, etc. mais permettre plusieurs transitions depuis un état en fonction de l'état actuel du monstre, et une modification de certains de ses paramètres. Comme vous pouvez le voir sur l'image, je vous concocte déjà un petit 'appleman' bien particulier grâce à ce nouveau mécanisme.
C'est en lisant un making of du jeu Another World que l'idée est revenue au premier plan pour la DS. Dans Another World, Eric Chahi a choisi de disposer d'un environnement de développement pour son jeu qui ne nécessite pas de recompilation entre deux tests successifs, mais aussi la possibilité d'écrire toute la logique du jeu indépendamment de la machine considérée. Le jeu étant développé sur Amiga500 et constitué exclusivement de polygones rendus 'à plat' (à partir d'image filmées, il n'y a donc aucun calcul 3D), il n'est pas possible de s'en tenir à un "BASIC" traditionnel, trop gourmand en temps de calcul. Eric nous concocte alors son propre petit langage, mélange d'assembleur et de BASIC (oui, quand-même) dans lequel il va exprimer toutes les réactions du jeu (genre "quand le laser entre en contact avec la base du rocher, le rocher se décroche et s'incline en oblique").Aucun élément compliqué n'intervient (pas de chaînes, pas de structures de données complexes): Eric utilise uniquement 256 variables entières pour représenter l'état du jeu, et le plus souvent, il ne les nomme même pas.
Même le maniement du joystick est géré de cette manière.
Another World was coded by Eric Chahi on an Amiga 500. He wanted cross-platform game logic for a game whose logic is *much* more complex than a shoot'm'up, and he also wanted to avoid recompilations between two tests. He thus naturally opted for some scripting language -- a mix between BASIC and assembly -- through which he controls animations and game variables (just integers).
Well, that's more or less what i'll do for my sidescroller game engine, except that you'll have per-object (monster, hero, switch...) variable in addition to the global (per-level) variables. In my case, i can even make the scripting simpler than Eric's "bassemblic" as it will essentially be used in predicates and actions of a state machine. So no control flow is needed at all.
Eh bien, je me suis dirrigé dans la même direction avec mon Game Engine, mais en donnant plutôt 16 variables par personnage (oh, il y aura aussi des variables globales comme dans Another World, rassurez-vous). Je me suis évidemment inspiré de mon interpréteur WASP qui m'a valu mon doctorat pour le bytecode, mais en simplifiant encore un coup: il me faut des expressions, pas des programmes ici. Donc exit toutes structure if-then-else ou les boucles, qui peuvent se coder par la structure de la machine d'état. Reste à inclure quelques instructions supplémentaires (p.ex. créer un nouveau monstre ou jouer un son), mais la base est là, même si mon 'langage' est encore plus moche que le bassemblic de Eric Chahi.
# gestion de la chute de Bilou.
state4 :anim0 {
using gravity
testpoint off (4,16)
testpoint off (12,16)
}
state5 :anim0 {
using stopper
testpoint on (4,16)
testpoint on (12,16)
}
# si la vitesse est trop élevée, on rebondit en la réduisant de moitié.
state4->state4 on fail [v1 $40 >=] (v1 2 / ~ :1)
state4->state5 on fail [t]
En avant. Essayons de voir si j'arrive à fixer la caméra sur Bilou pour le promener dans le niveau. Et en même temps, je vais tenter de faire un petit .nds de démo pour que vous puissiez tester ça sans devoir comprendre le fonctionnement de mon 'runme'.
Tags: choice, coding, collisions, english, gobscript, milestone, mybrew, sidescroller, state machine, testpoints, uml
Tuesday, February 26, 2008
ntxm->play();
j'ai des petits soucis avec la bibliothèque mikmod qui aurait du servir de soundplayer dans mes jeux. De plus en plus de soucis, en fait ... Du coup, j'ai l'intention de passer à libntxm, le modplayer qui est au coeur de l'excellent NitroTracker de 0xtob, et qui nous fait pleinement profiter des capacités audio de la console (mixage hardware sur 16 canaux).
Le seul hic, c'est que pour l'instant, je n'arrive pas encore à lui faire cracher du son dans runme :( Une fois que ce sera règlé, il me restera à assurer le support des S3M (minimum) et des IT (là, ça va être plus chaud) pour pouvoir profiter des petites musiques du frérot.
Howdy! I got libntxm working in runme this lunch time. I was getting less and less satisfied with libmikmod's quality (corrupted playout buffer) and wanted to switch to hardware mixing instead. This is still a very basic test atm (e.g. just playing a pre-configured XM song when you enter the "beam out" window), but you can expect "play XM received by WiFi" feature within the week ...
Once i get that done, i bet i'll try to write a S3M reader (drawing inspiration from mikmod's source, of course) and add support for S3M effects to the libntxm.
That should bring me most of my bros' music library (before he switched to Impulse Tracker -- which will be a higher challenge to deal with) for my games, with minimal CPU load and best possible output quality.
edit: okay. ça marche. J'avais fait l'andouille suprême: contrairement à la bibliothèque libmikmod, libntxm n'utilise *pas* le FIFO interne de la DS pour transmettre ses commandes. On a tout simplement un tableau circulaire dans lequel les commandes successives (e.g. playSong, stopSong, etc.) sont inscrite et que chaque "moitié" du modplayer manipule. Le tout est placé dans une zone de mémoire spéciale (qui sert également pour s'échanger les coordonnées du stylet, et tout ça).
Et pour la petite histoire, la bibliothèque réseau dswifi, elle, utilise encore un autre mécanisme (une zone de transfert dans la RAM principale, mais dont l'adresse est bidouillée pour court-circuiter le cache du processeur). Là, l'ARM7 apprend l'adresse de la structure (allouée côté ARM9) lors d'une phase de dialogue initial à travers le FIFO, et les mises à jour de la structure sont également annoncées "over FIFO". 0xtob, lui, reprenant le code du modplayer posté sur le forum GBAdev, vérifie gentillement si un nouveau message est arrivé tous les 60èmes de secondes (et donc dans le "Vblank" handler plutôt qu'en réponse à un évènement sur le FIFO :P)
Ca y est ? vous êtes perdus ?
Thursday, November 22, 2007
Scrollons encore
On discutait scrolling avec Cyril hier midi, et il m'a fait remarqué assez justement que je m'inquiétais inutilement pour les scrolling à grande vitesse. Bin oui, tant qu'on ne scrolle que d'un pixel à la fois on peut s'en sortir très bien avec seulement 512 pixels de large dans la mémoire vidéo. Au cours du décalage, on finira bien alors à se retrouver avec un morceau de 256 pixels appartenant à un seul bloc de VRAM à l'écran et on a les mains libres pour changer le contenu du deuxième.

Mais si la vitesse devient trop élevée, on risque de se retrouver à passer d'un 'bloc' à l'autre mais en gardant toujours plus d'un bloc de VRAM à l'écran, ce qui nous empèche de mettre l'image à jour sans prendre le risque d'un affichage inconsistant. Mais c'est là que Cyril est intervenu: rien n'empèche d'arrêter le scrolling plus tôt que prévu juste pour ce 1/60eme de seconde... On a alors de nouveau les mains libres pour copier le bloc suivant en VRAM, et au coup suivant, on scrollera un peu plus. L'un dans l'autre, ça devrait être invisible (edit 26.11) J'ai testé sur le temps de midi: c'est indécelable.
Problem: at speeds higher than 1px/frame, we might never have one of the squares fully off-screen because we switch from "still showing some pixels from the logical square on our left" to "already showing some pixels from the logical square on our right.
Proposal: snap the camera to the squares grid when a transition is detected. That snapping will affect the scrolling speed, but only for 16.6ms so it shouldn't be noticeable.
Result: that looks smooth, indeed. I cannot notice when the snapping happens.
Reste un autre problème: l'encodage des "types" de blocs. Si je faisais une copie élément par élément à la main (jouable si on remet un bloc de 8x8 pavés à jour d'un coup), je pouvais chiper les bits "miroir" pour encoder les types dans la carte "offscreen", et les remettre à zéro avant la copie dans la surface "onscreen".Mais pour remettre tout un bloc à jour (2K), il serait franchement préférable de passer par le DMA.
Mais bonne nouvelle, on peut s'en sortir avec un encodage un peu plus subtil. Imaginez: on a besoin d'encoder si chaque tile est solide, traversable, incliné, etc. mais aussi s'il s'agit d'un bonus, de pics, de lave, etc. Trop pour 4 bits ... sauf si on encode que 12 types de tiles et que l'on se sert des numéros 12 à 15 pour des blocs plus "spéciaux", par exemple ceux dont seul le personnage du joueur doit s'occuper.
Idea: Let's have 12 "direct" properties for slopes, ground, air, etc. and have only other properties for 16x16 pixels blocks. That means we can combine the palette bits of 4 tiles to know what block (out of 256 possible special blocks) we use.
Alors, quel est le truc ? Eh bien voilà. Notre niveau n'est pas construit à partir de tiles 8x8 mais à partir de blocs 16x16. On peut donc supposer que la lave prendra bien 16x16, et ce n'est pas juste 2 bits (de 12 à 15) que l'on a pour faire la différence entre les picots, la lave ou les bonus, mais bien 4x2 bits (2 dans chaque tile faisant partir du bloc 16x16). Soit 256 types de blocs spéciaux possibles. De quoi voir venir...
edit : au final, seul le scrolling vertical utilisera des transferts DMA.
Tags: choice, coding, jump-thru, scrolling, sidescroller, special block, tiled, translate me, tutoriel, vram
Wednesday, June 14, 2006
J'attaque la DS
Ce coup-ci je m'y met: je me lance dans le développement sur nintendo DS. Bon, ne vous attendez pas à du sensationnel genre "Bilou en 3D" ou "Badman IV" ni même un portage de Clicker (bien que ce serait fun).
Finis les grands projets még-à-l'eau: j'attaque par la base. Petits "sprites editors" et autres qui serviront de base. Ce serait déjà bien fun si je pouvais créer et animer des persos depuis mon fauteuil, transférer tout ça sur PC et le ramener dans un jeu plus tard.
En plus, d'après mon expérience sur PC et C64, c'est encore par là que c'est le plus facile d'attaquer: pas besoin d'avoir une tonne de texture et compagnie pour s'y mettre ... Puis je ferai tourner le "tracker" pour DS dessus et je prête la bête 2 semaines à mon frère pour qu'il me fasse une zik ou deux, et c'est parti pour "Bilou Sky Quest DS" :)
Let's go! i'm starting DS development. I don't bother doing large projects such as Badman IV or a 3D Bilou game. This time, let's make it simple first. Small editors for pixeling and animating characters from my armchair would already be a nice starting point. Maybe our "Bilou Sky Quest" shoot'm'up could be a starting point after i integrate a modplayer...
http://www.aaronrogers.com/nintendods/wifime.php
http://www.devkitpro.org/
http://www.double.co.nz/nintendo_ds/
http://www.dsdev.org/ -last updated in 2005. See https://web.archive.org/web/20100505045923/http://www.dsdev.org:80/
Saturday, December 31, 2005
choice (t.a.g.)
You've reached the last post about major project choices.
These posts sets the boundaries of what I do and what I let for others to do. Not all of them are technical choices, but when they are, they usually have a significant impact on what the project will support.
Tags: choice, tagtionary




Vote for your favourite post
