Al cargar mapas extra grandes, el método de carga elimina la excepción de memoria donde se crea una nueva instancia de mosaico de mapa. Me gustaría tener un mapa completo procesado al menos en la aplicación del servidor (y en el cliente si es posible). ¿Cómo debo resolver este problema?
UPD: la pregunta aquí es cómo hacer que el juego deje de fallar cuando todavía hay memoria libre para usar. En cuanto a dividir el mapa en trozos, es un buen enfoque, pero no es lo que quiero en mi caso.
UPD2: Al principio fui y asigné una textura a cada nueva instancia de clase de mosaico y eso es lo que tomó tanta memoria (también tiempo de carga). Ahora toma alrededor de cuatro veces menos espacio. Gracias a todos, ahora puedo ejecutar mapas enormes sin pensar en dividirlos en pedazos todavía.
UPD3: Después de reescribir el código para ver si las matrices de propiedades de mosaico funcionan más rápido o consumen menos memoria (que dichas propiedades en sus respectivos mosaicos como propiedades de objeto), descubrí que no solo me llevó mucho tiempo intentarlo, sino que no trajo ninguna mejora en el rendimiento e hizo que el juego fuera terriblemente difícil de depurar.
fuente
Respuestas:
Esto sugiere fuertemente que no está representando sus mosaicos correctamente, ya que esto significaría que cada mosaico tiene un tamaño de ~ 80 bytes.
Lo que debes entender es que debe haber una separación entre el concepto de juego de un mosaico y el mosaico visual que ve el usuario. Estos dos conceptos no son lo mismo .
Toma Terraria por ejemplo. El mundo más pequeño de Terraria ocupa 4200x1200 fichas, que son 5 millones de fichas. Ahora, ¿cuánto recuerdo se necesita para representar ese mundo?
Bueno, cada mosaico tiene una capa de primer plano, una capa de fondo (las paredes de fondo), una "capa de alambre" donde van los cables y una "capa de muebles" donde van los muebles. ¿Cuánta memoria ocupa cada mosaico? Nuevamente, solo estamos hablando conceptualmente, no visualmente.
Un mosaico en primer plano podría almacenarse fácilmente en un corto sin firmar. No hay más de 65536 tipos de mosaicos en primer plano, por lo que no tiene sentido usar más memoria que esta. Los mosaicos de fondo podrían estar fácilmente en un byte sin signo, ya que hay menos de 256 tipos diferentes de mosaicos de fondo. La capa de cable es puramente binaria: o un mosaico tiene un cable o no. Eso es un bit por ficha. Y la capa de muebles podría volver a ser un byte sin firmar, dependiendo de la cantidad de muebles posibles que haya.
Tamaño de memoria total por mosaico: 2 bytes + 1 byte + 1 bit + 1 byte: 4 bytes + 1 bit. Por lo tanto, el tamaño total para un pequeño mapa de Terraria es 20790000 bytes, o ~ 20 MB. (nota: estos cálculos se basan en Terraria 1.1. El juego se ha expandido mucho desde entonces, pero incluso los Terraria modernos podrían caber dentro de 8 bytes por ubicación de mosaico, o ~ 40 MB. Todavía es bastante tolerable).
Usted debe no tener esta representación almacenada como matrices de clases de C #. Deben ser matrices de enteros o algo similar. AC # struct también funcionaría.
Ahora, cuando llega el momento de dibujar parte de un mapa (tenga en cuenta el énfasis), Terraria necesita convertir estos mosaicos conceptuales en mosaicos reales . Cada mosaico debe elegir una imagen de primer plano, una imagen de fondo, una imagen de muebles opcional y una imagen de alambre. Aquí es donde entra XNA con sus diversas hojas de sprites y demás.
Lo que debe hacer es convertir la parte visible de su mapa conceptual en mosaicos de hojas de sprites XNA reales. No deberías intentar convertir todo de una vez . Cada mosaico que almacene debería ser un índice que diga "Soy un mosaico tipo X", donde X es un número entero. Utiliza ese índice entero para buscar qué sprite usa para mostrarlo. Y usa las hojas de sprites de XNA para hacer esto más rápido que solo dibujar quads individuales.
Ahora, la región visible de los mosaicos debe dividirse en varios trozos, de modo que no esté constantemente construyendo láminas de sprites cuando la cámara se mueva. Por lo tanto, es posible que tenga trozos de 64x64 del mundo como hojas de sprites. Cualesquiera trozos de 64x64 del mundo visibles desde la posición actual de la cámara del jugador son los trozos que dibujas. Cualquier otro trozo ni siquiera tiene hojas de sprites; si un trozo se cae de la pantalla, tira esa hoja (nota: en realidad no la borras; la guardas y la respetas para un nuevo trozo que pueda ser visible más adelante).
Su servidor no necesita saber ni preocuparse por la representación visual de los mosaicos. Todo lo que necesita preocuparse es la representación conceptual. El usuario agrega un mosaico aquí, por lo que cambia ese índice de mosaico.
fuente
Divide el terreno en regiones o trozos. Luego solo carga los fragmentos que son visibles para los jugadores y descarga los que no lo son. Puedes pensarlo como una cinta transportadora, donde estás cargando trozos en un extremo y descargándolos en el otro a medida que el jugador avanza. Siempre por delante del jugador.
También puedes usar trucos como la creación de instancias. Donde si todos los mosaicos de un tipo solo tienen una instancia en la memoria y se dibujan varias veces en todas las ubicaciones donde se necesita.
Puedes encontrar más ideas como esta aquí:
Cómo lo hicieron: millones de azulejos en Terraria
Zonificación de áreas en un mapa de mosaico grande, así como mazmorras
fuente
La respuesta de Byte56 es buena, y comencé a escribir esto como un comentario a eso, pero se hizo demasiado largo y contiene algunos consejos que podrían ser más útiles en una respuesta.
El enfoque es absolutamente tan bueno para el servidor como para un cliente. De hecho, probablemente sea más apropiado, dado que se le pedirá al servidor que se preocupe por muchas más áreas que un cliente. El servidor tiene una idea diferente acerca de lo que debe cargarse de lo que lo haría un cliente (se preocupa por todas las regiones que les interesan a todos los clientes conectados), pero es igualmente capaz de administrar un conjunto de regiones en funcionamiento.
Incluso si pudieras colocar un mapa tan gigantesco (y probablemente no puedas) en la memoria, no quieres . No hay absolutamente ningún punto en cargar datos que no tiene que usar de inmediato, es ineficiente y lento. Incluso si pudiera guardarlo en la memoria, lo más probable es que no pueda procesarlo todo en un período de tiempo razonable.
Sospecho que su objeción a que se descarguen ciertas regiones es porque su actualización mundial está iterando sobre todos los mosaicos y procesándolos. Eso es ciertamente válido, pero no impide tener solo ciertas partes cargadas. En cambio, cuando se carga una región, aplique las actualizaciones que se perdieron para actualizarla. Eso también es mucho más eficiente en cuanto a procesamiento (concentrando un gran esfuerzo en un área pequeña de memoria). Si hay algún efecto de procesamiento que pueda cruzar los límites de la región (por ejemplo, un flujo de lava o un flujo de agua que se trasladará a otra región), entonces une esas dos regiones para que cuando una esté cargada, también lo haga la otra. Idealmente, esas situaciones se minimizarían, de lo contrario, se encontrará rápidamente de regreso en el caso 'todas las regiones deben cargarse en todo momento'.
fuente
Una forma eficiente que he usado en un juego XNA fue:
También podría usar un solo mapa grande de esta manera si no es TAN grande y usar la lógica presentada.
Por supuesto, el tamaño de sus texturas debe ser equilibrado y la resolución paga un gran precio en esto. Intente trabajar a una resolución más baja y, si lo necesita, haga un paquete de alta resolución para cargar si la configuración está configurada en.
Tendrá que recorrer solo las partes relevantes de este mapa cada vez y renderizar solo lo que se necesita. De esta manera mejorarás mucho el rendimiento.
Un servidor puede procesar un mapa enorme más rápido porque no tiene que renderizar (una de las operaciones más lentas) el juego, por lo que la lógica puede ser el uso de fragmentos más grandes o incluso todo el mapa, pero solo procesa la lógica alrededor de los jugadores, como en un área de visualización 2 veces superior a la del jugador.
Un consejo aquí es que de ninguna manera soy un experto en desarrollo de juegos y mi juego fue hecho para el proyecto final de mi graduación (que obtuve el mejor puntaje), por lo que puede que no sea tan correcto en todos los puntos, pero he investigé la web y sitios como http://www.gamasutra.com y el sitio de creadores de XNA creators.xna.com (en este momento http://create.msdn.com ) para reunir conocimientos y habilidades y funcionó bien para mí .
fuente
Ya sea
fuente
texture2d
? ¿Cada mosaico tiene uno único o comparten texturas? Además, ¿ dónde has dicho esto? Ciertamente no lo pusiste en tu pregunta, que es donde debería estar la información. Explica mejor tus circunstancias específicas, por favor.En respuesta a su actualización, no puede asignar matrices de mayor
Int32.MaxValue
tamaño. Tienes que dividirlo en trozos. Incluso podría encapsularlo en una clase de contenedor que expone una fachada tipo matriz:fuente