Pregunta
Cuando tienes un juego en 2D que usa imágenes realmente grandes (más grandes de lo que admite tu hardware), ¿qué haces? Quizás haya alguna solución interesante a este problema que nunca antes me haya ocurrido.
Básicamente estoy trabajando en una especie de creador de juegos de aventura gráfica. Mi público objetivo son personas con poca o ninguna experiencia en desarrollo de juegos (y programación). Si estuvieras trabajando con una aplicación de este tipo, ¿no preferirías que maneje el problema internamente en lugar de decirte que "dividas tus imágenes"?
Contexto
Es bien sabido que las tarjetas gráficas imponen un conjunto de restricciones en el tamaño de las texturas que pueden usar. Por ejemplo, cuando trabajas con XNA estás restringido a un tamaño de textura máximo de 2048 o 4096 píxeles dependiendo del perfil que elijas.
Por lo general, esto no representa un gran problema en los juegos 2D porque la mayoría de ellos tienen niveles que se pueden construir a partir de piezas individuales más pequeñas (por ejemplo, fichas o sprites transformados).
Pero piense en algunos juegos clásicos de aventura gráfica point'n'click (por ejemplo, Monkey Island). Las salas de los juegos de aventuras gráficas generalmente están diseñadas en su conjunto, con secciones pequeñas o nulas repetibles, como un pintor que trabaja en un paisaje. O tal vez un juego como Final Fantasy 7 que utiliza grandes fondos pre-renderizados.
Algunas de estas habitaciones pueden ser bastante grandes, con frecuencia abarcando tres o más pantallas de ancho. Ahora, si consideramos estos fondos en el contexto de un juego moderno de alta definición, superarán fácilmente el tamaño máximo de textura admitido.
Ahora, la solución obvia para esto es dividir el fondo en secciones más pequeñas que se ajusten a los requisitos y dibujarlas una al lado de la otra. Pero, ¿debería usted (o su artista) ser el que divide todas sus texturas?
Si está trabajando con una API de alto nivel, ¿no debería estar preparado para lidiar con esta situación y dividir y ensamblar las texturas internamente (ya sea como un proceso previo como el Canal de contenido de XNA o en tiempo de ejecución)?
Editar
Esto es lo que estaba pensando, desde el punto de vista de la implementación de XNA.
Mi motor 2D solo requiere un subconjunto muy pequeño de la funcionalidad de Texture2D. De hecho, puedo reducirlo a esta interfaz:
public interface ITexture
{
int Height { get; }
int Width { get; }
void Draw(SpriteBatch spriteBatch, Vector2 position, Rectangle? source, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth);
}
El único método externo que uso que se basa en Texture2D es SpriteBatch.Draw () para poder crear un puente entre ellos agregando un método de extensión:
public static void Draw(this SpriteBatch spriteBatch, ITexture texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
{
texture.Draw(spriteBatch, position, sourceRectangle, color, rotation, origin, scale, effects, layerDepth);
}
Esto me permitiría usar una ITexture de manera intercambiable cada vez que use una Texture2D antes.
Entonces podría crear dos implementaciones diferentes de ITexture, por ejemplo, RegularTexture y LargeTexture, donde RegularTexture simplemente envolvería un Texture2D normal.
Por otro lado, LargeTexture mantendría una Lista de Texture2D cada una correspondiente a una de las piezas divididas de la imagen completa. La parte más importante es que llamar a Draw () en LargeTexture debería poder aplicar todas las transformaciones y dibujar todas las piezas como si fueran solo una imagen contigua.
Finalmente, para hacer que todo el proceso sea transparente, crearía una clase de cargador de texturas unificado que actuaría como un Método de Fábrica. Tomaría la ruta de la textura como parámetro y devolvería una ITexture que sería una RegularTexture o una LargeTexture, dependiendo de que el tamaño de la imagen exceda el límite o no. Pero el usuario no necesitaba saber cuál era.
¿Un poco exagerado o suena bien?
Respuestas:
Creo que estás en el camino correcto. Tienes que dividir las imágenes en piezas más pequeñas. Como creas un creador de juegos, no puedes usar la canalización de contenido de XNA. A menos que su creador sea más como un motor que aún requerirá que los desarrolladores usen Visual Studio. Por lo tanto, debe crear sus propias rutinas que harán la división. Incluso debería considerar transmitir las piezas un poco antes de que se vuelvan visibles para que aún no ponga límites a la cantidad de memoria de video que deberían tener los reproductores.
Hay algunos trucos que puedes hacer específicamente para juegos de aventura. Me imagino que, como todos los juegos de aventura clásicos, tendrás pocas capas de estas texturas para que ... tu personaje pueda caminar detrás del árbol o del edificio ...
Si desea que estas capas tengan un efecto de paralaje, cuando divida sus mosaicos, omita todos los translúcidos una vez. Aquí debes pensar en el tamaño de los mosaicos. Los mosaicos más pequeños agregarán gastos generales de gestión y llamadas de extracción, pero serán menos datos donde los mosaicos más grandes serán más amigables con la GPU pero tendrán mucha translucidez.
Si no tiene un efecto de paralaje, entonces, en lugar de cubrir toda la escena con 2-4 imágenes gigantes que tienen una profundidad de 32 bits, puede crear solo una profundidad de 32 bits. Y puede tener una capa de plantilla o profundidad que será de solo 8 bits por píxel.
Sé que esto suena difícil de administrar por parte de desarrolladores que no son desarrolladores de juegos, pero en realidad no tiene que ser así. Por ejemplo, si tiene una calle con 3 árboles y el jugador va detrás de 2 de ellos y frente al tercer árbol y el resto del fondo ... Simplemente haga el diseño de la escena como desee en su editor y luego en un paso a paso de su creador de juegos, puede hornear estas imágenes gigantes (bueno, pequeños mosaicos de una imagen grande).
Sobre cómo lo hacían los juegos clásicos. No estoy seguro de que probablemente hicieran trucos similares, pero debes recordar que en el momento en que la pantalla era de 320x240, los juegos tenían 16 colores que, al usar texturas palattelizadas, te permiten codificar 2 píxeles en un solo byte. Pero las arquitecturas actuales no funcionan así: D
Buena suerte
fuente
Córtelos para que ya no sean arbitrariamente grandes, como usted dice. No hay magia Esos viejos juegos 2D lo hicieron o se renderizaron en software, en cuyo caso no hubo restricciones de hardware más allá del tamaño de RAM. Es de destacar que muchos de esos juegos 2D realmente no tenían grandes fondos, simplemente se desplazaban lentamente para que se sintieran enormes.
Lo que está buscando hacer en su creador de juegos de aventuras gráficas es preprocesar esas imágenes grandes durante algún tipo de paso de "compilación" o "integración" antes de que el juego esté empaquetado para ejecutarse.
Si desea utilizar una API y una biblioteca existentes en lugar de escribir la suya, le sugiero que convierta esas imágenes grandes en mosaicos durante esa "compilación" y utilice un motor de mosaico 2D, del cual hay muchos.
Si desea utilizar sus propias técnicas 3D, es posible que quiera dividirse en mosaicos y usar una API 2D bien conocida y bien diseñada para escribir su propia biblioteca 3D para eso, de esta manera, al menos, está garantizado que al menos comenzará con un buen diseño de API y menos diseño requerido de su parte.
fuente
Si está familiarizado con el aspecto de un quadtree en la práctica visual, he utilizado un método que corta imágenes detalladas grandes en imágenes más pequeñas, relativamente similares a cómo se ve una prueba / visualización de un quadtree. Sin embargo, los míos se hicieron de esa manera para cortar áreas específicamente que podrían codificarse o comprimirse de manera diferente según el color. Podría usar este método y hacer lo que describí, ya que también es útil.
En tiempo de ejecución, cortarlos requeriría más memoria temporalmente y reduciría el tiempo de carga. Sin embargo, diría que depende de si te importa más el almacenamiento físico o el tiempo de carga de un juego de usuarios hecho con tu editor.
Tiempo de carga / ejecución: la forma en que haría la carga es simplemente cortarlo en trozos de tamaño proporcional. Tenga en cuenta un píxel en la pieza más correcta si es un número impar de píxeles, a nadie le gusta que se corten sus imágenes, especialmente a los usuarios sin experiencia que tal vez no sepan que no depende totalmente de ellos. Haga que tengan la mitad del ancho O la mitad de la altura (o 3rds, 4tos, lo que sea), la razón por la que no está en cuadrados o 4 secciones (2 filas 2 columnas) es porque reduciría la memoria de mosaico ya que no se preocuparía por x e y al mismo tiempo (y dependiendo de cuántas imágenes sean tan grandes, podría ser bastante importante)
Antes de la mano / Automáticamente: en este caso, me gusta la idea de darle al usuario la opción de almacenarlo en una imagen o almacenarlo como piezas separadas (una imagen debería ser la predeterminada). Almacenar la imagen como un .gif funcionaría muy bien. La imagen estaría en un archivo, y en lugar de que cada cuadro sea una animación, sería más como un archivo comprimido de la misma imagen (que es esencialmente lo que es un .gif de todos modos). Permitiría la facilidad del transporte de archivos físicos, la facilidad de carga:
Ah, y si usa el método .gif, debe cambiar la extensión del archivo a otra cosa (.gif -> .kittens) en aras de una menor confusión cuando su .gif se anima como si fuera un archivo roto. (no se preocupe por la legalidad de eso, .gif es un formato abierto)
fuente
Esto va a sonar obvio, pero ¿por qué no dividir tu habitación en pedazos más pequeños? Elija un tamaño arbitrario que no choque con las tarjetas gráficas (como 1024x1024, o tal vez más grande) y simplemente divida sus habitaciones en varias piezas.
Sé que esto evita el problema y, sinceramente, este es un problema muy interesante de resolver. De todos modos, esta es una solución trivial, que puede funcionar algunas veces para usted.
fuente