Por cada juego que hice, acabo colocando todos mis objetos de juego (balas, autos, jugadores) en una lista de matriz única, que recorro para dibujar y actualizar. El código de actualización para cada entidad se almacena dentro de su clase.
Me he estado preguntando, ¿es esta la forma correcta de hacer las cosas o es una forma más rápida y eficiente?
software-engineering
Derek
fuente
fuente
Respuestas:
No hay una manera correcta. Lo que está describiendo funciona, y presumiblemente es lo suficientemente rápido como para que no importe, ya que parece estar preguntando porque le preocupa el diseño y no porque le haya causado problemas de rendimiento. Entonces es una manera correcta, claro.
Ciertamente, podría hacerlo de manera diferente, por ejemplo, manteniendo una lista homogénea para cada tipo de objeto o asegurándose de que todo en su lista heterogénea esté agrupado de manera tal que haya mejorado la coherencia para el código que está en caché o para permitir actualizaciones paralelas. Es difícil decir si vería una mejora apreciable del rendimiento al hacer esto sin conocer el alcance y la escala de sus juegos.
También podría factorizar aún más las responsabilidades de sus entidades (parece que las entidades se actualizan y se representan a sí mismas en este momento) para seguir mejor el Principio de responsabilidad única. Esto puede aumentar la capacidad de mantenimiento y la flexibilidad de sus interfaces individuales al desacoplarlas entre sí. Pero, una vez más, es difícil para mí saber si o no verías un beneficio del trabajo adicional que implicaría saber lo que sé de tus juegos (que básicamente no es nada).
Recomiendo no estresarse demasiado al respecto y tener en cuenta que podría ser un área potencial de mejora si alguna vez nota problemas de rendimiento o de mantenimiento.
fuente
Como han dicho otros, si es lo suficientemente rápido, entonces es lo suficientemente rápido. Si se pregunta si hay una mejor manera, la pregunta que debe hacerse es
¿Me encuentro iterando a través de todos los objetos pero solo operando en un subconjunto de ellos?
Si lo hace, eso es una indicación de que es posible que desee tener varias listas que contengan solo referencias relevantes. Por ejemplo, si está recorriendo cada objeto del juego para renderizarlos pero solo se necesita renderizar un subconjunto de objetos, puede valer la pena mantener una lista Renderizable separada solo para aquellos objetos que necesitan renderizarse.
Esto reduciría la cantidad de objetos que recorre y omite las comprobaciones innecesarias porque ya sabe que todos los objetos de la lista deben procesarse.
Tendría que perfilarlo para ver si está obteniendo una gran ganancia de rendimiento.
fuente
Depende casi por completo de tus necesidades específicas de juego . Puede funcionar perfectamente para un juego simple, pero falla en un sistema más complejo. Si está funcionando para tu juego, no te preocupes por eso o trata de diseñarlo en exceso hasta que surja la necesidad.
En cuanto a por qué el enfoque simple puede fallar en algunas situaciones, es difícil resumirlo en una sola publicación, ya que las posibilidades son infinitas y dependen completamente de los juegos en sí. Otras respuestas han mencionado, por ejemplo, que es posible que desee agrupar objetos que comparten responsabilidades en una lista separada.
Ese es uno de los cambios más comunes que podrías estar haciendo dependiendo del diseño de tu juego. Aprovecho la oportunidad para describir algunos otros ejemplos (más complejos), pero recuerde que todavía hay muchas otras posibles razones y soluciones.
Para empezar, señalaré que actualizar los objetos de su juego y representarlos puede tener diferentes requisitos en algunos juegos y, por lo tanto, deben manejarse por separado. Algunos posibles problemas con la actualización y representación de objetos del juego:
Actualización de objetos del juego
Aquí hay un extracto de Game Engine Architecture que recomendaría leer:
En otras palabras, en algunos juegos es posible que los objetos dependan unos de otros y requieran un orden específico de actualización. En este escenario, es posible que deba diseñar una estructura más compleja que una lista para almacenar objetos y sus interdependencias.
Representación de objetos del juego
En algunos juegos, las escenas y los objetos crean una jerarquía de nodos padre-hijo y deben representarse en el orden correcto y con transformaciones en relación con sus padres. En esos casos, es posible que necesite una estructura más compleja como un gráfico de escena (una estructura de árbol) en lugar de una sola lista:
Otras razones podrían ser, por ejemplo, el uso de una estructura de datos de particionamiento espacial para organizar sus objetos de una manera que mejore la eficiencia de realizar el sacrificio de vista de frustum.
fuente
La respuesta es "sí, esto está bien". Cuando trabajé en grandes simulaciones de hierro donde el rendimiento no era negociable, me sorprendió ver todo en una gran matriz global (GRANDE y GRANDE). Más tarde trabajé en un sistema OO y pude comparar los dos. Aunque era súper feo, la versión de "una matriz para gobernarlos a todos" en C simple y simple de un solo subproceso compilada en depuración fue varias veces más rápida que el sistema OO multiproceso "bonito" compilado -O2 en C ++. Creo que un poco tuvo que ver con la excelente localidad de caché (tanto en espacio como en tiempo) en la versión big-fat-array.
Si desea rendimiento, una matriz C global grande y gruesa será el límite superior (por experiencia).
fuente
Básicamente, lo que quiere hacer es crear listas según las necesite. Si vuelve a dibujar los objetos del terreno de una sola vez, una lista de ellos podría ser útil. Pero para que esto valga la pena, necesitaría decenas de miles de ellos, y muchos 10,000 de cosas que no son terreno. De lo contrario, examinar su lista de todo y usar un "si" para extraer objetos del terreno es lo suficientemente rápido.
Siempre he necesitado una lista maestra, independientemente de cuántas otras listas haya tenido. Por lo general, lo convierto en un mapa, una tabla con clave o un diccionario, de algún tipo para que pueda acceder aleatoriamente a elementos individuales. Pero una lista simple es mucho más simple; Si no accede aleatoriamente a los objetos del juego, no necesita el Mapa. Y la búsqueda secuencial ocasional a través de unos pocos miles de entradas para encontrar la correcta no tomará mucho tiempo. Entonces, diría que, hagas lo que hagas, planea quedarte con la lista maestra. Considere usar un Mapa si se hace grande. (Aunque si puedes usar índices en lugar de claves, puedes quedarte con matrices y tener algo mucho mejor que un Mapa. Siempre terminé con grandes espacios entre los números de índice, así que tuve que renunciar).
Una palabra sobre las matrices: son increíblemente rápidas. Pero cuando se levantan alrededor de 100,000 elementos, comienzan a darles cabida a los recolectores de basura. Si puede asignar el espacio una vez y no tocarlo después, está bien. Pero si está expandiendo continuamente la matriz, está asignando y liberando continuamente grandes cantidades de memoria y es probable que su juego se cuelgue durante segundos e incluso falle con un error de memoria.
¿Tan rápido y más eficiente? Seguro. Un mapa para acceso aleatorio. Para listas realmente grandes, una lista vinculada superará a una matriz si no necesita acceso aleatorio. Múltiples mapas / listas para organizar los objetos de varias maneras, según los necesite. Podría realizar varios subprocesos de la actualización.
Pero todo es mucho trabajo. Ese conjunto único es rápido y simple. Puede agregar otras listas y cosas solo cuando sea necesario, y probablemente no las necesitará. Solo ten en cuenta qué puede salir mal si tu juego se hace grande. Es posible que desee tener una versión de prueba con 10 veces más objetos que en la versión real para darle una idea de cuándo se enfrentará a problemas. (Solo haga esto si su juego se está haciendo grande). (Y no intente el subproceso múltiple sin pensarlo mucho. Todo lo demás es fácil de comenzar y puede hacer todo lo que quiera o lo que quiera y retroceda en cualquier momento. Con los subprocesos múltiples, pagará un alto precio por entrar, las cuotas para quedarse son altas y es difícil volver a salir).
En otras palabras, sigue haciendo lo que estás haciendo. Su límite más grande es probablemente su propio tiempo, y está haciendo el mejor uso posible de eso.
fuente
He usado un método algo similar para juegos simples. Almacenar todo en una matriz es muy útil cuando se cumplen las siguientes condiciones:
En cualquier caso, implementé esta solución como una matriz de tamaño fijo que contiene una
gameObject_ptr
estructura. Esta estructura contenía un puntero al objeto del juego en sí y un valor booleano que representaba si el objeto estaba "vivo" o no.El propósito del booleano es acelerar las operaciones de creación y eliminación. En lugar de destruir los objetos del juego cuando se eliminan de la simulación, simplemente establecería la bandera "viva"
false
.Al realizar cálculos en objetos, el código recorrerá la matriz, realizando cálculos en objetos marcados como "vivos", y solo se representan los objetos marcados como "vivos".
Otra optimización simple es organizar la matriz por tipo de objeto. Todas las viñetas, por ejemplo, podrían vivir en las últimas n celdas de la matriz. Luego, al almacenar un int con el valor del índice o un puntero al elemento de matriz, se puede hacer referencia a tipos específicos a un costo reducido.
No hace falta decir que esta solución no es buena para juegos con grandes cantidades de datos, ya que la matriz será inaceptablemente grande. Tampoco es adecuado para juegos con restricciones de memoria que prohíben que los objetos no utilizados ocupen memoria. La conclusión es que si tiene un límite superior conocido en la cantidad de objetos del juego que tendrá en un momento dado, no hay nada de malo en almacenarlos en una matriz estática.
¡Esta es mi primera publicación! ¡Espero que responda tu pregunta!
fuente
Es aceptable, aunque inusual. Términos como "más rápido" y "más eficiente" son relativos; normalmente organizaría los objetos del juego en categorías separadas (una lista para las viñetas, una lista para los enemigos, etc.) para que la programación funcione más organizada (y, por lo tanto, más eficiente), pero no es como si la computadora recorriera todos los objetos más rápido .
fuente
Dices matriz única y objetos.
Entonces (sin su código para mirar), supongo que todos están relacionados con 'uberGameObject'.
Entonces quizás dos problemas.
1) Puede tener un objeto 'dios': hace toneladas de cosas, no segmentadas realmente por lo que hace o es.
2) ¿Recorre esta lista y emite para escribir? o desempaquetando cada uno. Esto tiene una gran penalización de rendimiento.
considere dividir diferentes tipos (viñetas, tipos malos, etc.) en sus propias listas fuertemente tipadas.
fuente
update()
).Puedo proponer un compromiso entre la lista única general y las listas separadas para cada tipo de objeto.
Mantenga referencia a un objeto en múltiples listas. Estas listas están definidas por comportamientos básicos. Por ejemplo,
MarineSoldier
se hará referencia en el objetoPhysicalObjList
,GraphicalObjList
,UserControlObjList
,HumanObjList
. Entonces, cuando procesas la física, iterasPhysicalObjList
, cuando el proceso es dañino por el venenoHumanObjList
, si el soldado muere, retíraloHumanObjList
pero siguePhysicalObjList
. Para que este mecanismo funcione, debe organizar la inserción / eliminación automática de objetos cuando se crea / elimina.Ventajas del enfoque:
AllObjectsList
con conversión al actualizar)MegaAirHumanUnderwaterObject
se insertarían en las listas correspondientes en lugar de crear una nueva lista para su tipo)PD: En cuanto a la actualización automática, encontrará problemas cuando interactúan varios objetos y cada uno de ellos depende de otros. Para resolver esto, necesitamos afectar el objeto externamente, no dentro de la actualización automática.
fuente