Les jeux vidéo sont-ils orientés objet? [fermé]

18

Je me suis toujours demandé dans quelle mesure l'orientation des objets était utilisée par les jeux vidéo, en particulier les grands projets 3D. Je pense que ce serait cool que les deux trollet a werewolfhérité ses attributs de enemyet réenregistrés certains d'entre eux. Mais peut-être que les cours sont trop lourds pour être pratiques, je ne sais pas ...

vemv
la source
2
Comment quantifier (ou même qualifier objectivement) l'orientation objet? Voulez-vous une réponse dans Meyers ou Dahl-Nygaards?
De toute évidence, ils sont si lourds qu'un langage fortement basé sur l'OO est la norme de l'industrie.
The Communist Duck
4
Si vous voulez dire C ++, il a été choisi parce que c'est un bon langage pour explorer la programmation générique de type sécurisé d'une manière soucieuse de la vitesse; les quelques fonctionnalités de POO sont évidemment une fausse conception malheureuse conservée uniquement pour une compatibilité descendante. ;)

Réponses:

20

Pour revenir à votre exemple, je vais vous expliquer mes réflexions sur Entites dans les jeux vidéo. Je ne discuterai pas de l'avantage et des inconvénients généraux des objets et des classes, ni de leur rôle dans les jeux vidéo.

Les entités dans les petits jeux vidéo sont principalement définies par des classes distinctes, dans les grands jeux, elles sont souvent définies dans des fichiers. Il y a deux approches générales à suivre:

Extension : Dans votre exemple Trollet Werewolfprolongera Enemy, qui se prolonge Entity. Entityprend soin des choses de base comme le mouvement, la santé, etc., tout en Enemydéclarant les sous-classes comme des ennemis et faisant d'autres choses (Minecraft suit cette approche).

Basé sur les composants : La deuxième approche (et ma préférée) est une Entityclasse, qui a des composants. Un composant pourrait être Moveableou Enemy. Ces composants s'occupent d' une chose comme le mouvement ou la physique. Cette méthode rompt les longues chaînes d'extension et minimise le risque de se heurter à un conflit. Par exemple: Comment pouvez-vous faire des ennemis non mobiles, s'ils héritent d'un personnage mobile? .

Dans les grands jeux vidéo comme World of Warcraft ou Starcraft 2, les entités ne sont pas des classes, mais des ensembles de données, qui spécifient l'entité entière. Les scripts sont également importants, car vous définissez ce que l'entité ne fait pas dans une classe, mais dans un script (s'il est unique, pas des choses comme le déplacement).

Je programme actuellement un RTS et j'utilise l'approche basée sur les composants avec des fichiers de descripteur et de script pour les entités, les compétences, les éléments et tout le reste dynamique dans le jeu.

Marco
la source
2
Cela semble intéressant. Mais je ne mentirai pas, je ne sais pas ce que componentc'est dans ce contexte. Un lien serait très apprécié.
vemv
Un composant est une entité qui a la responsabilité d'une seule fonctionnalité. L'entité basée sur les composants possédera un composant (si c'est le cas), il n'en héritera pas. La propriété implique qu'une entité peut désactiver l'un de ses composants en modifiant son comportement. Dans le paradigme d'Extension classique, le comportement est dû à "l'appartenance" à une super-classe. Une entité peut être un composant et un composant peut être modélisé en tant que classe ou en tant que structure si vous préférez.
FxIII
Composant (parfois appelé systèmes d'entité) - vous pouvez rechercher des idées sur ce blog t-machine.org Il existe des variantes, mais l'idée de base est qu'un composant est un objet à des fins spéciales et que votre acteur relie un tas de composants, comme la construction d'un modèle de Legos.
Patrick Hughes
2
J'ai commencé un blog sur le développement de jeux et j'ai écrit ma première entrée sur ce sujet. Rien de nouveau, mais avec un peu de code: jagamedev.wordpress.com/2011/06/26/…
Marco
23

Je pense que ce serait cool que les trolls et les loups-garous héritent de leurs attributs de l'ennemi et en écrasent certains.

Malheureusement, cette approche du polymorphisme, populaire dans les années 90, s'est révélée être une mauvaise idée dans la pratique. Imaginez que vous ajoutez «loup» à la liste des ennemis - eh bien, il partage certains attributs avec le loup-garou, donc vous voudriez les consolider dans une classe de base partagée, par exemple. «WolfLike». Maintenant, vous ajoutez «Humain» à la liste ennemie, mais les loups-garous sont parfois des humains, ils partagent donc également des attributs, comme marcher sur 2 jambes, pouvoir parler, etc. Créez-vous une base commune «humanoïde» pour les humains et les loups-garous , et devez-vous alors arrêter les loups-garous dérivant de WolfLike? Ou multipliez-vous l'héritage des deux - dans ce cas, quel attribut a priorité lorsque quelque chose dans Humanoid entre en conflit avec quelque chose dans WolfLike?

Le problème est qu'essayer et modéliser le monde réel comme un arbre de classes est inefficace. Prenez 3 choses et vous pouvez probablement trouver 4 façons différentes de factoriser leur comportement en partagé et non partagé. C'est pourquoi beaucoup se sont plutôt tournés vers un modèle modulaire ou basé sur des composants, où un objet est composé de plusieurs objets plus petits qui composent l'ensemble, et les objets plus petits peuvent être échangés ou modifiés pour créer le comportement que vous souhaitez. Ici, vous ne pouvez avoir qu'une seule classe ennemie, et elle contient des sous-objets ou des composants pour la façon dont elle marche (par exemple. Bipède contre Quadrupède), ses méthodes d'attaque (par exemple, morsure, griffe, arme, poing), sa peau (verte pour les trolls, à fourrure pour les loups-garous et les loups), etc.

C'est toujours complètement orienté objet, mais la notion de ce qui est un objet utile est différente de ce qui était communément enseigné dans les manuels. Plutôt que d'avoir un grand arbre d'héritage de diverses classes abstraites et plusieurs classes concrètes aux extrémités de l'arbre, vous n'avez généralement qu'une seule classe concrète représentant un concept abstrait (par exemple, «ennemi») mais qui contient des classes plus concrètes représentant des concepts plus abstraits (p. ex. attaques, type de peau). Parfois, ces deuxièmes classes peuvent être mieux implémentées via 1 niveau d'héritage (par exemple une classe d'attaque de base et plusieurs classes dérivées pour des attaques spécifiques) mais l'arbre d'héritage profond a disparu.

Mais peut-être que les cours sont trop lourds pour être pratiques, je ne sais pas ...

Dans la plupart des langues modernes, les cours ne sont pas «lourds». Ils ne sont qu'une autre façon d'écrire du code, et ils enveloppent généralement l'approche procédurale que vous auriez écrite de toute façon, sauf avec une syntaxe plus facile. Mais par tous les moyens, n'écrivez pas une classe où une fonction fera l'affaire. Les classes ne sont pas intrinsèquement meilleures, seulement là où elles rendent le code plus facile à gérer ou à comprendre.

Kylotan
la source
3
J'adore cette réponse :)
Czarek Tomczak
8

Je ne sais pas trop ce que vous entendez par «trop lourd», mais C ++ / OOP est la lingua franca du développement de jeux pour les mêmes bonnes raisons qu'il est utilisé ailleurs.

Parler de souffler des caches est un problème de conception, pas une fonctionnalité de langue. Le C ++ ne peut pas être trop mauvais car il est utilisé dans les jeux depuis 15-20 ans. Java ne peut pas être trop mauvais car il est utilisé dans les environnements d'exécution très serrés des téléphones intelligents.

Passons maintenant à la vraie question: pendant de nombreuses années, les hiérarchies de classes sont devenues profondes et ont commencé à souffrir de problèmes liés à cela. Plus récemment, les hiérarchies de classes se sont aplaties et la composition et d'autres conceptions basées sur les données sont plutôt que l'héritage logique.

Tout est POO et toujours utilisé comme base de référence lors de la conception de nouveaux logiciels, juste un accent différent sur ce que les classes incarnent.

Patrick Hughes
la source
2

Les classes n'impliquent aucune surcharge d'exécution. Le polymorphisme au moment de l'exécution appelle simplement un pointeur de fonction. Ces frais généraux sont extrêmement minimes par rapport à d'autres coûts tels que les appels Draw, la synchronisation simultanée, les E / S disque, les commutateurs en mode noyau, la mauvaise localisation de la mémoire, la surutilisation de l'allocation dynamique de la mémoire, le mauvais choix d'algorithmes, l'utilisation de langages de script et la liste continue. Il y a une raison pour laquelle l'orientation objet a essentiellement supplanté ce qui l'a précédé, et c'est parce que c'est beaucoup mieux.

DeadMG
la source
La répartition entre les différents types polymorphes se fait d'une manière qui donne une mauvaise localisation mémoire. Même dans les implémentations les plus légères comme les vtables C ++ ou la mise en cache IMP Objective-C, si vous utilisez réellement le polymorphisme pour traiter des objets de types différents, vous ferez exploser vos caches. Bien sûr, si vous n'utilisez pas le polymorphisme, la vtable reste dans votre cache ou le message en cache IMP conduit au bon endroit, mais alors pourquoi s'embêter? Et en Java ou C # le coût est plus lourd, et en JavaScript ou Python le coût est encore plus lourd.
1
@Joe Wreschnig: Si vous utilisez ces langages plus lents, le coût de l'OO est trivial par rapport aux autres coûts tels que l'interprétation / JIT. Plus important encore, vous pouvez théoriser ce que vous voulez à propos de la mauvaise localisation de la mémoire, mais le fait est que la prédiction de branche, l'exécution dans le désordre et les fonctionnalités similaires sur le CPU annulent pratiquement entièrement le coût. Sinon, pourquoi tant de logiciels haute performance sont-ils écrits en utilisant l'orientation objet?
DeadMG
1

Une chose à savoir est que les concepts de code orienté objet sont souvent plus précieux que leur implémentation dans les langages. En particulier, devoir effectuer des milliers d'appels de mise à jour virtuelle sur des entités dans une boucle principale peut provoquer un gâchis dans le cache en C ++ sur des plates-formes comme 360 ​​ou PS3 - donc l'implémentation peut éviter des mots clés "virtuels" ou même "classe" pour le plaisir de vitesse.

Souvent, il suivra toujours les concepts OO d'héritage et d'encapsulation - les implémentant simplement différemment de la façon dont le langage se fait (par exemple, des modèles curieusement récursifs, la composition au lieu de l'héritage (la méthode basée sur les composants décrite par Marco)). Si l'héritage peut toujours être résolu au moment de la compilation, les problèmes de performances disparaissent, mais le code peut devenir un peu poilu avec les modèles, les implémentations RTTI personnalisées (là encore, les intégrées sont généralement peu pratiques) ou la logique de compilation dans macros etc.

Dans Objective-C pour iOS / Mac, il y a des problèmes similaires, mais pires avec le schéma de messagerie (même avec les trucs de mise en cache de fantaisie) ...

jheriko
la source
0

La méthode que j'essaierai d'utiliser l'héritage de classes mixtes et les composants. (Tout d'abord, je ne fais pas de l'ennemi une classe - j'en fais un composant.) Je n'aime pas l'idée d'utiliser une classe d'entité générique pour tout, puis d'ajouter les composants nécessaires pour étoffer l'entité. Au lieu de cela, je préfère qu'une classe ajoute automatiquement des composants (et des valeurs par défaut) dans le constructeur. Ensuite, les sous-classes ajoutent leurs composants supplémentaires dans leur constructeur, afin que la sous-classe ait ses composants et ses composants de classe parent. Par exemple, une classe d'armes ajouterait des composants de base communs à toutes les armes, puis la sous-classe des épées ajouterait des composants supplémentaires spécifiques aux épées. Je suis toujours en train de débattre de l'utilisation des ressources text / xml pour définir les valeurs des entités (ou des épées spécifiques par exemple), ou de faire tout cela dans le code, ou quel est le bon mélange. Cela permet toujours au jeu d'utiliser les informations de type et le polymorphisme du langage de code, et rend ObjectFactories beaucoup plus simple.

Chris
la source
3
Salut Chris. Bien que partager votre expérience et vos plans soit formidable, il ne répond pas vraiment directement à la question. Je pense que la question est plutôt de savoir comment ces choses sont généralement faites et où vous répondez à la façon dont vous prévoyez de le faire . Ce sont des questions similaires, mais pas vraiment les mêmes.
MichaelHouse