Bref, c'est une pratique courante ayant plusieurs raisons telles que :
- l'amusement de programmer un tel moteur ;
- l'expérience gagnée en le faisant et donc l'augmentation des opportunités de recrutement ;
- la connaissance parfaite du moteur pour la création d'un jeu et la création d'un moteur dédiée à un jeu précis.
Toutefois, la pratique peut aussi rapidement devenir démoralisante :
- c'est un projet colossal ;
- il existe des solutions reconnues (Unity, Unreal Engine, Godot, Game Maker...) ;
- la création d'un jeu est souvent mise au second plan et n'aboutit que très rarement.
Toutefois, le sujet d'aujourd'hui n'est pas les pour ou les contres d'une telle pratique, mais comment réaliser un moteur de jeux vidéo avec lequel vous aurez des facilités pour créer votre jeu. Sachant que nous sommes en 2017, les outils ont fortement évolué et permettent un meilleur rendement.
Randy Gaul décrit sur son blog une méthode où votre code devient l'éditeur de votre jeu. Ainsi, le C++ peut être perçu tel un langage de script. Pour que cela fonctionne, vous devez suivre quelques bonnes pratiques :
- mettre l'implémentation du jeu dans une DLL et ainsi permettre le rechargement à la volée de la logique ;
- garder des temps de compilation très bas ;
- réimplémenter le polymorphisme (le rechargement de la DLL provoquera une destruction de la vtable). Pour cela, vous pouvez utiliser un identifiant d'entité, qui sera utilisé comme indice pour votre vtable statique :
Code cpp : Sélectionner tout 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42enum EntityID { eDebugPlayer, // all other types here EntityID_Count }; struct Entity { EntityID id; Entity* next; Entity* prev; }; struct VTABLE { void (*Update)( Entity*, f32 ); void (*Draw)( Entity* ); }; #define VTABLE_ENTRY( T ) \ { \ (void (*)( Entity*, f32 ))Update##T, \ (void (*)( Entity* ))Draw##T \ } VTABLE g_VTABLE[ EntityID_Count ] = { VTABLE_ENTRY( DebugPlayer ), }; STATIC_ASSERT( ArraySize( g_VTABLE ) == EntityID_Count ); void Update( Entity* entity, f32 dt ) { g_VTABLE[ entity->id ].Update( entity, dt ); } void Draw( Entity* entity ) { g_VTABLE[ entity->id ].Draw( entity ); }
- charger les ressources à la volée (et non juste le code). Pour cela, vous pouvez mettre en place un thread scannant les ressources afin de détecter les modifications ;
- le système Entity Component (ECS) est à bannir. Du point de vue de Randy, vous devez vous concentrer sur l'écriture du jeu et faire du refactoring que si nécessaire.
L'objectif global est de réduire les temps d'itération. Vous n'avez qu'à cliquer sur un bouton et le jeu est à jour. Même si vos valeurs sont dans le code, cela ne pose pas de souci tant que votre jeu fonctionne. Si ces valeurs doivent être disponibles pour un game designer, vous allez sûrement embarquer une bibliothèque pour lire un fichier XML (ou autre) et, chaque fois qu'il voudra tester le jeu, il devra recharger le fichier XML (souvent, cela est équivalent à recharger le jeu). De même, le fichier XML ne suffit pas toujours, car le designer voudra possiblement des changements dans les mécaniques de jeu, nécessitant une recompilation du moteur et un relancement du jeu. Cela prend du temps et encore plus si le designer (ou vous-même) devez peaufiner les valeurs. En faisant en sorte que le code devienne éditeur du jeu, ces temps sont réduits. Par contre, le designer devra avoir petite connaissance du C. Rien d'insurmontable pour des gains énormes de productivité.
Randy propose une implémentation du rechargement à la volée de la DLL sur son GitHub.
Voir aussi
CppCon 2016 - Un moteur de jeux avec la bibliothèque standard et C++11
Compilation C++ à l'exécution
Votre opinion
Vous êtes-vous lancé dans la création d'un moteur de jeux vidéo ? Quel a été votre résultat ? Comment avez-vous perçu cette expérience ? Que changeriez-vous à votre démarche ?
Source
Le blog de Randy Gaul