¿Qué tan orientados a objetos son los videojuegos? [cerrado]

18

Siempre me he preguntado hasta qué punto los videojuegos utilizan la orientación a objetos, especialmente los grandes proyectos 3D. Creo que estaría bien que tanto trolly werewolfheredaron sus atributos desde enemyy sobreescribí algunos de ellos. Pero tal vez las clases son demasiado pesadas para ser prácticas, no sé ...

vemv
fuente
2
¿Cómo se cuantifica (o incluso califica objetivamente) la orientación a objetos? ¿Quieres una respuesta en Meyers o Dahl-Nygaards?
Obviamente son tan pesados ​​que un lenguaje fuertemente basado en OO es el estándar de la industria.
El pato comunista
44
Si te refieres a C ++, se eligió porque es un buen lenguaje para explorar la programación genérica de tipo seguro de una manera consciente de la velocidad; Las pocas características de OOP son obviamente un diseño desafortunado que se mantiene solo para compatibilidad con versiones anteriores. ;)

Respuestas:

20

Para llegar a tu ejemplo, explicaré mis pensamientos sobre Entites en videojuegos. No discutiré las ventajas y desventajas generales de los objetos y las clases, ni su papel en los videojuegos.

Las entidades en los videojuegos pequeños se definen principalmente por clases separadas, en los grandes juegos a menudo se definen en archivos. Hay dos enfoques generales a seguir:

Extensión : en su ejemplo Trolly Werewolfse extenderá Enemy, que se extiende Entity. Entityse ocupa de las cosas básicas como el movimiento, la salud, etc., mientras Enemyque declararía a las subclases como enemigas y haría otras cosas (Minecraft sigue este enfoque).

Basado en componentes : el segundo enfoque (y mi favorito) es una Entityclase, que tiene componentes. Un componente podría ser Moveableo Enemy. Estos componentes se encargan de una cosa como el movimiento o la física. Este método rompe largas cadenas de extensión y minimiza el riesgo de encontrarse con un conflicto. Por ejemplo: ¿Cómo puedes hacer enemigos no movibles, si heredan de un personaje movible? .

En grandes videojuegos como World of Warcraft o Starcraft 2, las Entidades no son clases, sino conjuntos de datos que especifican toda la Entidad. La secuencia de comandos también es importante, porque define lo que la entidad no hace en una clase, sino en una secuencia de comandos (si es única, no cosas como moverse).

Actualmente estoy programando un RTS y adopto el enfoque basado en componentes con archivos Descriptor y Script para Entites, Skills, Items y todo lo demás dinámico en el juego.

Marco
fuente
2
Suena interesante. Sin embargo, no mentiré, no sé qué componentes a en este contexto. Un enlace sería muy apreciado.
vemv
Un componente es una entidad que tiene la responsabilidad de una única funcionalidad. La entidad basada en componentes poseerá un componente (si es el caso) que no heredará de él. La propiedad implica que una entidad puede desactivar uno de sus componentes cambiando su comportamiento. En el paradigma clásico de Extensión, el comportamiento se debe a "pertenecer a" una superclase. Una entidad puede ser un componente y un componente puede modelarse como una clase o como una estructura si lo prefiere.
FxIII
Componente (a veces llamado sistemas entidad) - se puede buscar este blog para las ideas t-machine.org Hay variantes, pero la idea básica es que un componente es un objeto de propósito especial y sus lazos actores juntos un montón de componentes, como la construcción de un modelo de Legos
Patrick Hughes
2
Empecé un blog sobre desarrollo de juegos y escribí mi primera entrada de blog sobre esto. Nada nuevo, pero con un poco de código: jagamedev.wordpress.com/2011/06/26/…
Marco
23

Creo que sería genial que tanto el troll como el hombre lobo heredaran sus atributos del enemigo y sobrescribieran algunos de ellos.

Lamentablemente, este enfoque del polimorfismo, popular en los años 90, ha demostrado ser una mala idea en la práctica. Imagina que agregas 'lobo' a la lista de enemigos; bueno, comparte algunos atributos con el hombre lobo, por lo que querrás consolidarlos en una clase base compartida, por ejemplo. 'WolfLike'. Ahora agregas 'Humano' a la lista de enemigos, pero los hombres lobo a veces son humanos, por lo que también comparten atributos, como caminar sobre dos patas, poder hablar, etc. ¿Creas una base común 'Humanoide' para humanos y hombres lobo? , ¿y luego tienes que detener a los hombres lobo derivados de WolfLike? ¿O multiplica la herencia de ambos, en cuyo caso, qué atributo tiene prioridad cuando algo en Humanoid choca con algo en WolfLike?

El problema es que probar y modelar el mundo real como un árbol de clases es ineficaz. Tome 3 cosas y probablemente pueda encontrar 4 formas diferentes de factorizar su comportamiento en compartido y no compartido. Esta es la razón por la que muchos han optado por un modelo modular o basado en componentes, en el que un objeto se compone de varios objetos más pequeños que forman el todo, y los objetos más pequeños se pueden intercambiar o cambiar para crear el comportamiento que desea. Aquí puede tener solo 1 clase de Enemigo, y contiene subobjetos o componentes de cómo camina (por ejemplo, Bípedo contra Cuadrúpedo), sus métodos de ataque (por ejemplo, mordedura, garra, arma, puño), su piel (verde para trolls, peludos para hombres lobo y lobos), etc.

Esto todavía está completamente orientado a objetos, pero la noción de lo que es un objeto útil es diferente de lo que solía enseñarse en los libros de texto. En lugar de tener un gran árbol de herencia de varias clases abstractas y varias clases concretas en las puntas del árbol, generalmente tiene solo 1 clase concreta que representa un concepto abstracto (por ejemplo, 'enemigo') pero que contiene más clases concretas que representan conceptos más abstractos (por ejemplo, ataques, tipo de piel). A veces, estas segundas clases se pueden implementar mejor a través de 1 nivel de herencia (por ejemplo, una clase de ataque base y varias clases derivadas para ataques específicos), pero el árbol de herencia profunda se ha ido.

Pero tal vez las clases son demasiado pesadas para ser prácticas, no sé ...

En la mayoría de los idiomas modernos, las clases no son "pesadas". Son solo otra forma de escribir código y, por lo general, simplemente envuelven el enfoque de procedimiento que habría escrito de todos modos, excepto con una sintaxis más fácil. Pero, por supuesto, no escriba una clase donde una función servirá. Las clases no son intrínsecamente mejores, solo cuando hacen que el código sea más fácil de administrar o comprender.

Kylotan
fuente
3
Me encanta esta respuesta :)
Czarek Tomczak
8

No estoy seguro de qué quieres decir con "demasiado pesado", pero C ++ / OOP es la lengua franca del desarrollo de juegos por las mismas buenas razones por las que se usa en otros lugares.

Hablar de volar cachés es un problema de diseño, no una característica del lenguaje. C ++ no puede ser tan malo ya que se ha utilizado en juegos durante los últimos 15-20 años. Java no puede ser tan malo, ya que se utiliza en entornos de tiempo de ejecución muy ajustados de teléfonos inteligentes.

Ahora a la pregunta real: durante muchos años las jerarquías de clase se hicieron profundas y comenzaron a sufrir problemas relacionados con eso. En tiempos más recientes, las jerarquías de clases se han aplanado y la composición y otros diseños basados ​​en datos están siendo más que una herencia basada en la lógica.

Todo es POO y todavía se usa como línea de base al diseñar un nuevo software, solo se enfoca de manera diferente en lo que encarnan las clases.

Patrick Hughes
fuente
2

Las clases no implican ninguna sobrecarga en tiempo de ejecución. El polimorfismo en tiempo de ejecución solo llama a través de un puntero de función. Estos gastos generales son extremadamente mínimos en comparación con otros costos como llamadas de Draw, sincronización de concurrencia, E / S de disco, conmutadores de modo de kernel, mala ubicación de memoria, uso excesivo de la asignación de memoria dinámica, mala elección de algoritmos, uso de lenguajes de script y la lista sucede. Hay una razón por la que la orientación a objetos esencialmente ha suplantado lo que vino antes, y es porque es mucho mejor.

DeadMG
fuente
El envío entre varios tipos polimórficos se realiza de una manera que da poca memoria a la localidad. Incluso en las implementaciones más livianas, como las vtables de C ++ o el almacenamiento en caché de Objective-C IMP, si realmente usa el polimorfismo para lidiar con objetos de diversos tipos, volará sus cachés. Por supuesto, si no usa el polimorfismo, la vtable permanece en su caché o el mensaje en caché de IMP conduce al lugar correcto, pero entonces ¿por qué molestarse? Y en Java o C #, el costo es mayor, y en JavaScript o Python, el costo es aún mayor.
1
@ Joe Wreschnig: Si está utilizando esos idiomas más lentos, entonces el costo de OO es trivial en comparación con los otros costos, como la interpretación / JIT. Más importante aún, puede elaborar teóricamente lo que quiere sobre la localidad de memoria pobre, pero el hecho es que la predicción de rama, la ejecución fuera de orden y características similares en la CPU prácticamente niegan el costo. De lo contrario, ¿por qué se escribe tanto software de alto rendimiento utilizando la orientación a objetos?
DeadMG
1

Una cosa a tener en cuenta es que los conceptos de código orientado a objetos a menudo son más valiosos que su implementación en lenguajes. En particular, tener que realizar miles de llamadas de actualización virtual en entidades en un bucle principal puede causar un desastre en la memoria caché en C ++ en plataformas como 360 o PS3, por lo que la implementación podría evitar palabras clave "virtuales" o incluso "de clase" por el bien de velocidad

A menudo, aunque seguirá los conceptos OO de herencia y encapsulación, solo los implementará de manera diferente a cómo lo hace el lenguaje (por ejemplo, plantillas curiosamente recursivas, composición en lugar de herencia (el método basado en componentes descrito por Marco)). Si la herencia siempre se puede resolver en tiempo de compilación, los problemas de rendimiento desaparecen, pero el código puede volverse un poco complicado con plantillas, implementaciones RTTI personalizadas (una vez más, las incorporadas suelen ser muy lentas) o la lógica de tiempo de compilación en macros etc.

En Objective-C para iOS / Mac hay problemas similares, pero peores, con el esquema de mensajería (incluso con las cosas sofisticadas de almacenamiento en caché) ...

jheriko
fuente
0

El método que intentaré usar combina la herencia de clases y los componentes. (En primer lugar, no convierto al Enemigo en una clase, lo convierto en un componente). No me gusta la idea de usar una clase de entidad genérica para todo y luego agregar los componentes necesarios para desarrollar la entidad. En cambio, prefiero que una clase agregue automáticamente componentes (y valores predeterminados) en el constructor. Luego, las subclases agregan sus componentes adicionales en su constructor, por lo que la subclase tendría sus componentes y sus componentes de clase primaria. Por ejemplo, una clase de Arma agregaría componentes básicos comunes a todas las armas, y luego la subclase de Espada agregaría cualquier componente adicional específico para espadas. Todavía estoy debatiendo si usar activos text / xml para definir valores para entidades (o espadas específicas, por ejemplo), o hacer todo eso en código, o cuál es la combinación correcta. Esto aún permite que el juego use la información de tipo y el polimorfismo del lenguaje de código, y hace que ObjectFactories sea mucho más simple.

Chris
fuente
3
Hola Chris Si bien compartir su experiencia y sus planes es excelente, en realidad no responde directamente a la pregunta. Creo que la pregunta es más acerca de cómo se hacen estas cosas en general cuando respondes cómo planeas hacerlo . Son preguntas similares, pero en realidad no son lo mismo.
MichaelHouse