Des trains debout sur leurs rails, sans moteur 3D
Sur la carte de Ramedex, les trains sont dessinés debout sur leurs rails, en vue isométrique. Deux réglages de caméra MapLibre et trois propriétés de placement suffisent, sans une ligne de moteur 3D.
Vue du dessus, un train est un point. La voie est un trait, et le train qui roule dessus est un point sur ce trait, avec éventuellement une flèche pour la direction. Pour suivre trois circulations, ça suffit. Pour un jeu dont tout le sujet est de regarder des trains rouler, non. Deux rames sur des voies parallèles de sens opposés, ou une rame à quai qu'on confondrait avec un train de passage : à plat, il faut des artifices pour démêler tout ça.
La carte de Ramedex est donc inclinée, et les trains y sont dessinés debout sur leurs rails, en vue isométrique. On jurerait une petite scène 3D. Il n'y a pas une ligne de moteur 3D là-dedans.
Incliner la caméra, rien d'autre
MapLibre dessine un plan. Ce plan, la caméra peut le regarder de biais, avec deux réglages. Le pitch l'incline par rapport à la verticale : à 0° la vue est verticale comme sur une carte papier, vers 60° on regarde le sol de trois quarts. Le bearing fait pivoter la caméra autour du point central, et le nord se met à tourner. Ensemble, les deux donnent l'effet d'une maquette posée sur une table.
const map = new maplibregl.Map({
container: "carte",
style: "/style.json",
center: [7.73, 48.58],
pitch: 55,
bearing: -20,
}); C'est tout ce qu'il y a à écrire. Les tuiles, les routes, les libellés se projettent tout seuls en perspective. MapLibre sait aussi extruder des volumes avec fill-extrusion , pour des bâtiments par exemple, mais ici on n'en a pas besoin : l'effet de relief est entièrement fabriqué par la caméra.
Poser le train dans l'axe de la voie
Les trains, eux, ne sont pas dessinés par la carte. Ce sont des illustrations SVG générées en PHP sur le serveur, à partir d'une fiche par famille : une silhouette, une livrée, une composition. Un TGV et un Régiolis ne se dessinent pas pareil, et les TER prennent les couleurs de leur région. Chaque illustration est enregistrée comme image connue de la carte, puis une couche symbol la pose à la position calculée.
En version simplifiée, cette couche tient en quelques lignes :
map.addLayer({
id: "trains",
type: "symbol",
source: "trains", // GeoJSON : un point par train
layout: {
"icon-image": ["get", "famille"],
"icon-rotate": ["get", "cap"],
"icon-rotation-alignment": "map",
"icon-pitch-alignment": "viewport",
"icon-allow-overlap": true,
},
}); Trois propriétés font le travail. icon-rotate oriente l'icône en degrés, dans le sens horaire depuis le nord. Le cap vient presque gratuitement : la position du train est une fraction du tracé entre deux gares, et le segment du tracé à cet endroit donne la direction. icon-rotation-alignment: map attache cette rotation au repère de la carte, donc si le joueur fait pivoter la vue, l'icône tourne avec elle et le train reste dans l'axe de sa voie. icon-pitch-alignment: viewport est la clé du volume : quand la caméra s'incline, l'icône ne se couche pas avec le sol, elle reste face à l'écran. Le train apparaît de profil, debout sur ses rails.
Avec la valeur map sur cette dernière propriété, le même SVG serait aplati en perspective, comme un autocollant collé sur les voies.
Reste icon-allow-overlap . Par défaut, la détection de collision masque l'une des deux icônes quand elles se chevauchent. Deux trains qui se croisent doivent rester visibles tous les deux.
Ce que le volume rend lisible
À plat, donner un sens à un point demande une flèche, une couleur, une animation. Debout, la direction se lit sans rien ajouter. Deux rames nez à nez sur des voies parallèles ne se confondent plus. À une bifurcation, on voit quelle rame prend quelle branche, parce que l'illustration est dans l'axe de la voie qu'elle suit.
Ça sert aussi en gare. Entre son heure d'arrivée et son heure de départ, un train de Ramedex est posé sur la voie annoncée quand elle est connue, sinon devant le bâtiment voyageurs. Sur la carte inclinée, cette rame immobile s'aligne sur son quai au milieu de celles qui circulent, et la gare se lit d'un coup d'œil, avec ses voies parallèles et ses trains rangés dessus.
Les limites de l'illusion
Pas de profondeur, d'abord. Un train debout masque ce qui est derrière lui, et MapLibre dessine les symboles selon l'ordre de sa couche, sans notion de qui est devant qui dans la scène. Deux trains proches peuvent se recouvrir dans le mauvais ordre. symbol-sort-key permet d'imposer un tri, mais ça reste un tri, sans calcul de profondeur.
Les libellés ensuite. Plus la caméra est inclinée, plus le champ visible est grand, et plus les textes candidats se bousculent au loin. Il faut arbitrer qui a droit à un nom. Et quand le bearing tourne, le nord se perd : le contrôle de navigation de MapLibre affiche une boussole, autant la laisser visible.
Le coût, aussi. Une caméra inclinée voit plus de terrain, donc plus de tuiles à charger et plus de géométrie à dessiner. Sur un téléphone moyen, un pitch appuyé sur une zone dense se paie en fluidité. Ramedex se joue sur téléphone, donc ces réglages se dosent.
Dernier point, l'échelle. Les icônes sont dimensionnées en pixels d'écran, pas en mètres de terrain. C'est très lisible au bon niveau de zoom, et discutable dès qu'on s'éloigne. C'est une carte à lire, pas une maquette.
Si un jour il faut de l'occlusion exacte, un vrai moteur de scène s'imposera. Je ne m'en presse pas. Deux réglages de caméra et trois propriétés de placement suffisent à faire exister des trains sur des rails, et je préfère ça à une scène 3D de plus à maintenir.