Comparaison de la Conception Orientée Données (DOD) et de la Programmation Orientée Objet (OOP) : Analyse architecturale et des performances sur Unity DOTS et Unreal Engine 5
Unity

Comparaison de la Conception Orientée Données (DOD) et de la Programmation Orientée Objet (OOP) : Analyse architecturale et des performances sur Unity DOTS et Unreal Engine 5

AuteurGameDev Base
Date17 octobre 2026
25 min de lecture

DOTS et Unreal Mass : Ciblage de l'efficacité du cache CPU et du traitement parallèle pour des jeux AAA fluides

Ce rapport compare en profondeur les approches de conception orientée données (DOD) et de programmation orientée objet (OOP) traditionnelles dans les moteurs de jeu Unity et Unreal Engine 5, du point de vue des développeurs de jeux. L'étude vise à analyser les résultats de performance fondamentaux, en particulier en ce qui concerne l'optimisation de la disposition de la mémoire, l'utilisation du cache CPU et la prise en charge du multithreading. Le rapport entre dans les détails techniques à l'aide d'exemples de code concrets et d'étapes d'intégration issus des versions actuelles des moteurs. De plus, inspirés par des titres tels que Kena: Bridge of Spirits, les impacts stratégiques de ces choix architecturaux sont évalués pour les jeux à gros budget (AAA) à la troisième personne, en monde ouvert et destinés aux plateformes mobiles. Chaque affirmation est prouvée en se basant sur les sources fournies.

Différences paradigmatiques fondamentales : Logique de stockage des données et résultats de performance

La différence fondamentale entre la conception orientée données (DOD) et la programmation orientée objet (OOP) repose sur la manière dont les données sont regroupées en unités logiques et stockées en mémoire. L'OOP se concentre sur les objets qui regroupent les données et les fonctionnalités, tandis que la DOD vise à optimiser les schémas d'accès à la mémoire en maintenant des données du même type ensemble. Cette distinction dans le paradigme fondamental est d'une importance critique en termes de capacité à exploiter pleinement le potentiel de performance des architectures informatiques modernes.

La programmation orientée objet (OOP) repose généralement sur la logique « stocker les données à l'intérieur de l'objet ». Si l'on considère un objet personnage, cet objet possédera des attributs contenant différents types de données tels que Position, Health (Santé) et Velocity (Vitesse). Chaque objet personnage occupe son propre espace d'adresse unique en mémoire. S'il y a des centaines ou des milliers de personnages dans un jeu complexe, ces objets sont dispersés de manière aléatoire dans la mémoire. Lorsqu'une opération telle que le déplacement de tous les personnages est exécutée dans une boucle de mise à jour (Update), le CPU doit accéder séparément aux données de position de chaque personnage. Dans ce cas, il est peu probable que les informations de position et de santé de l'objet CharacterA se trouvent dans le sillage du même bloc de mémoire ; elles se trouvent à des adresses différentes car il s'agit d'objets distincts. De même, les informations de position de CharacterB sont probablement très éloignées des données de CharacterA. Cela amène le CPU à accéder à des blocs de mémoire différents et non contigus à chaque fois qu'il doit extraire des données de la mémoire. Cette situation crée un goulet d'étranglement de performance qui contredit la structure des CPU modernes . Les ralentissements soudains et les chutes de FPS observés dans des jeux comme Kena: Bridge of Spirits sont des indicateurs typiques de problèmes de performance découlant de tels schémas d'accès à la mémoire basés sur l'OOP et peu favorables au cache .

La conception orientée données (DOD), en revanche, inverse cette logique. La DOD ne se concentre pas sur les objets, mais sur les données qui composent leur contenu, et vise à stocker les données du même type de manière aussi dense et séquentielle que possible en mémoire. L'application la plus courante et la plus formelle de cette approche est l'architecture Entity Component System (ECS). L'ECS définit les entités complexes comme de simples « entités », ajoute leurs comportements et propriétés à l'aide de « composants » (components) indépendants, et les traite par l'intermédiaire de « systèmes » (systems) qui utilisent ces composants. Le fondement de cette architecture repose sur des structures de données telles que Structure of Arrays (SoA) ou similaires . Le SoA, contrairement à la logique AoS (Array of Structures) de l'OOP, désigne la création d'un tableau distinct pour chaque type de données. Par exemple, les informations de position de tous les personnages sont stockées séquentiellement dans un seul tableau (positions[]), les informations de santé de tous les personnages dans un autre tableau (health[]), et les informations de vitesse de tous les personnages dans un troisième tableau (velocity[]). Grâce à cet agencement, la tâche de mettre à jour les positions de tous les personnages en même temps nécessite de charger un grand bloc de données dans le CPU une seule fois seulement. Le schéma d'accès séquentiel à la mémoire permet au CPU d'utiliser efficacement les caches accélérés tels que L1 et L2, car lorsque les premières données sont chargées, les données adjacentes sont également mises en cache, et ces données peuvent être réutilisées encore et encore . La documentation ECS d'Unity souligne que les données sont stockées dans des blocs de mémoire séquentiels de taille fixe appelés Chunks, et à quel point cela est compatible avec les lignes de cache du CPU . Ce changement simple mais efficace améliore considérablement les performances des algorithmes travaillant sur de grands ensembles de données.

CaractéristiqueProgrammation orientée objet (OOP)Conception orientée données (DOD/ECS)Logique de regroupement des données« Stocker les données à l'intérieur de l'objet » (AoS - Struct of Array)« Stocker les mêmes données ensemble » (SoA - Array of Struct)Disposition de la mémoireLes données se trouvent dans une mémoire aléatoire et disperséeLes données sont stockées de manière séquentielle et dense en mémoireSchéma d'accès aux donnéesLes accès dispersés et croisés sont courantsLes accès séquentiels et locaux prédominentEfficacité du cacheGénéralement faible (nombre élevé de défauts de cache / Cache Miss)Généralement élevée (nombre élevé de succès de cache / Cache Hit)Composition architecturaleL'héritage (inheritance) et l'abstraction sont au centreStructure flexible composée de composantsApplication typiqueUnity GameObjects, Unreal ActorsUnity DOTS/Entities, Unreal Mass

Ces paradigmes fondamentaux ont suivi des voies de développement différentes pour les moteurs de jeu modernes tels que Unity et Unreal Engine 5. Tout en conservant l'architecture GameObject basée sur l'OOP, Unity a introduit une pile entièrement nouvelle orientée données appelée DOTS (Data-Oriented Technology Stack) . Cela offre aux développeurs la flexibilité d'utiliser ces deux approches conjointement dans différentes parties du projet. Par exemple, l'interface utilisateur et certaines mécaniques de jeu spécifiques peuvent être gérées avec l'OOP, tandis que la simulation physique ou le contrôle d'un grand nombre d'unités d'IA peut être réalisé avec la DOD . Néanmoins, l'architecture fondamentale d'Unreal Engine 5 est profondément axée sur l'OOP avec l'héritage d'UObject . Cependant, Unreal a également développé des solutions spécifiques pour les domaines nécessitant de hautes performances. La plus importante est le Mass Entity Framework. Mass est un cadre conçu spécifiquement pour les scénarios où des centaines ou des milliers d'unités d'IA s'exécutent simultanément, capable d'effectuer des calculs orientés données . L'objectif de Mass est de dépasser les limites de performance de l'architecture Actor/Component et offre la possibilité de paralléliser certaines tâches (par exemple, la gestion d'unités à grande échelle) en les organisant de manière orientée données . De plus, le système physique Chaos a également évolué vers des approches orientées données et intègre des mécanismes de mise en cache pour éviter de recalculer les simulations . Par conséquent, les deux moteurs progressent sur la voie de la combinaison de la flexibilité de l'OOP et de la performance de la DOD, mais suivent des stratégies différentes découlant de leurs antécédents architecturaux distincts.

Analyse de la disposition de la mémoire et de l'efficacité du cache CPU

La performance des moteurs de jeu modernes est limitée par le fait que le CPU est beaucoup plus rapide que la vitesse à laquelle il extrait les données de la RAM. Le moyen de surmonter ce « goulet d'étranglement de von Neumann » est d'utiliser les caches L1, L2 et L3 du CPU, qui sont extrêmement rapides mais de petite taille, de la manière la plus efficace possible . Lorsqu'un CPU accède à une adresse mémoire, au lieu de lire uniquement les données de cette adresse en mémoire, il met immédiatement en cache quelques octets situés juste à côté (généralement une « ligne de cache » de 64 octets) . Si la prochaine donnée nécessaire se trouve dans cette ligne de cache, le CPU peut y accéder rapidement ; cette situation est appelée un « succès de cache » (cache hit). Sinon, il est contraint de charger un nouveau bloc de données depuis la RAM lente, ce qui est qualifié de « défaut de cache » (cache miss), un goulet d'étranglement de performance significatif. La conception orientée données (DOD) offre une solution architecturale visant à augmenter le taux de succès du cache en évitant les schémas d'accès à la mémoire dispersés apportés par l'OOP .

L'approche de programmation orientée objet (OOP) conduit naturellement à une disposition fragmentée de la mémoire. Un objet Character héberge des membres contenant différents types de données tels que la position, la santé (health) et la rotation. Chaque objet Character commence par sa propre adresse unique en mémoire. Lorsqu'on souhaite appeler la fonction de mise à jour (update) de tous les personnages dans une boucle, le CPU doit se rendre au début de chaque objet Character en mémoire. Les membres (données) de ces objets ne sont pas nécessairement proches les uns des autres. Par exemple, le membre position de CharacterA et le membre health de CharacterB ne se trouvent peut-être pas dans la même ligne de cache de 64 octets. Cela entraîne la nécessité pour le CPU de sauter continuellement vers des adresses mémoire différentes et éloignées lorsque nous voulons traiter les données de manière séquentielle. Passer à une nouvelle adresse à chaque fois signifie invalider la ligne de cache précédente, ce qui conduit à un taux élevé de défauts de cache (cache misses). Pour les plateformes mobiles, cette situation est encore plus critique ; accomplir davantage de traitement tout en consommant moins d'énergie constitue un avantage fondamental pour les appareils mobiles .

La conception orientée données (DOD) apporte une solution directe à ce problème. L'architecture DOTS d'Unity stocke les données dans des blocs de mémoire de taille fixe, entièrement séquentiels, appelés « Chunks » . Chaque Chunk contient un tableau « Structure of Arrays » (SoA) composé de composants du même type. Par exemple, toutes les données relatives à un composant nommé Translation (Position) sont stockées dans un seul bloc de mémoire continu. De même, les données de Rotation se trouvent séquentiellement dans un autre chunk ou dans un tableau SoA distinct au sein du même chunk. Cet agencement permet à un système (par exemple, MovementSystem) de traiter toutes les données de Translation dans une seule boucle for. Lors de l'exécution de cette boucle, le CPU ne charge qu'une seule fois un grand bloc de données depuis la mémoire et réutilise ces données encore et encore, car toutes les données associées sont très proches les unes des autres. Cette situation permet une utilisation efficace des caches CPU et réduit considérablement le taux de défauts de cache . Le regroupement par les entités de l'architecture ECS d'Unity de toutes les données de composants qu'elles contiennent dans un seul bloc de mémoire maximise cette efficacité . Plus important encore, la disposition des données en mémoire dans l'architecture DOTS d'Unity correspond parfaitement à la propre disposition de la mémoire du CPU (par exemple, les lignes de 64 octets du cache de données L1) . Les tailles et les alignements des zones de données sont optimisés en fonction des exigences des lignes de cache. Cela se transforme en un avantage significatif, en particulier sur les CPU mobiles modernes tels que l'Arm Cortex-X4, car les performances de ces architectures sont directement liées à une largeur de bande mémoire et une efficacité de cache au plus haut niveau.

Une philosophie similaire existe également dans Unreal Engine 5. Le Mass Entity Framework permet de stocker les données plus efficacement en mémoire en regroupant les entités et leurs composants selon des structures de données complexes appelées Archetypes. Un Archetype définit un ensemble spécifique de composants. Par exemple, vous pouvez créer un Archetype « Capable de se déplacer et possédant une arme ». Ensuite, toutes les entités possédant ces types de composants (par exemple, 500 soldats) sont automatiquement stockées dans les chunks appartenant à cet Archetype. Cela permet une exécution plus efficace des requêtes et un gaspillage moindre du cache CPU. Dans les nouvelles versions telles que UE 5.7, la réalisation d'optimisations de bas niveau pour Mass est une preuve évidente que ce cadre est axé sur la performance. De plus, le système physique Chaos a également évolué vers des approches orientées données et intègre des mécanismes de mise en cache pour éviter de recalculer les simulations. Des outils tels que le Chaos Cache Manager permettent de calculer une simulation une seule fois et de la rejouer plus d'une fois, empêchant ainsi le CPU d'effectuer la même opération à répétition. Cela procure un gain de performance considérable, en particulier dans les opérations gourmandes en CPU telles que la destruction dynamique ou les simulations de fluides.

En conclusion, l'optimisation de la disposition de la mémoire et l'efficacité du cache CPU constituent l'un des facteurs les plus critiques déterminant les performances des moteurs de jeu modernes. Alors que l'approche OOP gaspille le potentiel des caches CPU en rendant les schémas d'accès aux données naturellement dispersés, la DOD offre une solution directe à ce problème. DOTS d'Unity et le framework Mass d'Unreal Engine 5 sont tous deux conçus pour améliorer les performances en évitant les schémas d'accès à la mémoire dispersés et en utilisant efficacement les caches CPU. Ce changement architectural revêt une importance vitale pour offrir l'expérience fluide souhaitée, en particulier dans les jeux offrant un monde riche et vivant tel que Kena: Bridge of Spirits. Pour les plateformes mobiles, ces optimisations offrent la possibilité d'accomplir davantage de travail tout en consommant moins d'énergie, garantissant ainsi des économies d'énergie.

Intégration multithreading et capacité de traitement parallèle

Les processeurs modernes disposent de plusieurs cœurs de processeur, et chacun de ces cœurs peut fonctionner de manière autonome. Le multithreading est la capacité d'exploiter cette puissance de traitement parallèle dans la logique réelle d'un jeu. Cependant, la structure de code basée sur la POO (OOP) nécessite des mécanismes de synchronisation stricts pour éviter les conflits de données, ce qui peut entraîner des ralentissements inattendus et l'inactivité des cœurs du CPU. La Conception Orientée Données (DOD) offre une solution architecturale à ces problèmes, facilitant naturellement l'exécution parallèle du code.

Dans l'approche de la Programmation Orientée Objet (POO), le code s'exécute généralement de manière synchrone sur un seul thread principal (par exemple, GameThread dans Unreal ou Main Thread dans Unity). Ce thread traite de nombreux aspects de la logique du jeu, tels que les entrées des joueurs, la logique de l'IA et les mises à jour physiques. Bien que les autres sous-systèmes (rendu, audio, physique) s'exécutent souvent sur des threads séparés, ces threads fonctionnent généralement indépendamment du thread principal. Étant donné qu'une grande partie de votre code s'exécute sur un seul thread, les autres cœurs du CPU restent généralement inactifs. Si un développeur souhaite ajouter du parallélisme dans un projet basé sur la POO, il est obligé d'utiliser des mécanismes de synchronisation tels que des verrous (locks), des mutex ou des sémaphores pour éviter les conflits de données. Ces mécanismes peuvent amener les autres threads à attendre qu'un thread termine sa section critique (par exemple, une opération d'écriture sur une liste). Le temps d'attente signifie qu'un cœur du CPU reste inactif, ce qui entraîne des saccades (hitchs) imprévues et une baisse globale des performances. Le support du multithreading est inhérent à Unreal Engine 5, mais le moteur se retrouve parfois bloqué sur un seul thread, et l'exploitation complète de cette fonctionnalité par les développeurs est limitée. Cela signifie qu'une grande partie de la puissance potentielle du CPU est gaspillée.

La Conception Orientée Données (DOD) est un paradigme architectural conçu pour le traitement parallèle. La DOD adopte des principes qui éliminent les conflits de données entre les threads lisant les données à un seul endroit et les écrivant à un seul endroit. L'architecture DOTS d'Unity est l'exemple le plus net de cette philosophie et combine deux technologies fondamentales : le Job System et le Burst Compiler. Le Job System divise votre code en morceaux et vous permet de les exécuter en parallèle sur différents cœurs du CPU. Une boucle Entities.ForEach à l'intérieur d'un SystemBase est en fait compilée en tant que job. Ce job élimine les conflits de données (data races) en empêchant la modification des données sur un seul thread. Chaque job peut être exécuté en toute sécurité sur différents cœurs du CPU. Cela permet d'obtenir un parallélisme sans nécessiter de code de synchronisation complexe pour éviter les conflits de données. Le Burst Compiler est une optimisation de compilateur qui traduit le code C# en code C++ natif hautement optimisé. Burst utilise le back-end LLVM pour compiler le code au niveau C/C++, ce qui vous permet d'atteindre les performances obtenables avec le C++. Il est extrêmement efficace, en particulier pour les boucles, les calculs mathématiques et les opérations de conversion de données. Chaque SystemBase de votre système (par exemple, MovementSystem) s'exécute en fait comme un job et peut être optimisé par Burst pour s'exécuter en parallèle sur différents cœurs du CPU. Cela élimine les conflits de données en empêchant la modification des données sur un seul thread et garantit la sécurité du parallélisme.

Dans Unreal Engine 5, Mass Entity Framework adopte également des principes similaires qui facilitent l'utilisation du multithreading. Mass facilite l'utilisation du multithreading en organisant une tâche spécifique (par exemple, l'exécution de la logique de recherche de chemin pour des centaines d'IA) de manière orientée données et en l'exécutant en parallèle sur plusieurs threads. Mass est également conçu pour fonctionner en intégrant d'autres modèles de threads d'Unreal, tels que le graph de rendu. Cela permet à différentes parties du moteur de s'exécuter en parallèle sur différents threads, augmentant ainsi les performances globales. Depuis la version 5.0 d'Unreal Engine, le moteur lui-même a apporté des améliorations pour accroître l'utilisation du multithreading ; par exemple, les mises à jour de stabilité et de performance apportées à Vulkan RHI ont permis une meilleure utilisation du multithreading sur les plateformes de bureau et mobiles.

Exemple de code concret (Unity DOTS) :


public class CharacterController_OOP : MonoBehaviour
{
    public float moveSpeed = 5f;
    void Update()
    {
        
        foreach(var character in allCharacters)
        {
            character.Move(); 
        }
    }
}

using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;
public class MovementSystem_DOD : SystemBase
{
    protected override void OnUpdate()
    {
        float deltaTime = Time.DeltaTime;
       
        Entities.ForEach((ref Translation translation, in Rotation rotation, in MoveSpeed speed) =>
        {
            translation.Value += speed.value * deltaTime * math.rotate(rotation.Value, new float3(0, 0, 1)).normalize();
        }).Schedule();
    }
}

Dans cet exemple, alors que l'approche POO s'exécute de manière synchrone sur le thread principal, l'approche DOD planifie la boucle Entities.ForEach à l'intérieur de OnUpdate en tant qu'élément de travail pouvant être traité en parallèle par le Job System. Ce job peut être optimisé par Burst et exécuté simultanément sur différents cœurs du CPU. Cela élimine les conflits de données en empêchant la modification des données sur un seul thread et garantit la sécurité du parallélisme.

En conclusion, la DOD facilite naturellement l'utilisation du multithreading en éliminant les difficultés de synchronisation et les ralentissements inattendus apportés par la POO. DOTS d'Unity et le framework Mass d'Unreal Engine 5 sont conçus pour améliorer les performances en évitant les modèles d'accès à la mémoire dispersés et en utilisant efficacement les caches du CPU dans les deux moteurs. Ce changement architectural est d'une importance vitale pour offrir l'expérience fluide souhaitée, en particulier dans les jeux offrant un monde riche et vivant comme Kena: Bridge of Spirits. Pour les plateformes mobiles, ces optimisations offrent la possibilité d'accomplir plus de travail tout en consommant moins d'énergie, garantissant ainsi des économies d'énergie.

Pile technologique orientée données (DOTS) au sein de l'écosystème Unity

Tout en préservant l'architecture GameObject basée sur la POO, Unity introduit également une toute nouvelle pile technologique appelée Data-Oriented Technology Stack (DOTS) pour les applications gourmandes en performances. DOTS optimise la disposition de la mémoire en se basant sur les principes de la conception orientée données, augmente l'efficacité du cache du CPU et offre un support multithreading natif. Cette section examine les composants de base de DOTS, des exemples de code concrets et le processus d'intégration.

Le paquet Entities forme la base de DOTS. Ce paquet fournit de manière fondamentale des éléments ECS (Entity Component System) tels que des entités, des données de composants et des systèmes. La philosophie fondamentale ici est de séparer les données des fonctionnalités et de les stocker plus efficacement en mémoire. 1. Entités : Les entités sont des conteneurs vides dotés d'identifiants uniques qui ne transportent aucune donnée. Une entité n'est pas un objet ; c'est simplement un EntityId. Cela la rend très légère en mémoire et permet de les créer et de les détruire rapidement. 2. Données de composants : Ce sont des structures (structs) qui transportent des données. Elles doivent être une structure et l'héritage ne doit pas être utilisé. Par exemple, des structures de transport de données simples telles que Translation { public float3 Value; } ou Health { public int Value; } sont définies. Ces structures implémentent l'interface IComponentData. 3. Systèmes : Il s'agit de la logique qui traite ces données. Les classes dérivées de la classe SystemBase utilisent des requêtes (queries) pour sélectionner des types de données (composants) spécifiques et effectuent des opérations sur ces données. La méthode OnUpdate est planifiée comme un job et inclut généralement une boucle Entities.ForEach.

Exemple de code concret (Unity DOTS) : Le code suivant compare la façon dont le déplacement d'un personnage est traité différemment avec la POO et avec DOTS :


public class PlayerController_OOP : MonoBehaviour
{
    public float moveSpeed = 5f;
    void Update()
    {
      
        float moveInput = Input.GetAxis("Horizontal");
        transform.Translate(Vector3.right * moveInput * moveSpeed * Time.deltaTime);
    }
}

public struct Translation : IComponentData
{
    public float3 Value;
}
public struct Rotation : IComponentData
{
    public quaternion Value;
}
public struct MoveSpeed : IComponentData
{
    public float value;
}
// 2. Le système exprimant la logique du jeu
public class MovementSystem : SystemBase
{
    protected override void OnUpdate()
    {
       
        float deltaTime = Time.DeltaTime;
     
        Entities
            .WithName("MoveEntities")
            .ForEach((ref Translation translation, in Rotation rotation, in MoveSpeed speed) =>
            {
                translation.Value += speed.value * deltaTime * math.normalize(math.mul(rotation.Value, new float3(0, 0, 1)));
            }).Schedule();

        
        Dependency.Complete();
    }
}

Dans cet exemple, l'approche DOD montre comment les données sont regroupées et traitées en mémoire. La boucle à l'intérieur d'Entities.ForEach est compilée en tant qu'unique job qui peut être optimisé par Burst et exécuté en parallèle sur différents cœurs du CPU. Cela élimine les conflits de données en empêchant la modification des données sur un seul thread et garantit la sécurité du parallélisme.

Étapes d'intégration (Unity DOTS) : L'intégration de DOTS dans un projet Unity commence par la création d'un projet Unity standard, puis nécessite l'ajout des paquets requis via le Package Manager :

  1. Gestionnaire de paquets : Accédez au menu Edit > Project Settings > Packages ou au menu Window > Package Manager dans l'IDE.

  2. Ajouter les paquets requis : Les paquets DOTS de base suivants peuvent être ajoutés pour le projet :

    • com.unity.entities : Le paquet fournissant l'architecture ECS de base.

    • com.unity.jobs : Le paquet nécessaire pour exécuter le système de jobs.

    • com.unity.burst : Le paquet qui traduit et optimise le code C# en code C++ natif.

    • com.unity.mathematics : Fournit des types mathématiques de base tels que des opérations vectorielles et matricielles utilisées pour DOTS.

    • com.unity.netcode.entities : Nécessite pour les applications DOTS connectées en réseau.

  3. Définition de la structure des données : Des structures implémentant l'interface IComponentData sont créées. Ces structures transportent des données mais ne peuvent pas contenir de fonctions.

  4. Création d'un système : Des classes dérivées de la classe SystemBase sont créées. Ces classes remplacent (override) les méthodes de cycle de vie telles que OnUpdate, OnFixedUpdate ou OnCreate.

  5. Intégration du renderer : Un renderer est nécessaire pour afficher à l'écran les entités créées avec DOTS. Au départ, il est pratique d'utiliser Hybrid Renderer pour refléter les données DOTS sur les GameObject existants. Cela vous permet d'utiliser l'architecture orientée données de DOTS tout en préservant le pipeline de rendu existant. Ensuite, les paquets Entities Graphics intégrables à URP ou HDRP peuvent être utilisés.

Impacts stratégiques pour les mondes ouverts AAA et l'optimisation mobile : Des jeux comme Kena: Bridge of Spirits nécessitent à la fois un environnement riche et détaillé (monde ouvert AA) et un contrôle des personnages fluide et réactif. Les plateformes mobiles, quant à elles, ajoutent une couche de défi supplémentaire en termes de puissance CPU/GPU limitée et d'autonomie de la batterie.

Performances et fluidité : La DOD est l'un des outils les plus puissants pour réduire les ralentissements soudains et les chutes de FPS, en particulier dans les projets basés sur la POO. Les problèmes de saccades (stutters) dans le jeu Kena révèlent clairement la différence que l'approche DOD peut apporter. La DOD offre une capacité de traitement des données accrue en utilisant toute la puissance du CPU, ce qui permet de viser un 60 FPS plus stable.

Optimisation pour plateformes mobiles :

  • Puissance du CPU : Les CPU mobiles (par exemple, Arm Cortex-X4) deviennent de plus en plus puissants, mais l'efficacité énergétique reste un facteur critique. La DOD peut garantir un travail plus important avec une consommation d'énergie réduite en minimisant le temps d'inactivité du CPU. Des améliorations telles que la réduction de 33 % de la bande passante dans l'architecture Cortex-X4 d'Arm peuvent signifier que les applications s'exécutant avec la DOD en tireront encore plus profit.

  • Gestion de la mémoire : L'utilisation de la mémoire doit être gérée méticuleusement sur les plateformes mobiles. DOTS peut réduire l'utilisation globale de la mémoire en travaillant avec des données plus compactes et allouées en mémoire. Des technologies telles que les formats de compression AssetBundle d'Unity et SVT (Virtual Texturing), combinées à l'organisation des données de la DOD, peuvent créer une structure très efficace pour les plateformes mobiles.

  • Minification ProGuard : Des outils tels que la minification ProGuard pour Android, lorsqu'ils sont utilisés conjointement avec le code optimisé généré par la DOD, peuvent encore améliorer la taille de la distribution et les performances.

En conclusion, la pile DOTS d'Unity est un ensemble d'outils puissants conçus pour repousser les limites d'évolutivité de la POO. Les développeurs peuvent adopter une approche hybride sans perturber la structure basée sur la POO existante en utilisant DOTS dans les domaines où les performances du projet sont critiques (physique, IA, systèmes de particules).

Approches architecturales axées sur les performances dans Unreal Engine 5 : Mass et Chaos

Unreal Engine 5 est profondément orienté vers la programmation orientée objet (POO) dans son architecture fondamentale avec l'héritage UObject. Cependant, les exigences de haute performance et d'évolutivité nécessaires pour les jeux de qualité AAA ont poussé le moteur lui-même à évoluer et à s'adapter à des approches axées sur les données. Pour répondre à ce besoin, Unreal Engine 5 propose deux approches architecturales principales axées sur les performances : Mass Entity Framework et Chaos Physics. Ces deux systèmes dépassent la structure basée sur la POO d'Unreal en adoptant des principes de conception axés sur les données, offrant un gain de performance vital, en particulier pour les simulations à grande échelle et les simulations physiques.

Mass Entity Framework : Gameplay axé sur les données Mass est un framework conçu pour les calculs axés sur les données dans Unreal Engine 5, développé spécifiquement pour les scénarios où des centaines ou des milliers d'unités de jeu sont gérées simultanément. Au lieu de remplacer le gameplay d'Unreal, Mass est conçu comme une couche axée sur les données et adaptée au traitement parallèle, construite par-dessus l'architecture Actor/Component existante. Sa philosophie fondamentale est d'améliorer les performances en regroupant de grandes quantités de jeux de données similaires plus efficacement en mémoire et en les traitant en parallèle sur plusieurs threads.

Les composants fondamentaux de l'architecture Mass sont les suivants :

  • Entité : Dans Mass, une entité est un conteneur vide avec un identifiant unique qui ne transporte aucune donnée. Cela la rend extrêmement légère, tout comme les Entity dans Unity.

  • Composant : Il s'agit de la structure de données qui transporte les données. Il peut s'agir d'une valeur float, d'un vecteur ou d'une structure complexe. Les composants permettent de regrouper les données en mémoire.

  • Système : C'est la logique qui sélectionne les entités possédant des composants spécifiques et les traite. Ces systèmes peuvent être parallélisés lors du traitement des données.

  • Archétype : C'est le composant le plus important et le plus unique de l'architecture Mass. Un Archétype définit un ensemble de composants spécifique. Par exemple, un Archétype peut être créé avec les composants "Position", "Velocity" et "AIController". Ensuite, toutes les entités possédant ces types de composants (par exemple, 1000 unités ennemies) sont automatiquement stockées dans les blocs de mémoire appartenant à cet Archétype. Cela permet d'exécuter les requêtes plus efficacement et de gaspiller moins de cache CPU.

Exemple de code concret (Unreal Engine 5 - Mass) : Mass est généralement utilisé avec C++ ou via des nœuds dédiés dans Blueprint. Vous trouverez ci-dessous un exemple de pseudocode donnant une idée générale de la structure de base d'un système Mass :


USTRUCT(BlueprintType)
struct FPositionComponent
{
    GENERATED_BODY()
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Components")
    FVector Location;
};
USTRUCT(BlueprintType)
struct FVelocityComponent
{
    GENERATED_BODY()
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Components")
    FVector Velocity;
};

class FMovementSystem : public FGlobalComponentSystem
{
public:
    virtual void Tick(float DeltaTime) override
    {
      
        Query.ForEach(
            [&](FEntityHandle Entity, FMutableRef<FPositionComponent> PositionComp, FMutableRef<FVelocityComponent> VelocityComp)
            {
               
                PositionComp->Location += VelocityComp->Velocity * DeltaTime;
            });
    }
};
// 3. Le système est enregistré et intégré au moteur
IMPLEMENT_MODULE(MyModule, MyModuleName);

Dans cet exemple, l'appel Query.ForEach forme la base de la structure axée sur les données de Mass. Le système sélectionne toutes les entités possédant les composants spécifiés et traite les données de ces entités en une seule boucle. Cette boucle peut être exécutée sur différents threads par les mécanismes de traitement parallèle internes d'Unreal. Dans les versions récentes telles que UE 5.7, les optimisations

de bas niveau pour Mass se poursuivent, ce qui est une preuve claire de l'orientation performance du framework.

Physique Chaos et mise en cache :

Chaos est le moteur physique par défaut dans Unreal Engine 5 et a été conçu comme faisant partie de l'architecture POO. Cependant, Chaos lui-même a adopté des principes axés sur les données et a été conçu pour améliorer les performances, en particulier pour les simulations gourmandes en CPU. Chaos prend en charge des fonctionnalités telles que les articulations cassables et la restitution des collisions.

La caractéristique la plus importante de l'aspect axé sur les performances de Chaos est la mise en cache des simulations. Si une simulation physique complexe (par exemple, l'effondrement d'un grand bâtiment) est recalculée à chaque image, cette opération s'avère très coûteuse. Le gestionnaire de cache Chaos (Chaos Cache Manager) est conçu pour résoudre ce problème. Le développeur enregistre une simulation, et le moteur en sauvegarde une copie en mémoire ou sur disque. Ensuite, lorsque la simulation est relue, le moteur utilise les données enregistrées sans effectuer de nouveau calcul, ce qui permet d'économiser un temps de CPU considérable. Cela est extrêmement efficace, en particulier pour les effets répétés ou les objets dynamiques. Cependant, ce système doit être configuré méticuleusement pour fonctionner correctement ; s'il est mal configuré, les données du cache ne seront pas synchronisées et les objets risquent de sauter vers leurs positions d'origine.

Étapes d'intégration (Unreal Engine 5) :

L'intégration de systèmes tels que Mass et Chaos dans un projet Unreal est généralement plus aisée du fait qu'ils sont déjà présents dans la configuration par défaut du moteur.

  1. Activation du masque : Vous pouvez activer le plugin Mass dans les paramètres du projet (Settings > Plugins > Gameplay > Mass).

  2. Développement avec Blueprint ou C++ : Mass peut être utilisé depuis la palette Mass Entity dans l'éditeur Blueprint ou directement en écrivant du code C++. Le support de Blueprint est idéal pour le prototypage rapide.

  3. Création de système : Vous pouvez commencer en utilisant des contrôleurs prêts à l'emploi tels que MassEntityPlayerController ou en créant votre propre système en C++ via FMassExecutionContext.

  4. Paramètres Chaos : Les paramètres de physique Chaos se trouvent sous Project Settings > Chaos Physics. Dans ces paramètres, des fonctionnalités telles que la restitution des collisions peuvent être activées. Pour la mise en cache Chaos, l'option bEnableCache sous Chaos Solver Settings doit être activée.

Impacts stratégiques :

Les approches axées sur les données d'Unreal Engine 5 revêtent une importance stratégique, en particulier pour les mondes ouverts AAA et les plateformes mobiles. Dans des jeux de style Kena, il existe un vaste monde où la vie est continuellement présente devant le joueur. Lorsque les comportements instantanés de n'importe quel PNJ ou objet de ce monde sont gérés dans un style POO, des centaines d'adresses mémoire différentes sont consultées à chaque image, ce qui provoque des ralentissements (stutters). L'approche DOD (Data-Oriented Design) est d'une importance vitale pour garantir l'expérience fluide souhaitée dans ce type de jeux. Pour les plateformes mobiles, l'efficacité du cache CPU est encore plus critique ; traiter davantage de données tout en consommant moins d'énergie constitue un avantage fondamental pour les appareils mobiles. Le système de particules Niagara d'Unreal Engine 5, lorsqu'il est utilisé conjointement avec des technologies telles que le SVT (Virtual Texture), peut également apporter des améliorations significatives en termes de performances et de mémoire grâce à la mise en cache des simulations.

En conclusion, tout en conservant sa structure basée sur la POO, Unreal Engine 5 a développé Mass et Chaos, qui constituent une puissante couche axée sur les données pour répondre aux besoins de performance. Ces systèmes offrent aux développeurs les performances nécessaires pour des simulations à grande échelle et à forte densité de données, tout en fonctionnant de manière intégrée sans modifier l'architecture de base du moteur.

Impacts stratégiques et coût de productivité : une évaluation pour les jeux en monde ouvert AA et les plateformes mobiles

Le choix entre la conception orientée données (DOD) et la programmation orientée objet (OOP) dépasse le simple cadre de la préférence technique pour devenir une décision stratégique quant au succès d'un jeu. En particulier pour les jeux AAA riches, fluides et dotés de vastes mondes tels que Kena: Bridge of Spirits, ainsi que pour les plateformes mobiles aux ressources limitées, cette décision a de profondes répercussions sur les performances, la vitesse de développement et l'expérience de jeu. La DOD est un paradigme puissant conçu spécifiquement pour dépasser les limites d'évolutivité de l'OOP, mais ces avantages doivent être mis en balance avec des coûts tels que la courbe d'apprentissage et le coût d'intégration.

Impacts stratégiques pour les mondes ouverts AAA et les problèmes de fluidité :

Kena: Bridge of Spirits se distingue par un monde mythologique riche, un contrôle fluide des personnages et des éléments d'environnement détaillés. Les discussions sur les forums de la communauté Steam ont soulevé des problèmes de performance liés au jeu ("stutter" et "FPS drops"). Les causes techniques sous-jacentes à ces problèmes sont très probablement les limites d'évolutivité d'une architecture basée sur l'OOP. En OOP, chaque entité de la scène (PNJ, objet, système de particules) possède son propre GameObject et les composants qui y sont attachés. Ces GameObject étant stockés de manière dispersée en mémoire, la mise à jour de l'ensemble de ces entités à chaque image ordonne au CPU d'accéder à des centaines d'adresses mémoire différentes. Il en résulte un nombre élevé de défauts de cache CPU et le processeur est contraint de récupérer des données depuis une RAM lente, ce qui provoque des ralentissements perceptibles (micro-saccades). La DOD offre une solution directe à ce problème. Des frameworks tels que DOTS dans Unity ou Mass dans Unreal Engine 5 stockent des types de données similaires (par exemple, les données de position de tous les PNJ) de manière séquentielle en mémoire. Cela signifie qu'un thread peut récupérer l'ensemble de ces données en une seule fois depuis la mémoire et les traiter, ce qui augmente considérablement le taux de réussite du cache CPU et les performances globales. Cette architecture est d'une importance vitale pour offrir l'expérience fluide recherchée dans des jeux comme Kena. En exploitant toute la puissance du CPU, la DOD offre une plus grande capacité de traitement des données, ce qui permet de cibler un 60 FPS plus stable.

Impacts stratégiques pour l'optimisation des plateformes mobiles :

Les plateformes mobiles disposent de ressources plus limitées que les ordinateurs de bureau ou les consoles de jeux : moins de puissance CPU, moins de RAM et un facteur critique qu'est l'autonomie de la batterie. Par conséquent, pour les développeurs de jeux mobiles, l'efficacité énergétique et la gestion de la mémoire deviennent des mesures essentielles du succès du jeu.

  • Efficacité énergétique : Les processeurs mobiles (par exemple, Arm Cortex-X4) deviennent de plus en plus puissants, mais l'efficacité énergétique reste un facteur critique. La DOD permet au CPU d'accomplir plus de travail tout en consommant moins d'énergie, en minimisant son temps d'inactivité. Des améliorations telles que la réduction de 33% de la bande passante dans l'architecture Cortex-X4 d'Arm peuvent signifier que les applications fonctionnant avec la DOD en tireront encore plus de profit. Une utilisation réduite du CPU contribue également à ce que l'appareil chauffe moins et que la batterie se décharge plus lentement.

  • Gestion de la mémoire : Sur les plateformes mobiles, l'utilisation de la mémoire doit être gérée méticuleusement. La DOD peut réduire l'utilisation globale de la mémoire en travaillant avec des données plus compactes et allouées en mémoire. Des technologies telles que les formats de compression AssetBundle de Unity () et le SVT (Virtual Texturing) (), combinés à l'organisation des données de la DOD, peuvent créer une structure très efficace pour les plateformes mobiles. En outre, de nouvelles technologies telles que la prise en charge de DirectStorage par Unity () et le streaming de shaders soutiennent également la vision qui sous-tend l'architecture de gestion des données de la DOD.

  • Productivité et temps de développement : Les optimisations spécifiques aux plateformes mobiles (par exemple, la minification ProGuard) () et l'utilisation d'API spécifiques à la plateforme (), lorsqu'elles sont considérées conjointement avec la DOD, permettent de créer une base de code plus efficace et de rendre ces optimisations plus percutantes.

Coût de productivité et expérience des développeurs :

Les avantages en termes de performances offerts par la DOD doivent être mis en balance avec une courbe d'apprentissage prononcée et un coût de baisse initiale de la productivité.

  • Difficulté de transition : Un grand projet existant basé sur l'OOP ne peut pas être entièrement converti vers une architecture DOD. Une approche hybride est généralement adoptée : certains domaines critiques (physique, IA, systèmes de particules) sont migrés vers la DOD, tandis que d'autres (UI, certaines mécaniques de gameplay spécifiques) sont laissés sous la structure OOP existante. Bien que le Hybrid Renderer de Unity () facilite de telles transitions, une intégration complète présente des difficultés. Les problèmes rencontrés dans le jeu Kena sont très probablement liés à l'évolutivité de la structure existante basée sur l'OOP, ce qui suggère qu'ils pourraient être résolus par un changement d'architecture tel que la DOD.

  • Niveau d'expérience : La courbe d'apprentissage de la DOD peut être particulièrement raide, en particulier pour les développeurs ayant une expérience en OOP. En regardant le code, il est difficile de passer de la logique « j'ai un objet qui possède... » à la logique « que vais-je faire avec cet ensemble de données ? ». Cela peut initialement ralentir la vitesse de développement.

  • Écosystème d'outils : Unity et Unreal, basés sur l'OOP, possèdent un écosystème d'outils développé et mûri au fil des ans. Les frameworks DOD tels que DOTS et Mass progressent également, mais peuvent encore présenter des lacunes et une documentation plus récente. Par exemple, certains bugs liés à DOTS (comme les erreurs de chargement de l'assemblage burst) ( ) ou l'intégration de systèmes tels que le Chaos Cache Manager dans Unreal () peuvent encore poser des difficultés aux développeurs.

Évaluation comparative :

CritèreProgrammation Orientée Objet (OOP)Conception Orientée Données (DOD)Courbe d'apprentissageFaible, c'est un paradigme courant et populaireÉlevée, nécessite une nouvelle philosophieProductivité (Début)Élevée, adaptée au prototypage rapideFaible, peut être lente au débutPerformance (Évolutivité)Limitée, ralentit pour les grands ensembles de donnéesÉlevée, s'adapte bien à des millions de donnéesEfficacité du cache CPUFaible (accès dispersé)Élevée (accès séquentiel)Facilité de traitement parallèleMoyenne (nécessite une synchronisation)Élevée (naturellement parallèle)Adéquation aux plateformes mobilesRaisonnable, mais l'efficacité énergétique est plus faibleÉlevée, utilise le CPU plus efficacementIntégration (Projets existants)Facile, directement compatible avec les projets existantsDifficile, nécessite une approche hybride

Conclusion et recommandation stratégique :

Le choix entre l'OOP et la DOD varie en fonction de facteurs tels que l'échelle du jeu, la plateforme cible et le domaine d'expertise de l'équipe. Pour les projets de petite et moyenne envergure ou pour le prototypage rapide, l'OOP peut présenter des avantages en termes de vitesse de développement et de flexibilité. Cependant, pour les jeux AAA tels que Kena: Bridge of Spirits qui intègrent un monde riche, fluide et étendu, la DOD devient une option incontournable en termes de performances et d'évolutivité. Pour les plateformes mobiles, la DOD offre des atouts supplémentaires tels que l'efficacité énergétique et une utilisation réduite de la mémoire, ce qui la rend encore plus attrayante. Les développeurs doivent analyser attentivement les exigences du projet et envisager d'adopter une stratégie hybride combinant la flexibilité de l'OOP et les performances de la DOD. Cela permettra d'appliquer la bonne architecture dans les différentes parties du projet.

*Information : Les visuels ont été générés par Google Gemini.

UnityUE5UnityDOTSMultithreadingChaosPhysics
Outil de recherche approfondie
Qwen (Alibaba Cloud)
Qwen (Alibaba Cloud)
Outil de traduction
Google Gemini
Google Gemini

Partager