Outils pour utilisateurs

Outils du site


dindomoteur_mise_en_place_un_moteur_de_jeu_video

Ceci est une ancienne révision du document !


DindoMoteur : Mise en place d'un moteur de jeu vidéo

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.

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 devient 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ématique : un répertoire Personnage, un répertoire Lieu, un repértoire 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 ( Porjet > Paramètres du projet… > Contrôles ) pour associer les touches du clavier aux mots-clefs suivant :

  • q : “gauche”
  • d : “droite”
  • s : “bas”
  • z : “haut”
  • espace : “espace”

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 noeud global.

Il est possible dans Godot d'instancier les scènes indépendamment les une 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 noeud global, je créé un script global.gd situé dans le répertoire src/Main/ puis utilise le menu Projet > Paramètres du projet… > AutoLoad pour lui assigner le nom Global.

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

Création d'un joueur et d'une plate-forme

Je souhaite dans un premier temps avoir un simple joueur contrôlable ainsi qu'une plate-forme sur lequel celui-ci puisse se mouvoir.

En un premier temps, je créé deux répertoire dans src/ : Joueur/ et Niveau/ .

Je remarque que dans Platformer, il y a un répertoire Platform/ dédié aux plate-formes. Nous verrons en temps voulu si c'est pertinent, commençons simplement pour l'instant.

Pour l'instant, avant toute logique de déplacement et de collision, je souhaite simplement que ces deux objets soient affichés à l'écran. J'utilise donc Inkscape pour créer deux sprites qui leurs correspondent.

À ce stade, il est importer de choisir une unité de référence pour plus tard : nous serons amenés à utiliser des tilemaps pour générer plus facilement les niveaux et ceux-ci supposent que l'écran soit divisés en cas d'une certaine largeur. De manière arbitraire, cette unité de référence sera le multiple de deux ( 32 / 64 / 128 / 256 / 512 ).

Dans notre cas, la plate-forme fera 512 x 64 pixels.

Pour l'instant, je créerai un dossier assets/Dev/ qui contiendra les ressources temporaires liées au développement.

Voici les deux sprites utilisés pour l'instant :

Une fois les sprites réalisés, je crée donc les scènes associées :

  • pour le Joueur, un Kinematic Body 2D .
  • pour la plate-forme, un Static Body 2D.

De manière intuitive, chacun d'entre-eux aura un nœud Sprite et une Collision Shape 2D de la bonne taille.

Ensuite, je les ajoute au nœud Main en les instanciant. Ils ne bougent pas, mais ils apparaissent à l'écran !

dindomoteur_mise_en_place_un_moteur_de_jeu_video.1647187095.txt.gz · Dernière modification : 2022/03/13 15:58 de Simon Deplat