Les mexicains vont pas aimer

Des murs !!! Ça y est, le système affiche proprement les murs définit, actuellement définit en code pour les besoins du POC (test). Vous voyez ici, dans l’ordre haut gauche vers bas droite, un N et un E, puis un S, ensuite un W et E, et enfin encore un W. Ainsi l’on constate que tout se met bien même côte à côte (EW).

Ça c’était la partie facile, au final, vu que tout était prêt avec GridBlock. La difficulté à présent est de modifier le pathfinder pour qu’il tienne compte des murs.

Premier constat, initialement nous tenons compte de l’élévation entre blocs de manière claire au sein du code, ce qui va à l’encontre d’une idée générique (entre l’idée de la librairie pathfinder et son usage en fonction du projet). Ceci dit c’était essentiel pour les POC précédents. C’est donc notre point d’entrée, il faut un moyen d’accepter ou non le fait de passer d’une case à une autre.

On retire donc tout ce qui touche à l’élévation et hauteur enregistrée jusque là, on crée une fonction que l’on envoie au pathfinder pour sa résolution, ainsi il ne connait rien de votre projet et cela vous permet de définir les règles d’acceptation, on le nommera le Validator. Pour un premier essai que tout continue de fonctionner comme avant, il renvoie toujours oui, et ça fonctionne, cette mécanique est prometteuse.

Ensuite vient le fait d’avoir connaissance des murs, car jusque là, tout ce que le préparateur de la grille du pathfinder sait c’est une coordonnée (x, y, vu qu’on a retiré l’élévation). Mais dans notre Validator nous n’avons aucun accès à la Map ou au subsetMap, nous avons juste la structure de coordonnées du pathfinder (précision importante ici).

Il faudra donc ajouter le GridBlock consulté lors de la préparation de la grille à la structure de coordonnée du pathfinder, pour garder un accès et le consulter plus tard au moment de la résolution du chemin et donc de la validation (passage d’une case à une autre) et modifier ci – et – là le fait d’utiliser cette structure au lieu de simple coordonées [x, y].

Ceci étant fait, nous avons donc un pathfinder, qui a une grille avec accès à la Map par référence, et une méthode de validation de passage le rendant générique. Il ne nous reste plus qu’à écrire ces fameuses règles autorisant ou non le passage d’une case à une autre, notre soucis : les murs.

Nouvelle question comment savoir quel murs comparer sur une case et sur l’autre ? Car oui, comme dans l’exemple initial en début d’article, on a deux murs collés EW, chaque sur leur case, il faut tenir compte des deux. Et ce n’est pas tout, comment savoir que c’est le N et le S qu’il faut comparer alors que l’on reçoit juste 2 coordonnées en entrée du Validator.

Nouveau défit, nouvelles idées. Nous avons au sein d’Element une fonction permettant de définir l’orientation. C’est grâce à cela que le caddie se tourne suivant son chemin. En divisant cette fonction en deux on a le getDirection() qui nous manquait. Dès lors la théorie à appliquer pour ce premier POC est assez simple, suivant l’orientation NESW, on compare les 2 murs des 2 cases, par exemple je vais vers le N, je compare mon mur N d’origine et le S de destination, et ainsi de suite selon toute logique :).

Et ça fonctionne !

Maintenant des portes et des interactions, car un mur avec une pancarte par exemple, sera lisible (interaction) mais : de quel côté ? condition(s) de mur mitoyen ? Etc. Beaucoup de questions qui vont demander de modifier les fonctions de l’IsoPlayer et du PlayerElement car, au final, ce ne sont que des détections ^^…

La grille, ultime frontière

Première étape cruciale dans l’évolution du système vers l’objectif HeroQuest : la grille (cf Tron Legacy ^^). L’évolution permettant l’ajout de murs et la gestion de toutes les conséquences induites, comme le pathfinder, le système de rendu, le raycasting, etc.

La solution est qu’une coordonnée de la grille, qui était un tableau d’Elements, devienne un GridBlock et c’est lui qui contient les Elements dans un de ses espaces. En gros GridBlock est lui même un tableau d’Element, un multi-tableau d’Element même, youhou bienvenue dans la 4ème dimension de la grille !

Évidemment ce n’est pas aussi basique ni aussi simple. GridBlock est volontairement une classe et non un simple tableau de plus. Ceci nous permet de prendre la partie du rendu globales des zones dans un ordre logique, de donner sur demande le contenu d’une zone et de prendre de son côté les fonctions d’insert et de retrait d’Element. Donc on rassemble ce qui peut l’être au lieu d’être au sein des méthodes ci-et-là.

On parle de zones, mais qu’est-ce donc ? En gros, nous voulons améliorer la gestion de l’empilement, et avec HeroQuest nous avons besoin de murs et portes, et pourquoi pas plus tard des indications au dessus des objets ou du héro. L’objet GridBlock a donc pour vocation de gérer des espaces virtuels pour faciliter le montage finale de la coordonnées. Il y a NESW, Center (centre), Top (dessus) et Bottom (dessous), ce qui correspond à un Ground en Bottom, un personnage ou arbre en Center, les murs et portes en NESW, etc.

Ensuite, il y a l’usage, car, dans un premier temps, j’avais tout mis dans la zone centrale, et fait en sorte que les méthodes liées utilisent la zone centrale par défaut. Ainsi, étape par étape, ça continue de fonctionner et ça permet d’isoler chaque étape de modifications.

En premier j’ai divisé la méthode qui construit actuellement la carte dynamiquement pour le POC, une grille de 10×10 qui ajoute aléatoirement des arbres. Et j’ai donc poussé le héro en zone centrale avec les arbres et le sol dans la zone du dessous. Rien de compliqué là dedans. Mais du coup le système de rendu n’affichait plus le sol (vu que c’est central par défaut).

Comme dit plus haut, l’avantage d’avoir GridBlock en tant qu’objet et non que comme simple conteneur, j’ai pu lui écrire une méthode draw() pour orchestrer le rendu par zone et donc déplacer le code initialement dans Iso vers GridBlock et ce pour chaque zone dans l’ordre désiré. Le tout en gardant les notions Z importante pour l’empilement. « Les » car Top se base sur Center, NESW et Center sur Bottom.

On a donc un caddie sur sol avec des arbres. Retour case départ, mais le caddie ne bouge plus.

C’est normal, le pathFinder se base sur un objet de type Ground et il cherche dans la zone centrale alors qu’on a déplacé dans bottom. Jusque là c’est simple de lui dire quoi faire pour corriger ça, mais est-ce au service de préparation de la grille du pathFinder de se tracasser de ça ? Non ! J’ai donc remplacé excludedTypes (types d’Element exclus du calcul) par une fonction que l’appelant implémente avec sa logique de tri.

Ainsi, un coup je peu être un personnage marchant sur des zones centrales inoccupée, autant une autre fois je peux être un oiseau parcourant les zones top. C’est bête mais important ! Du coup on a une fonction plus générique, une séparation logique propre et un résultat fonctionnel. Enfin, quand on oublie pas que le caddie occupe une place centrale et donc invalide sa position et l’empêche de bouger ^^ Bug connu précédemment revenu suite au changement de façons de procéder, excludedTypes était la solution précédente, on lui envoyait TreeElement à exclure ce qui incluait donc le PlayerElement, CQFD. Et grâce au fait que maintenant c’est géré côté appelant, donc dans IsoPlayer, il peut clairement indiquer ce qu’il veut ou non, et exclure de manière forte notre fameux PlayerElement.

Dernier détail, ce qui soulève en fait une optimisation, c’est le raycasting, donc le fait de trouver sur la carte ce que l’on survole avec le curseur de la souris. Le but est de déterminer la coordonnée survolée, et si un objet est sur la zone (et qu’il peut avoir une forme variable et donc un contour) le raycasting détermine si on est dessus ou sur une autre coordonnée, en gros.

Mais maintenant nous avons plusieurs zones, donc un travail plus long. Il faut donc prendre les zones peuplées et ne pas considérer Bottom. On simplifie tout en complexifiant le travail ^^.

En résumé, on a amélioré le système de grille avec une gestion spatiale structurée, adapté le système de rendu, de pathfinding et de raycasting, rassemblé du code et éclairci d’autres. Tout fonctionne à nouveau comme si de rien n’était mais nous ouvre à présent de nouvelles portes pour les objectifs désirés.

HeroQuest

HeroQuest est un jeu de plateau de chez MB de 1989 qui se joue de 2 à 5 joueurs. L’idée est simple, des héros affrontent des donjons, gagnent des trésors, combattent des monstres et obtiennent la gloire !

Plateau de jeu v1

Pourquoi est-ce qu’on parle de HeroQuest ? Pourquoi est-ce que je vous montre ça ? Tout simplement car le projet Nahyan stagne du fait de la distance entre le niveau actuel du moteur et ce que j’essaie d’atteindre. Et plutôt que de ne pas bouger et d’attendre la solution théorique optimale, je me penche sur un jeu simple que j’ai adoré et qui représente « l’étape d’après » de l’état actuel. Cela fait quand même quasi un mois que je n’ai plus bougé réellement.

Nous avons : une carte, des mouvements de caméra, un pathfinder, la possibilité d’interaction de base, une résolution de position au clic sur objet, un chef d’orchestre et une orientation lors du déplacement.

Il ne nous manque plus qu’une gestion de murs et de portes, un pathfinder adapté, un système de combat au tour par tour, une gestion de héro multiple (si on prend cette option), une mécanique de jeu sur des règles déjà existantes et éprouvées et un système de suivis de quêtes. On est pas loin :). Et je ne vous parle pas totalement du jeu en lui-même à intégrer…

La première étape va donc être de créer quelques éléments de design HeroQuest, de modifier le système de carte pour intégrer les murs et d’adapter le pathfinder. La découverte du donjon en se déplaçant pourrait être une seconde étape de départ.

Ensuite viendra le moment de gérer une carte complète (un donjon) et d’y ajouter, étape par étape, des objets, des monstres et des mécaniques de jeu.

Quand cela fonctionnera pour un niveau test, on pourra imaginer une mécanique de jeu globale avec choix du/des personnages avec leur fiche et inventaire et un scénario à suivre.

Certains me diront que le jeu a été réédité, que des extensions ont été ajoutées, que ça va tout compliquer, etc. c’est vrai. Mais il faut bien commencer quelque part :).

Mais avant de commencer cette première étape, nous allons faire un éditeur de donjon :).