Le moteur 3d ECO
De profondis
De profondis est le squelette d'un jeu où 4 aventuriers tentent de quitter un donjon occupé par des damnés et autres. Il faut donc cette fois, non pas entrer et explorer le donjon, mais quitter les cachots et tenter de s'enfuir (tout reste à faire ! ). Lancez base.exe pour en avoir un aperçu.
Mise à jour 02/2026
Un dialogue à été ajouté au geôlier, qui permet aux perso de faire des échanges avec lui. Il en profite pour leur indiquer une voie de sortie.
L'introduction
Ici vous apprendrez à faire une introduction complète. Vous y trouverez la création d'un nouveau profil, le chargement d'une partie, et aussi le panneaux des options. Il reste encore pas mal de travail pour finaliser tout cela.
Pour lancer la partie, il faut cliquer sur le bouton « continuer » de la page d'accueil.
La caméra bouge avec les touches flèches.



Le corps du jeu
Nos valeureux aventuriers viennent de quitter leur cellule.
En vous approchant et en cliquant sur le geôlier, vous pouvez entamer une conversation. Soyez sympa et il vous indiquera comment sortir du donjon.
En haut à droite, vous voyez l'image disque qui montre le plan, votre orientation (la flèche rouge). À sa droite, les quatre boutons : inventaire, quête, compétences et sortie du jeu.
Cliquez sur le sol pour faire avancer le barbare, les autres suivront.
Approchez vous des objets, cliquez dessus pour savoir s'il interagit.

Quand vous aurez trouvé un coffre, il s'ouvrira et vous montrera ce qu'il contient.

Divers personnages accepteront de faire des échanges avec vous. Voici le panneau correspondant :

L'inventaire, le détail des perso, le plan, les quêtes
Vous apprendrez à faire un inventaire très complet, "à la naheulbeuk".
Il n'y a pas encore de quêtes de disponibles, les bonus et la magie ne sont pas mis en place. Il vous reste de quoi vous occuper !



les combats
Premiers pas. Les persos se placent, ils peuvent combattre avec leur arme. Pas encore de bonus ou de magie.

Explication : déplacement d'un personnage
Notre personnage va se déplacer en suivant une courbe de Bézier, alignée sur des points clef, qui peuvent être, comme ici dans De Profondis, des plots. Ces plots sont des points 3D qui, lors du picking, sont considérés comme des dalles de 1 mètre sur 1 mètre.
Dans le schéma ci-dessous, chaque case représente un plot.
Notre personnage (en 1-1) doit se rendre sur la case marquée d'un X (6-1), mais un mur (partie noire) l’empêche d'y aller en direct.

Le code consiste à créer un tableau des plots et de leur affecter la valeur 0, sauf pour les cases que l'on veut écarter du test. Ici, le mur, que l'on peut mettre à -1, ainsi que la case de destination. Il faut y ajouter les autres personnages, et parfois des éléments d'impossibilité, tel qu'une rivière, que certaines unités peuvent traverser et d'autres non.
Puis on fait une boucle (for …) en incrémentant les cases adjacentes (test de distance) de valeur nulle, à la valeur référence (case de destination, puis 1,2, ...), ce qui donne le résulta de la partie de gauche.
On poursuit la boucle, en incrémentant à chaque cycle la valeur référence, ce qui va donner le résultat de droite. Quand on arrive à la case destination (le plot), on a trouvé un chemin possible. Si la boucle s'arrête sans cela, il n'y a pas de chemin possible.
Il est alors simple de remonter la ligne, commençant à la valeur référence (ici 5), pour trouver le plot proche d'une valeur juste inférieure (4).
Vous voyez que plusieurs chemins sont possibles : en haut du mur, la valeur « 3 » aurait pu trouver le « 2 » plus bas (ce qui n'aurait rien changé au chemin). La solution dépend du sens de recherche et de l'ordre des plots : le premier trouvé est le bon !
Notez que le quadrillage n'est pas nécessaire : dans le schéma ci-dessous, on considère les vertex comme source de plots. La routine cherche un vertex de proche en proche pour parvenir à trouver un chemin.

A* (A star)
Une autre méthode pour trouver un chemin est la méthode dite A*. Elle est plus complexe mais plus efficace pour les grandes cartes (encore qu'il soit possible de créer une carte maître contenant uniquement les grandes distances. Ex : les points entre les villes, puis, si le chemin est trouvé, une autre boucle pour le chemin de ville à ville).
Voici le principe de base :
// Structure d'un nœud
struct Node {
int x, y;
float g, h, f;
Node* parent;
Node(int x, int y) : x(x), y(y), g(0), h(0), f(0), parent(nullptr) {}
};
Graphe : Ton monde (donjon, carte) est représenté comme un graphe où chaque case ou nœud est connecté à ses voisins (ici, les valeurs '>n' ).
Coût :
g(n) : Coût du chemin depuis le point de départ jusqu’au nœud n.
h(n) : Heuristique (estimation du coût restant jusqu’au but, souvent la distance de Manhattan ou euclidienne).
f(n) = g(n) + h(n) : Coût total estimé pour passer par n.
Liste ouverte : Nœuds à explorer, triés par f(n).
Liste fermée : Nœuds déjà explorés.
// Heuristique (distance de Manhattan)(2d)
abs(a.x – b.x) + abs(a.y – b.y);
// Euclidienne (3d)
sqrt(dx² + dy² + dz²)
// Trouver le chemin avec A*
#include <vector>
#include <queue>
#include <unordered_map>
#include <cmath>
#include <algorithm>
std::vector<Node*> findPath(Node* start, Node* end, std::vector<std::vector<bool>>& grid) {
std::priority_queue<Node*, std::vector<Node*>, decltype([](Node* a, Node* b) { return a->f > b->f; })> openList;
std::unordered_map<Node*, bool> closedList;
std::vector<Node*> path;
openList.push(start);
while (!openList.empty()) {
Node* current = openList.top();
openList.pop();
if (current->x == end->x && current->y == end->y) {
// Reconstruire le chemin
while (current != nullptr) {
path.push_back(current);
current = current->parent;
}
std::reverse(path.begin(), path.end());
return path;
}
closedList[current] = true;
// Explorer les voisins (4 directions)
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
if (dx == 0 && dy == 0) continue; // Ignorer le nœud courant
if (abs(dx) + abs(dy) != 1) continue; // Seulement les voisins directs (pas diagonaux ici)
int nx = current->x + dx;
int ny = current->y + dy;
if (nx < 0 || ny < 0 || nx >= grid.size() || ny >= grid[0].size()) continue; // Hors limites
if (grid[nx][ny] == false) continue; // Obstacle
Node* neighbor = new Node(nx, ny);
if (closedList.find(neighbor) != closedList.end()) continue;
float tentative_g = current->g + 1; // Coût = 1 par case
if (tentative_g < neighbor->g || std::find(openList.begin(), openList.end(), neighbor) == openList.end()) {
neighbor->parent = current;
neighbor->g = tentative_g;
neighbor->h = heuristic(neighbor, end);
neighbor->f = neighbor->g + neighbor->h;
openList.push(neighbor);
}
}
}
}
return {}; // Pas de chemin trouvé
}
consultez https://www.redblobgames.com/ pour en savoir plus.
