Outils pour utilisateurs

Outils du site


dindomoteur_portail_du_moteur_de_metroidvania_orleanais

DindoMoteur : Portail du moteur de metroidvania orléanais

Cette page est la page principale du DindoMoteur, une proposition de création et documentation d'un moteur de Metroidvania à l'aide du moteur de jeu libre Godot.

Cette page est divisée en trois sections :

  • Une présentation des enjeux du projet.
  • La documentation des réglages généraux du moteur.
  • La centralisation des sous-articles liés au projet.

Enjeux du projet

Contexte

Cela fait maintenant 5 ans que j'utilise régulièrement le moteur de jeu vidéo libre Godot.

Celui-ci a accompagné ma découverte du développement informatique, et il prend de l'ampleur, rejoignant Blender dans le registre des outils libres qui agrègent assez de contributeurs et de d'utilisateurs pour devenir des logiciels équivalents aux alternatives propriétaires.

Au cours de mes recherches, j'ai fini par me rendre compte que la plupart des jeux vidéos ayant eu un impact culturel ne s'appuient en général pas seulement sur un moteur généraliste. Ces moteurs, comme Godot, Unity, Unreal Engine ou encore RPG Maker, sont des frameworks dédiés à la création de jeu vidéo mettant en place des outils graphiques et un langage de programmation qui facilite la mise-en-place de certains outils nécessaires au développement du logiciel.

Si ce sujet vous intéresse, vous devriez essayer de coder un petit jeu vidéo via la librairie python Pygame, puis en développer un second en utilisant Godot. Vous vous rendrez alors compte qu'un grand nombre des fonctionnalités à recoder soi-même en Pygame (par exemple les hitbox) sont maintenant pleinement intégrés dans Godot, qui, de surcroît, intègre un framework graphique.

Ces moteurs généralistes, donc, mettent en place l'ensemble des outils nécessaires à coder rapidement un jeu vidéo, mais leur aspect généraliste ne les rend pas capables de créer autre chose qu'un clone de jeu vidéo existant. Ils agrègent d'une certaine manière les algorithmes redondants du monde du jeu vidéo une fois qu'un nombre assez grand de jeux vidéos les proposent.

Par exemple, le jeu Wolfenstein 3D était le premier jeu vidéo en trois dimensions. À son époque, ce sont les développeurs qui ont codé le moteur de 3D qu'il utilise, puis, devant le succès et la prouesse de l'algorithme, de nombreux autres développeurs l'ont copié. Maintenant, un moteur comme Godot intègre nativement cet algorithme.

Or, ce qui fait le charme de nombreux jeux, c'est le caractère unique du gameplay qu'ils proposent. Ils ne peuvent se contenter d'utiliser le moteur généraliste : il est nécessaire de recréer un sous moteur spécialisé à l'aide du moteur généraliste.

À titre d'exemple, on peut penser à l'algorithme de téléportation du jeu Portal, le système d'énigme de Baba Is You, ou encore l'environnement modulable de Minecraft.

En réalité, ces exemples sont des exemples assez avancés qui mettent facilement en valeur la nécessité de respécialiser un jeu par rapport à un concept généraliste. L'utilisation des menus comme espaces de jeu et non plus comme simple espace de documentation et de paramétrages dans les RPG, par exemple dans les premiers Pokémon, est déjà une mise-en-place d'un gameplay particulier au sein d'une architecture classique.

Objectif

Mon objectif est de construire, de manière incrémentale, un moteur de jeu vidéo libre et documenté en français qui permette de cloner des jeux d'aventures de type metroidvania, comme Hollow Knight ou Dead Cells.

En prenant soin de documenter le projet, le but est de pouvoir disposer d'un moteur intégrant déjà les paramètres classiques, modifiables aisément (si possible via des interfaces graphiques). Une fois ce sous-moteur généraliste en place, le but est de pouvoir facilement re-spécialiser le gameplay en ajoutant du code dédié.

Une fois le moteur en place, il devient possible de le mettre à disposition de designer et d'artistes pour réaliser des versions concrètes de jeux vidéos à chaque point d'étape. Dans cette optique, chaque jeu vidéo associé au moteur correspond à une spécialisation du moteur.

Godot : kézako ?

Godot est donc un moteur de jeu vidéo libre.

Il intègre à la fois une interface graphique de programmation et un langage nommé gdscript, basé sur la syntaxe python. Il reste également possible de coder en C++ directement.

Le langage manipulé est orienté objet, et c'est ce qui fait la force de Godot : il intègre de nombreuses classes communément utilisées dans la programmation : sprites, animations, gestion de collision, parallaxes, entrées utilisateurs, etc… Grâce au système d'héritage, il est alors possible de créer et instancier ses propres objets, qui, dans la sémantique propre à Godot, sont nommés des scènes.

Dans la philosophie du moteur, une scène correspond en général à un objet (au sens humain) particulier. Un exemple typique est l'idée qu'une scène Village contiendra une scène Maison, qui contiendra une scène Table, etc.

Concernant la gestion des coordonnées, c'est le système vectoriel qui est utilisé, comme à l'accoutumée dans les jeux vidéos.

Les objets étant organisés selon un système d'arbre, les coordonnées d'une scène sont modifiées par l'ensemble des parents ce qui, dans l'exemple précédent, permet de bouger la Table en même temps que la Maison.

Pour plus d'infos sur Godot, RTFM.

Base de travail

Godot intègre une librairie de ressources (Asset Library) qui permet de voir comment utiliser le moteur de manière efficiente à l'aide de projets didactiques.

Dans notre cas, c'est le 2D Platformer Demo (KinematicBody) qui servira de base de travail pour le développement du sous-moteur.

Celui-ci intègre déjà les fonctionnalités de base d'un metroidvania très simple : personnage contrôlable, plateformes mouvantes, prototype d'AI, objets à ramasser…

Nous allons donc commencer par décortiquer cet exemple et en reconstruire les fonctionnalités principales dans un nouveau projet.

Mais avant ça…

Réglages généraux du moteur

Cette section discute des réglages généraux du moteur, qui sont de fait partagés par l'ensemble des sous-modules qui constituent du projet. Pour des raisons de clarté, ces sous-modules sont ensuite discutés dans des pages dédiées du wiki.

La version de Godot avec laquelle est développée le moteur est la 3.4.3 .

Organisation du répertoire

Dans sa documentation, Godot conseille d'organiser les scènes et ressources de telle manière à ce que celles-ci soient facilement transférables d'un dossier à l'autre.

Un des problèmes inhérents à l'organisation nodale décentralisée est le fait que certaines relation d’interdépendance concernent des objets qui ne sont pas localisés au même endroit : il devient donc difficile de copier-coller certaines scènes dans d'autres projets, et modifier localement une ressource peut avoir des conséquences sur un autre endroit du programme. Il est donc nécessaire de séparer le plus possible les objets les uns des autres et de permettre des mécanismes de sécurité afin de palier au manque d'une ressource externe.

Également, il devient difficile de se retrouver dans l'arborescence de fichier dans certains cas. la documentation de Godot propose donc de regrouper les fichiers par thématiques : un répertoire Personnage, un répertoire Lieu, un répertoire Dialogue, etc.

Dans le cas du Platformer, le répertoire principal est lui-même divisé en deux répertoires : src et assets. src contient les éléments propres à Godot : les scènes et les scripts. assets contient les données du jeu, comme les images, les sons, etc.

Comme évoqué précédemment, le dossier src est lui-même sous-divisé en répertoire thématiques : Actors, Level, Main, etc .

Nous garderons le même principe d'arborescence pour le moteur.

Mise en place des systèmes globaux

Avant même de créer la première scène, il s'agit de mettre en place certains mécanismes généraux pour le moteur de jeu. Cela concerne :

  • Le nom des entrées utilisateurs et les boutons qui y sont associés.
  • La résolution de base ( 1920 x 1080 ) et le système de mise à l'échelle utilisé pour les moniteurs plus petits.
  • La création d'un script global pour centraliser un certain nombre de paramètres de jeu (en faisant attention au risque d'interdépendance des nœuds ultérieurs).

Mise en place des entrées utilisateurs

Le moteur étant particulièrement chauvin, je commence par utiliser l'interface d'association des entrées utilisateurs ( Projet > Paramètres du projet… > Contrôles ) pour associer les touches du clavier aux mots-clefs suivants :

  • “gauche”
    • q
    • Left Stick Left
  • “droite”
    • d
    • Left Stick Right
  • “bas”
    • s
    • Left Stick Down
  • “haut”
    • z
    • Left Stick Up
  • “espace”
    • espace
    • Xbox A
  • “interagir”
    • E
    • Xbox X

Même s'il est conseillé d'associer des mot-clefs par action plutôt que par touche, les différentes utilisations de la touche espace possibles m'amènent à garder une balise généraliste pour ce cas particulier.

Paramétrage de la fenêtre

J'utilise maintenant Projet > Paramètre du projet… > Général > Window afin d'assigner la résolution de la fenêtre principal.

De manière générale, les jeux sont maintenant affichés en plein écran avec une résolution de 1920 par 1080 pixels. Dans notre cas nous voulons néanmoins rester sur un affichage fenêtré afin de faciliter le développement (même si cela peut permettre de ne pas oublier d'arrêter le programme une fois que les vérifications ont été faites).

Pour les questions de redimensionnement, je modifie, dans l'onglet Stretch, les paramètres suivants :

  • Mode : 2d
  • Aspect : keep

Il faudra peut-être revoir ultérieurement ces réglages.

Création de la scène Main

Afin de pouvoir tester les réglages graphiques, il est nécessaire d'avoir une scène principale dans Godot.

Je créé donc une scène Main.tscn dans le répertoire src/Main/ .

Afin de rester polyvalent, cette scène sera de type Node, qui est la classe maîtresse dans Godot.

Mise en place d'un nœud global

Je vais maintenant utiliser le système d'AutoLoad de Godot afin de mettre en place un nœud global.

Il est possible dans Godot d'instancier les scènes indépendamment les unes des autres, ce qui est parfois problématique au cas où celles-ci auraient besoin d'autres scène pour fonctionner correctement.

Ce nœud global sera chargé automatiquement à chaque lancement du programme ce qui lui permettra de centraliser l'ensemble des ressources nécessaires à chaque scène pour fonctionner. Dans certains cas, il pourra également simuler l'existence de certaines scènes pour résoudre des problèmes de dépendance.

Pour autant, il serait préférable que chaque scène soit dotée d'un mécanisme tel que :

  • si la scène est lancée seule, un mécanisme est mis-en-place pour bloquer les méthodes nécessitant l'accès à des nœuds externes et au nœud global.
  • si le nœud global est activé, l'absence de ressources externe peut être substituée par ce nœud global.
  • si la ressource externe est chargée également, celle-ci a la priorité sur les méthodes décrite ci-dessus.

Pour mettre en place le nœud global, je créé une nouvelle scène de type Node nommée Global.tscn et lui assigne un script global.gd, enregistrés dans le répertoire src/Main/ . Puis j'utilise le menu Projet > Paramètres du projet… > AutoLoad pour assigner le nom Global à la scène.

Le mot Global permet maintenant d'y accéder depuis n'importe quelle scène ( exemple : print( Global.attribut ) ).

Cette scène permettra notamment de mettre en place un Affichage Tête Haute (ATH) à l'aide d'un noeud CanvasLayer .

Pour éviter de rendre les nœuds qui utilisent cette scène dépendants de celle-ci, on pourra vérifier au préalable qu'elle est bien chargée avant d'y accéder, par exemple pour le Joueur qui accède à une variable de gravité stockée dans global.gd :

_gravite = 50

[...]

func _ready():
	if get_tree().root.has_node("Global"):
		_gravite = Global.gravite

Ci-dessus, vous pouvez voir qu'il y a déjà une valeur assignée par défaut à _gravite : il est important d'avoir des valeurs de sécurité pour ne pas casser le nœud si la dépendance est manquante. Cette méthodologie favorise le couplage faible.

Ajout du paramètre de gravité

Au lieu d'utiliser l'API de Godot, nous centralisons la gravité via le nœud Global :

...
var gravite = 50
...
func _ready():
	...
	ProjectSettings.set_setting( "physics/2d/default_gravity", gravite )
...

Script actuel du nœud global ( global.gd )

extends Node

var typeEntree = "clavier"
var zoneMorte = 0.3

var gravite = 50

func _ready():
	ProjectSettings.set_setting( "physics/2d/default_gravity", gravite )

Définition d'une unité de référence

Certaines classes de Godot ont besoin d'une unité spatiale de référence pour fonctionner, notamment les tilemaps. Cette unité aura un impact sur d'autres nœuds, par exemple le joueur dont la capacité de saut devra faire sens en fonction du tilemap.

De manière presque arbitraire, cette unité de référence sera la puissance de deux ( 32 / 64 / 128 / 256 / 512 ).

Veuillez noter que la possibilité de zoomer sur des éléments du jeu via l'utilisation du nœud Camera2D change la manière dont on devra penser le système de résolution par rapport à la taille effective des éléments graphiques en terme de collision. Le système de mipmaps se chargera de gérer ces mises-à-l'échelles.

Mise-en-place des collisions

Les masques de collision sont redéfinis pour faciliter le travail, dans les Paramètres du projet, via le menu Layer Names > 2d Physics :

  • 1 : joueur
  • 2 : ennemis
  • 3 : objets
  • 4 : terrain
  • 5 : terrain2

À ce moment de la rédaction de la documentation, il est fort probable que ces calques soient temporaires. Il y a par exemple de grandes chances qu'un calque dédié à la Camera2D soit nécessaire (notamment pour bloquer le déplacement de la caméra lors d'une chute du fatale du Joueur).

Sous-articles du moteur

Cette section présente les sous-articles du projet, qui documentent indépendamment les différents sous-modules du moteur :

Création du Joueur : explication de l'implémentation du personnage principal.

dindomoteur_portail_du_moteur_de_metroidvania_orleanais.txt · Dernière modification : 2022/03/27 13:42 de Simon Deplat