Estoy tratando de crear un motor de juego flexible basado en fichas para hacer todo tipo de juegos de rompecabezas que no sean en tiempo real, como Bejeweled, Civilization, Sokoban, etc.
El primer enfoque que tuve fue tener una matriz 2D de objetos Tile, y luego tener clases heredadas de Tile que representaran los objetos del juego. Desafortunadamente de esa manera no pude apilar más elementos del juego en el mismo mosaico sin tener una matriz 3D.
Luego hice algo diferente: todavía tenía la matriz 2D de objetos Tile, pero cada objeto Tile contenía una Lista donde puse y diferentes entidades. Esto funcionó bien hasta hace 20 minutos, cuando me di cuenta de que es demasiado costoso hacer muchas cosas, mira este ejemplo:
Tengo una entidad de pared. Cada actualización tengo que verificar los 8 mosaicos adyacentes, luego verificar todas las entidades en la Lista de mosaicos, verificar si alguna de esas entidades es un Muro, y finalmente dibujar el sprite correcto. (Esto se hace para dibujar muros que están uno al lado del otro sin problemas)
La única solución que veo ahora es tener una matriz 3D, con muchas capas, que pueda adaptarse a cada situación. Pero de esa manera no puedo apilar dos entidades que comparten la misma capa en el mismo mosaico. Cada vez que quiero hacer eso tengo que crear una nueva capa.
¿Hay una mejor solución? ¿Qué harías?
fuente
Respuestas:
¿Realmente ve este problema, o ha pensado simplemente para arriba? Porque no veo cómo iterar sobre la lista de objetos tiene un efecto notable en el rendimiento. Tendría que tener cientos de objetos por ficha para que esto sea importante. La mayoría de los juegos que se me ocurren tienen 10, como máximo.
Si realmente tiene un problema, la mejor idea es almacenar en caché los datos, no cambiar la representación. En su ejemplo, la configuración de la pared probablemente no cambia cada cuadro. Simplemente guarde en caché el tipo de sprite correcto, no lo recalcule constantemente.
Si quieres crear un juego loco que tenga muchos objetos y cambien todo el tiempo, puedes crear diferentes capas con diferentes semánticas. Por ejemplo, la capa de pared contiene solo paredes, no más de 1 por mosaico y, por ejemplo, la capa de decoración contiene listas de objetos que nunca cambian.
Finalmente, si quieres tener una arquitectura ultra flexible que sea ideal para todos los juegos posibles, mala suerte, no existe tal cosa.
fuente
Dos sugerencias Primero, debes resolver el sprite con el que se dibujará cada ficha durante la carga de tu mapa / nivel de ficha. Debería ser bastante fácil atravesar la matriz 2D en la que está cargado su nivel y decidir que un muro certian debe tener forma de L y otro debe ser un | forma.
En segundo lugar, almacenaría datos estáticos relacionados con un mosaico en un lugar diferente al de los datos activos. Por lo tanto, un objeto de mosaico puede tener una lista de objetos que van y vienen del mosaico, pero no es necesario recorrerlo para ver si, por ejemplo, solo queremos saber si el mosaico es transitable o no.
Este psudocódigo es más o menos cómo me he acercado a un sistema basado en mosaicos.
Esto supone que hay fichas estáticas en su juego, pero esa es una suposición bastante buena en muchos juegos basados en fichas en las que se trata de paredes y similares.
fuente
Tu dijiste:
Esta es exactamente la situación que desea resolver utilizando el patrón MVC (controlador de vista de modelo) para separar su modelo de su vista. MVC hace que este problema tenga dos partes:
En lugar de tener una matriz 2D de mosaicos, o una matriz 2D de una lista de mosaicos, su modelo almacenaría una matriz 2D de objetos de juego. Luego, tu matriz 2D de clases de mosaico mirará los objetos de juego correspondientes y decidirá cómo representarlos.
En lugar de tener una sola instancia de Tile que represente un solo objeto de juego, puede hacer que una única instancia de Tile represente algo que se parece a una pila de objetos. Esto sería más eficiente y le daría una separación clara de su lógica de código central y cómo se ve.
Al igual que las paredes. La lógica es muy trivial, pero la representación es más compleja.
fuente
Estoy de acuerdo con @Omnion. Use el enfoque de lista, pero manténgalo ordenado si el rendimiento es un problema. Es decir, usar un enfoque híbrido. Use la lista como su tercera dimensión, pero solo identifique los primeros 5 elementos como un tipo específico, y cualquier cosa después de eso es un tipo propio de tinta, o un tipo que no requerirá verificar varias veces por cuadro.
fuente
"Nunca" debe hacer clases separadas para cosas como fichas. Debe hacer una estructura a partir de bytes, cortos e ints que se refieren al tipo de mosaico y objetos en él. Los ints de rendimiento inteligente son los mejores para usar o incluso largos en sistemas de 64 bits. Pero si alguna vez desea guardar su mapa, debe ir con bytes o cortos siempre que pueda, especialmente para mapas enormes.
En mis juegos tengo una clase de mapa que solo tiene campos como: byte tileType; 256 tipos de terreno diferentes son suficientes para este juego. Toma 1 byte. ushort tileObject; 65,536 objetos diferentes es suficiente para ir. Toma 2 bytes, etc.
Si tomo el ejemplo anterior, cada mosaico ocupa solo 3 bytes en la memoria. Por lo tanto, un 10000x10000 tendría 300 MB de memoria y no debería ser un problema. Cada vez que necesite buscar algo, determinará en qué parte del mapa desea buscar y lo que está buscando e iterará las matrices en consecuencia.
Si tienes cosas dinámicas en todo el mapa, pregúntate si el jugador realmente nota esas cosas lejos de su ventana gráfica.
fuente