AVANT DE COMMENCER

Comment pense Unreal Engine

Ce n’est pas un autre sujet du cours, mais une boussole qui surplombe tous les autres. Douze « Pourquoi » que le moteur résout avec la même manière de penser encore et encore — une fois que vous comprenez le fil de la pensée lui-même, chaque système du moteur suivant cesse d’être un nouvel ensemble de faits à mémoriser et devient une conséquence prévisible du même raisonnement.

Règle du projet : moins de magie après chaque sujet

L’objectif du cours n’est pas de « montrer l’API ». L’objectif est qu’après chaque chapitre, ce qui ressemblait auparavant à de la magie du moteur devienne une conséquence logique de l’architecture. Après le chapitre sur la réflexion, on comprend pourquoi UCLASS, UPROPERTY et UFUNCTION sont nécessaires. Après le chapitre sur le cycle de vie, on comprend pourquoi certaines choses ne peuvent pas être faites dans le constructeur. Après le Gameplay Framework, il devient évident pourquoi la logique est répartie entre GameMode, PlayerController, PlayerState et Character. Cette page ne remplace pas ces sujets, mais en est le dénominateur commun : les onze « pourquoi » ci-dessous ne sont en réalité que des variations de la même question posée à différentes parties du moteur.

L’objectif principal de l’apprentissage

Après avoir terminé le cours, le lecteur doit apprendre à prendre de manière indépendante des décisions d’ingénierie dans Unreal Engine — non pas mémoriser des faits sur des classes spécifiques, mais être capable de : concevoir indépendamment l’architecture d’un nouveau système de jeu ; lire le code source d’Unreal Engine sans craindre un fichier inconnu ; comprendre les raisons de l’existence de tout système du moteur, même si celui-ci n’est pas couvert par le cours ; trouver une solution dans une partie inconnue du moteur par analogie avec une manière de penser déjà comprise, plutôt que par essais et erreurs ; comprendre les compromis derrière une décision architecturale spécifique d’Epic — et être capable de les formuler avec des mots, pas seulement de citer la conclusion.

L’objectif principal du cours est de rendre la documentation d’Unreal Engine finalement inutile pour le lecteur. Non pas parce qu’il a tout mémorisé, mais parce qu’il a appris à reproduire le raisonnement avec lequel la documentation elle-même a été écrite.

Pourquoi presque tout dans le code de gameplay est-il un UObject ?
Parce qu’« être un UObject » n’est pas de l’héritage pour l’héritage, mais un achat unique de trois choses à la fois : la visibilité pour le Garbage Collector, la visibilité pour la Réflexion (et donc pour Blueprint/l’éditeur), et un mécanisme de sérialisation unifié. Réécrire l’un de ces trois systèmes à partir de zéro pour un type « léger » sans UObject signifie résoudre un problème déjà résolu une deuxième fois.
Analyse approfondie → UObject : architecture objet
Pourquoi presque tout est-il un pointeur, pas une valeur ?
Un UObject est une unité d’identité et de propriété (via Outer), que le Garbage Collector suit par l’adresse de l’objet spécifique en mémoire, pas par sa valeur. Copier un UObject « par valeur » signifierait soit rompre cette identité (deux objets différents avec les mêmes données ne sont pas la même chose pour le GC et la Réflexion), soit secrètement copier encore par référence — le moteur rend donc cette nature de référence explicite via un pointeur, plutôt que de la cacher.
Analyse approfondie → Pointeurs intelligents et GC
Pourquoi y a-t-il un Garbage Collector ?
Le graphe des objets de jeu en temps réel n’est pas un arbre avec un seul propriétaire, mais un réseau de références croisées (un acteur référence un composant, un composant référence un autre acteur, qui référence en retour), où suivre manuellement « qui a été le dernier à cesser de référencer » est pratiquement impossible sans fuites ou libération prématurée. Le GC résout cela en suivant l’accessibilité depuis la racine, plutôt que par comptage manuel des références.
Analyse approfondie → Garbage Collector
Pourquoi y a-t-il la Réflexion ?
L’éditeur, Blueprint et le système de sérialisation ont besoin de connaître les champs et fonctions de votre classe C++ au moment de l’exécution — ce que C++ ne fournit fondamentalement pas (le langage n’a pas d’information d’exécution sur les champs arbitraires des classes). L’Unreal Header Tool résout cela en générant cette description à l’avance, à partir de macros comme UPROPERTY, plutôt que de forcer le moteur à deviner à partir du binaire.
Analyse approfondie → Réflexion
Pourquoi y a-t-il un Component ?
Parce que les capacités des entités de jeu (« a de la santé », « a un inventaire ») sont une relation « a-un », pas « est-un », et l’héritage, qui modélise correctement « est-un », lorsqu’il est utilisé pour exprimer « a-un », conduit à une explosion combinatoire de classes pour chaque nouvelle capacité indépendante.
Analyse approfondie → UActorComponent
Pourquoi la Composition plutôt que l’héritage ?
Parce que la composition est la seule des trois approches tentées (héritage, interfaces, templates) qui fournit simultanément l’activation/désactivation indépendante d’une capacité, le stockage physique de l’état et une visibilité complète en Réflexion/Blueprint — les interfaces ne fournissent qu’un contrat sans état, les templates sont invisibles pour UHT.
Analyse approfondie → UActorComponent
Pourquoi y a-t-il des Modules ?
Une base de code de la taille du moteur ne peut pas être compilée en un temps raisonnable et n’a pas de frontières de dépendance gérables sous forme d’un seul binaire monolithique. Un module est une unité de compilation séparée avec son propre fichier de build et une liste explicite de dépendances aux autres modules, ce qui permet de ne reconstruire que ce qui a réellement changé et empêche physiquement une partie du moteur d’importer ce dont elle n’a pas besoin (par exemple, le code de l’éditeur dans un jeu packagé).
Article à venir
Pourquoi y a-t-il des Plugins ?
Un module est une unité de compilation, mais pas une unité de distribution. Un plugin empaquette un ou plusieurs modules avec le contenu et la configuration en une fonctionnalité autonome, activable/désactivable pour un projet spécifique — de sorte qu’une extension du moteur ne nécessite pas de forker ou de patcher le code source du moteur, qui divergerait autrement à chaque mise à jour suivante du moteur.
Article à venir
Pourquoi y a-t-il un Subsystem ?
Un singleton/variable globale fait maison ne s’adapte pas bien au fait que plusieurs « mondes » peuvent exister simultanément dans Unreal (éditeur + Play-In-Editor en même temps, plusieurs clients dans un seul processus pendant les tests) — un état global n’a pas d’endroit auquel « appartenir » sans ambiguïté. Un Subsystem est un UObject avec une durée de vie automatique gérée par le moteur et liée à une frontière explicite spécifique (Engine/GameInstance/World/LocalPlayer), de sorte qu’il ne survive pas à sa frontière et ne se mélange pas entre les mondes parallèles.
Article à venir
Pourquoi y a-t-il des Gameplay Tags ?
Un ensemble croissant et arborescent d’états et de catégories de gameplay ne s’intègre pas bien dans un enum C++ — une nouvelle valeur nécessite une recompilation, et les relations hiérarchiques (« Status.Debuff.Stun » est un sous-type de « Status.Debuff ») ne peuvent pas du tout être exprimées par un enum. FGameplayTag est une chaîne hiérarchique avec une comparaison rapide, déclarée comme donnée plutôt que comme code recompilable.
Analyse approfondie → FGameplayTag
Pourquoi y a-t-il des Data Assets ?
Les caractéristiques de contenu codées en dur dans des constantes C++ ou Blueprint (dégâts d’une arme, poids d’un objet) ne passent pas à l’échelle avec une liste de contenu croissante et nécessitent l’intervention d’un programmeur pour chaque changement d’équilibrage. UDataAsset est un UObject ordinaire, mais spécialement conçu pour des données modifiables par un designer sans recompilation et sans passer par un programmeur.
Analyse approfondie → UDataAsset
Pourquoi y a-t-il des Primary Assets ?
TSoftObjectPtr résout le problème « ne pas tout charger d’un coup », mais il ne résout pas « lesquels des 3000 DataAssets sont réellement nécessaires actuellement » — gérer cela par des chaînes implicites de références douces ne passe pas à l’échelle. Un Primary Asset est un enregistrement explicite d’un UObject spécifique auprès de l’Asset Manager sous un identifiant stable (type + nom), indépendant du chemin de fichier, fournissant un catalogue unique et géré de manière centralisée pour le chargement de contenu.
Analyse approfondie → UAssetManager
💡 Réflexions Senior

Lorsque je rencontre un système moteur inconnu pour la première fois, je ne vais pas lire toute son API. Je me demande d’abord : lequel de ces douze problèmes résout-il — et pour qui joue-t-il le rôle de UObject, de Réflexion, de GC ou de Component dans cette tâche spécifique ? Dans neuf cas sur dix, la réponse est trouvée plus rapidement que la documentation du système lui-même.