Por ejemplo, tener una clase base GameObject con una jerarquía de herencia profunda podría ser bueno para el mantenimiento ...
En realidad, las jerarquías profundas son generalmente peores para la mantenibilidad que las superficiales, y el estilo arquitectónico moderno para los objetos del juego tiende hacia enfoques poco profundos basados en la agregación .
Sin embargo, creo que este enfoque puede crear problemas de rendimiento. Por otro lado, todos los datos y funciones de los objetos del juego podrían ser globales. Lo cual sería un dolor de cabeza de mantenimiento, pero podría estar más cerca de un bucle de juego con un rendimiento óptimo.
El bucle que ha mostrado potencialmente tiene problemas de rendimiento, pero no, como lo implica su declaración posterior, porque tiene datos de instancia y funciones miembro en la GameObject
clase. Más bien, el problema es que si tratas a todos los objetos del juego como exactamente iguales, probablemente no estés agrupando esos objetos de manera inteligente, es probable que estén dispersos al azar por toda la lista. Potencialmente, entonces, cada llamada al método de actualización para ese objeto (si ese método es una función global o no, y si ese objeto tiene datos de instancia o "datos globales" flotando en alguna tabla en la que está indexando o lo que sea) diferente de la llamada de actualización en las últimas iteraciones de bucle.
Esto puede aumentar la presión sobre el sistema, ya que es posible que deba buscar en la memoria la función relevante dentro y fuera de la memoria y volver a llenar el caché de instrucciones con mayor frecuencia, lo que resulta en un bucle más lento. Si esto es observable o no a simple vista (o incluso en un generador de perfiles) depende exactamente de lo que se considera un "objeto de juego", cuántos de ellos existen en promedio y qué más está sucediendo en su aplicación.
Los sistemas de objetos orientados a componentes son una tendencia popular en este momento, aprovechando la filosofía de que la agregación es preferible a la herencia . Tales sistemas potencialmente le permiten dividir la lógica de "actualización" de componentes (donde "componente" se define más o menos como alguna unidad de funcionalidad, como lo que representa la parte físicamente simulada de algún objeto, que es procesada por el sistema físico ) en varios subprocesos, discriminados por tipo de componente, si es posible y deseado, lo que podría tener una ganancia de rendimiento. Como mínimo, puede organizar los componentes de manera que todos los componentes de un tipo dado se actualicen juntos , haciendo un uso óptimo de la memoria caché de la CPU. Un ejemplo de dicho sistema orientado a componentes se discute en este hilo .
Tales sistemas a menudo están muy desacoplados, lo que también es una bendición para el mantenimiento.
El diseño orientado a datos es un enfoque relacionado: se trata de orientarse en torno a los datos requeridos de los objetos como primera prioridad, de modo que esos datos puedan procesarse efectivamente en masa (por ejemplo). Esto generalmente significa una organización que trata de mantener juntos los datos utilizados para el mismo clúster de propósito y operar todos a la vez. No es fundamentalmente incompatible con el diseño OO, y puede encontrar alguna charla sobre el tema aquí en GDSE en esta pregunta .
En efecto, un enfoque más óptimo para el ciclo del juego sería, en lugar de su original
foreach(GameObject g in gameObjects) g.update();
algo mas como
ProcessUserInput();
UpdatePhysicsForAllObjects();
UpdateScriptsForAllObjects();
UpdateRenderDataForAllObjects();
RenderEverything();
En un mundo así, cada uno GameObject
puede tener un puntero o referencia a su propio PhysicsData
o Script
, o RenderData
, para los casos en los que pueda necesitar para interactuar con los objetos de forma individual, pero el real PhysicsData
, Scripts
, RenderData
, etcétera, todos estaríamos de sus respectivos subsistemas (simulador de física, entorno de alojamiento de script, renderizador) y procesado en masa como se indicó anteriormente.
Es muy importante tener en cuenta que este enfoque no es una bala mágica y no siempre producirá una mejora en el rendimiento (aunque generalmente es un mejor diseño que un árbol de herencia profunda). Es muy probable que note que básicamente no hay diferencia de rendimiento si tiene muy pocos objetos o muchos objetos para los que no puede paralelizar efectivamente las actualizaciones.
Desafortunadamente, no existe un bucle mágico que sea el más óptimo: cada juego es diferente y puede necesitar ajustes de rendimiento de diferentes maneras. Por lo tanto, es muy importante medir las cosas (perfil) antes de seguir ciegamente el consejo de un tipo aleatorio en Internet.