đ§ Custom Engine â Architecture, Motifs & Trade-offs
Guide complet IDEO-Lab sur l'architecture des moteurs de jeu propriétaires.
Concept & "Build vs Buy"
Pourquoi ? (Avantages/Inconvénients).
Custom Engine Build vs BuyLe CĆur : Game Loop
Boucle de jeu (while(true)), Update/Render, DeltaTime.
Architecture (Modulaire)
Monolithe vs Modulaire (Interfaces, DLLs).
Architecture ModulaireArchi 1 : POO (Héritage)
ModĂšle "Actor" (Unreal). Actor -> Pawn -> Character.
Archi 2 : ECS (Composition)
Entity-Component-System (Unity, DOTS). Composition.
ECS CompositionArchi 3 : DOD (Data-Oriented)
Performance (Cache CPU), SoA vs AoS. (ECS pur).
DOD Cache CPUMoteur de Rendu
Pipeline (Rasterisation), APIs (Vulkan, DX12, Metal).
Renderer Vulkan DX12Pipeline : Deferred vs Forward
Forward+ (Rapide, MSAA) vs Deferred (LumiĂšres +++).
Deferred Forward+Scene Graph & Culling
Graph de ScĂšne (Octree, BVH), Frustum/Occlusion Culling.
Scene Graph CullingMoteur Physique (Détection)
Broad-Phase (SAP) vs Narrow-Phase (GJK, SAT).
Physique CollisionMoteur Physique (Résolution)
Simulation (Rigid Body), Solver (Impulse, PGS).
Rigidbody SolverPhysique : Intégration
Fait-maison vs. Intégration (PhysX, Jolt, Havok).
PhysX Jolt HavokSystĂšme de Scripting
C++ (Natif) vs. C# (Binding) vs. Lua (Embarqué).
C++ C# LuaScripting : Binding (Glue)
La "colle" (C++ <-> Script). (ex: Sol2 (Lua), SWIG).
Binding Sol2Gestion Mémoire
Allocateurs (Pool, Stack), Garbage Collector (Script).
MĂ©moire AllocateurL'Ăditeur (Outil)
L'outil central (WYSIWYG). (ex: ImGui, Qt, C#).
Ăditeur WYSIWYGAsset Pipeline
Importation, Compilation (ex: Textures -> DXT), Database.
Assets PipelineSystÚme de Réflexion (RTTI)
Le "cĆur" de l'Ăditeur (SĂ©rialisation, Inspecteur).
Reflection RTTIExemple : RAGE Engine
Rockstar (GTA, RDR). (Open World, Streaming).
RAGE RockstarExemple : Frostbite Engine
DICE/EA (Battlefield). (Destruction, Audio).
Frostbite DICEExemple : Decima Engine
Guerilla (Horizon). (Graphismes, Outils).
Decima GuerillaUn Moteur Personnalisé (Custom Engine) est un moteur de jeu (ou de rendu) développé en interne (in-house) par un studio, par opposition à l'achat (ou la location) d'une licence pour un moteur "sur étagÚre" (Off-the-shelf) comme Unity ou Unreal.
Pourquoi "Construire" (Build) ?
- Optimisation Ciblée (Performance) : (La raison n°1) Le moteur est spécialisé pour un seul type de jeu. (ex: Le moteur RAGE est optimisé pour streamer (charger) un monde ouvert (Open World) à haute vitesse (voiture), ce qu'Unreal/Unity géraient mal à l'époque).
- ContrÎle Total (Code) : AccÚs 100% au code source (pas de "boßte noire"). Permet de déboguer/optimiser le moteur au plus bas niveau.
- Pas de Royalties : Pas de frais (5% pour Unreal) ou de licence (Unity Pro) Ă payer Ă un tiers. (Ăconomies Ă trĂšs grande Ă©chelle, ex: EA/Frostbite).
- Propriété Intellectuelle (IP) : Le moteur *est* un actif de l'entreprise.
Pourquoi "Acheter" (Buy - ex: Unity/Unreal) ?
- Coût (Initial) : Construire un moteur coûte des dizaines (voire centaines) de millions et prend des années de R&D (Nécessite une équipe d'ingénieurs moteur "core").
- Temps (Time-to-Market) : Un moteur "sur Ă©tagĂšre" est prĂȘt Ă l'emploi (Jour 1).
- ĂcosystĂšme & Outils : Unity/Unreal ont 20 ans d'avance sur les outils (Ăditeur), la documentation, la communautĂ©, et l'Asset Store.
- Multiplateforme : Gérer le "build" pour 10 plateformes (PS5, Xbox, iOS...) est un travail à plein temps que Unity/Unreal font pour vous.
Verdict : Seuls les studios "AAA" (Rockstar, EA, Nintendo, Guerilla) peuvent justifier (et maintenir) un moteur propriétaire aujourd'hui.
Le "cĆur" (le main()) de tout moteur de jeu est une boucle while(true) infinie, appelĂ©e la Game Loop.
Boucle Simple (ProblÚme : Frame-dépendant)
while (game_is_running)
{
// (L'ordre est important)
// 1. Lire les Entrées (Input)
process_input();
// 2. Mettre Ă jour la Logique
update_game_logic();
// 3. Rendu (Dessiner)
render_graphics();
}
// ProblĂšme : 'update_game_logic()' (ex: Mouvement)
// s'exécute à la vitesse du Rendu.
// PC Rapide (500 FPS) -> Jeu 8x plus rapide
// PC Lent (60 FPS) -> Jeu normalBoucle avec DeltaTime (Frame-indépendant)
La solution est de mesurer le temps (DeltaTime) écoulé depuis la derniÚre frame, et de l'utiliser pour normaliser la logique.
double last_time = get_time();
while (game_is_running)
{
// 1. Calculer le DeltaTime
double current_time = get_time();
double delta_time = current_time - last_time; // (ex: 0.016s pour 60FPS)
last_time = current_time;
process_input();
// 2. Passer le DeltaTime Ă la logique
update_game_logic(delta_time);
render_graphics();
}
// (Dans update_game_logic)
// vitesse_constante = 10 (mĂštres/sec)
position += vitesse_constante * delta_time;
// (PC Rapide) : 10 * 0.002s = 0.02m (x 500 = 10m/s)
// (PC Lent) : 10 * 0.016s = 0.16m (x 60 = ~10m/s)Boucle avec Physique Fixe (Fixed Timestep)
La physique (ex: collisions) ne peut pas dépendre d'un delta_time variable (sinon elle est instable). Elle doit tourner à intervalle fixe (ex: 60x/sec).
// (Logique 'FixedUpdate' (Unity)
// ou '_physics_process' (Godot))
const double PHYSICS_TICK = 1.0 / 60.0; // 60 FPS
double accumulator = 0.0;
while (game_is_running)
{
double delta_time = ...;
accumulator += delta_time;
process_input();
// 1. Boucle Physique (Fixed)
// (S'exécute 0, 1, or N fois par frame)
while (accumulator >= PHYSICS_TICK)
{
update_physics_logic(PHYSICS_TICK);
accumulator -= PHYSICS_TICK;
}
// 2. Boucle Logique (Variable)
update_game_logic(delta_time);
// 3. Rendu
render_graphics();
}Un moteur est un ensemble de sous-systĂšmes (Managers) qui doivent communiquer.
ModĂšle Monolithique (Legacy)
Tous les systÚmes sont compilés en 1 seul gros exécutable (.exe). (ex: Moteur C++ classique).
Les "Managers" (Rendu, Physique, Audio) sont des Singletons (instances globales) ou des contextes (gEnv) que tout le monde peut appeler.
// (AccĂšs facile) g_Renderer->Draw(...); g_Audio->PlaySound(...);
- Avantage : Simple (au début), performance (pas d'overhead d'API).
- Inconvénient : "Code spaghetti" (fort couplage), temps de compilation trÚs longs.
ModĂšle Modulaire (Moderne - O3DE)
Le "CĆur" (Core) du moteur est minimal. Chaque sous-systĂšme (Rendu, Physique, Audio, Scripting...) est une librairie dynamique (.dll, .so) (ou "Gem" (O3DE)).
Les modules communiquent via des Interfaces (APIs) C++ ou des Bus d'ĂvĂ©nements (EBus) (5.2).
- Avantage : Modulaire (on peut "désactiver" la Physique), compilation rapide (seul le module changé est recompilé).
- Inconvénient : Plus complexe (gestion des DLLs/Plugins).
Philosophie : L'Héritage (Inheritance) (Programmation Orientée Objet - POO). C'est le modÚle "classique", utilisé par Unreal Engine.
Le ModĂšle "Actor"
L'objet de base est l'Actor (AActor). Un Actor est "lourd" (il sait gérer le réseau, le "tick", la physique...).
Les fonctionnalités sont créées en héritant (extends) et en spécialisant.
Exemple (Hiérarchie de Classes)
class Actor (Objet de base)
â
ââ class Pawn (Peut ĂȘtre "possĂ©dĂ©" par un Controller)
â
ââ class Character (Un Pawn "humanoĂŻde")
â (PossĂšde un Mesh, un Mouvement...)
â
ââ class MyPlayerCharacter (Mon C++ pour le Joueur)
â
ââ class BP_Player (Blueprint qui hĂ©rite de C++)
- Avantage : TrĂšs puissant, logique "verticale" claire.
- Inconvénient : Rigide. (Que faire si un objet doit hériter de
VoitureET deArme? (Héritage multiple impossible)).
Philosophie : La Composition (Composition over Inheritance). C'est le modÚle utilisé par Unity et (plus récemment) CryEngine/O3DE.
Le ModĂšle "Composant"
L'objet de base est l'Entité (GameObject / Entity). C'est une "coquille" vide, un simple ID avec un Transform.
L'Entité "devient" un joueur en lui attachant (composant) des briques de fonctionnalités (Components).
Exemple (Composition)
EntitĂ©: "Joueur" â Component: Transform â Component: Mesh (Visuel) â Component: Rigidbody (Physique) â Component: Health (Script) â Component: PlayerInput (Script) EntitĂ©: "Tourelle" â Component: Transform â Component: Mesh â Component: Health (Script) (RĂ©utilisation !) â Component: AI_Turret (Script)
- Avantage : ExtrĂȘmement flexible et rĂ©utilisable. (Permet un "HĂ©ritage multiple" via composition).
- Inconvénient : Peut devenir complexe (communication inter-composants : "GetComponent").
Le DOD (Data-Oriented Design) est une philosophie de performance (utilisée par DOTS/ECS d'Unity) qui se concentre sur l'efficacité du Cache CPU (L1/L2).
Le ProblĂšme (POO/ECS Classique)
Un ECS classique (Composition) stocke les données de maniÚre "Array of Structures" (AoS) :
[Joueur 1 (Pos, Vit, PV)] [Joueur 2 (Pos, Vit, PV)] ...
Si le "MouvementSystem" (logique) veut juste mettre à jour 1000 Positions, il doit "sauter" en mémoire (RAM) par-dessus les (inutiles) "Vit" et "PV", causant des "Cache Misses" (trÚs lents).
La Solution (DOD - SoA)
Le DOD (ou ECS "pur") stocke les données en "Structure of Arrays" (SoA).
(Array 'Positions') : [Pos 1] [Pos 2] [Pos 3] ... (Array 'Vitesses') : [Vit 1] [Vit 2] [Vit 3] ... (Array 'PVs') : [PV 1] [PV 2] [PV 3] ...
Flux (MouvementSystem) :
- Le "MouvementSystem" (Logique) demande les "Arrays" (
Positions,Vitesses). - Il boucle (
FOR) sur ces deux arrays (contigus en mĂ©moire). - RĂ©sultat : ZĂ©ro "Cache Miss". C'est extrĂȘmement rapide (idĂ©al pour le C# Job System (Unity) ou le parallĂ©lisme).
Studio : Rockstar Games.
Jeux : GTA IV, GTA V, Red Dead Redemption (1 & 2), Max Payne 3.
Objectif (Pourquoi "Build" ?)
RAGE a été développé (basé sur l'ancien moteur "Angel") spécifiquement pour une tùche : gérer des Mondes Ouverts (Open World) massifs, denses, et dynamiques, avec un streaming (chargement) "seamless" (sans couture) à haute vitesse (véhicules).
SystÚmes Clés
- Streaming (Monde Ouvert) : Le point fort. GÚre le "streaming" (chargement/déchargement) de la géométrie, des textures, et de la logique (IA) dans un rayon (ex: 2km) autour du joueur (véhicule).
- Moteur Physique : (A utilisé "Euphoria" (acheté) pour l'IA procédurale (Ragdolls dynamiques) et "Bullet" (open source) pour la physique générale).
- Scripting : Utilise massivement C++ (cĆur) et un langage de script propriĂ©taire (C-like).
Studio : DICE (pour Electronic Arts - EA).
Jeux : Série Battlefield, Mirror's Edge, Dragon Age: Inquisition, FIFA (depuis 2017).
Objectif (Pourquoi "Build" ?)
Conçu pour les FPS (First-Person Shooters) à grande échelle.
SystÚmes Clés
- Destruction : (Le point fort) CélÚbre pour son systÚme de "Destruction" (Micro-destruction (murs) et Macro-destruction ("Levolution")).
- Audio (HDR Audio) : (Point fort) Un moteur audio trÚs avancé pour gérer le "chaos" (centaines d'effets sonores) d'un champ de bataille.
- Rendu : (PBR) TrĂšs performant pour les grandes cartes (64-128 joueurs).
Le "ProblÚme" Frostbite : EA a (dans les années 2010) forcé tous ses studios (BioWare (RPG), Visceral (Horreur)) à abandonner leurs moteurs (ex: Eclipse) et à utiliser Frostbite (un moteur de FPS). Cela a causé des difficultés de développement (ex: Dragon Age, Mass Effect: Andromeda) car l'outil n'était pas conçu pour les RPGs (sauvegardes, inventaires...).
Studio : Guerilla Games (Sony Interactive Entertainment).
Jeux : Horizon Zero Dawn, Horizon Forbidden West, Death Stranding (Kojima Productions).
Objectif (Pourquoi "Build" ?)
Un moteur "AAA" moderne conçu pour la PlayStation (PS4/PS5), optimisé pour les mondes ouverts (Open World) avec une trÚs haute fidélité graphique (Rendu/Assets).
SystÚmes Clés
- Rendu : (Point fort) Rendu PBR (physique) de trÚs haute qualité (notamment les matériaux "durs" et la végétation).
- Outils (Monde) : Outils de génération procédurale pour les mondes ouverts.
- Partage : (Unique) Moteur interne de Sony, mais "partagé" (donné) à d'autres studios "amis" (ex: Kojima Productions pour Death Stranding).
