Gráficos 2D: ¿por qué usar spritesheets?

62

He visto muchos ejemplos de cómo renderizar sprites desde una hoja de sprites, pero no he comprendido por qué es la forma más común de tratar con sprites en juegos 2D.

Comencé con la representación de sprites 2D en las pocas aplicaciones de demostración que hice al tratar cada cuadro de animación para cualquier tipo de sprite dado como su propia textura, y esta colección de texturas se almacena en un diccionario. Esto parece funcionar para mí, y se adapta bastante bien a mi flujo de trabajo, ya que tiendo a hacer mis animaciones como archivos gif / mng y luego extraigo los marcos a png individuales.

¿Existe una notable ventaja de rendimiento al renderizar desde una sola hoja en lugar de texturas individuales? Con un hardware moderno que es capaz de dibujar millones de polígonos en la pantalla cien veces por segundo, ¿importa incluso para mis juegos 2D que solo se ocupan de unas pocas docenas de rectángulos de 50x100px?

Los detalles de implementación de cargar una textura en la memoria de gráficos y mostrarla en XNA parecen bastante abstractos. Todo lo que sé es que las texturas están unidas al dispositivo gráfico cuando se cargan, luego, durante el ciclo del juego, las texturas se procesan en lotes. Entonces no me queda claro si mi elección afecta el rendimiento.

Sospecho que hay algunas razones muy buenas por las que la mayoría de los desarrolladores de juegos 2D parecen estar usándolas, pero no entiendo por qué.

Columbo
fuente
No, en realidad no importa demasiado para tus 50 sprites impares. ¿Pero qué perder?
El pato comunista el

Respuestas:

69

Un argumento fuerte para usar hojas de sprites es que el número de texturas disponibles en una tarjeta gráfica puede ser limitado. Por lo tanto, su biblioteca de gráficos tendría que eliminar constantemente la textura y reasignar texturas en la GPU. Es mucho más eficiente simplemente asignar una textura grande una vez.

También tenga en cuenta que los tamaños de las texturas suelen tener una potencia de 2. Por lo tanto, si tiene un Sprite de 50x100px, asignará texturas con un tamaño de 64x128px o, en el peor de los casos, 128x128px. Eso solo está desperdiciando memoria gráfica. Es mejor empacar todos los sprites en una textura de 1024x1024px, lo que permitiría 20x10 sprites y solo perderá 24 píxeles horizontal y verticalmente. A veces, incluso los sprites de diferentes tamaños se combinan en una gran hoja de sprites para usar la textura de la manera más eficiente posible.

Anexo: Una razón muy importante para usar las hojas de sprites es reducir la cantidad de llamadas de extracción en su GPU, lo que puede tener un impacto notable en el rendimiento. Esto se ha indicado en otras respuestas y estoy agregando esto en aras de la integridad para que esto tenga más visibilidad.

bummzack
fuente
1
Gracias, no había considerado ninguno de esos puntos. ¿Sabes aproximadamente cuál es el límite en la cantidad de texturas disponibles? Nunca he visto esta estadística mencionada en las especificaciones de gráficos: ¿el límite es básicamente igual a la cantidad de RAM de gráficos?
Columbo
Por lo general, el límite sería la memoria de la GPU, sí. Pensé que había algunas restricciones en el número de texturas en las tarjetas más antiguas, pero parece que no puedo encontrar una referencia que pruebe ese punto.
bummzack
12
Incluso si no hay un límite en la cantidad, el tráfico de autobuses para mover muchas pequeñas imágenes no es tan eficiente como una (o algunas) grandes transferencias.
Mordachai
55
Buenos puntos, pero el aspecto más importante es minimizar las paradas de la tubería al mantener la cuenta regresiva del sorteo. Las GPU prefieren una pequeña cantidad de trabajos grandes, en lugar de una gran cantidad de trabajos pequeños. Poner todos sus sprites en una pequeña cantidad de hojas grandes significa que puede configurar la etapa de texto una vez y dibujar muchos spites a la vez en un lote. Spritebatch hace esto por ti. Si tiene más de una hoja de sprites, asegúrese de decirle a spritebatch que clasifique por textura
Cubed2D
Agregar compresión de textura convertiría cualquier espacio flojo en una pequeña cantidad muy pequeña de sobrecarga, además de todo eso
Joe Plante
36

Diría que el argumento a utilizar sería la capacidad de representar varias cosas en una sola llamada de sorteo. Tomemos, por ejemplo, la representación de la fuente, sin una hoja de sprites necesitaría representar cada carácter con un intercambio de textura separado seguido de una llamada de dibujo. Tírelos en una hoja de sprites y puede renderizar oraciones completas con una sola llamada de dibujo (los caracteres de diferencia en la fuente se seleccionan simplemente especificando los diferentes UV para las esquinas). Esta es una forma mucho más eficiente de renderizar cuando una sobrecarga muy real de renderizar cosas en muchas plataformas es hacer demasiadas llamadas API.

También puede ayudar a ahorrar espacio, pero depende de lo que empaques en la hoja de sprites en primer lugar.

Roger Perkins
fuente
2
+1 Toca en llamadas de sorteo. (algo que no vi en la respuesta aceptada)
Michael Coleman
11

Cada llamada de sorteo tiene una cierta cantidad de gastos generales. Mediante el uso de hojas de sprites puede agrupar el dibujo de cosas que no están usando el mismo cuadro de una animación (o más generalmente, todo lo que está en el mismo material) mejorando en gran medida el rendimiento. Esto puede no importar demasiado para las PC modernas dependiendo de su juego, pero definitivamente importa en, por ejemplo, el iPhone.

Tétrada
fuente
2
Definitivamente todavía importa. El hardware de gráficos se ha optimizado para impulsar toneladas de polígonos para una pequeña cantidad de modelos detallados (por ejemplo, personajes) porque la mayoría de los juegos tienden en esa dirección. El resultado es que necesita empujar tantos polígonos como sea posible en cada una de las docenas de llamadas de sorteo. Los motores de juego que representan escenas grandes como las ciudades a menudo combinan múltiples texturas en una sobre la marcha para reducir las llamadas de extracción.
jhocking
Definitivamente, me pregunto si la asignación de texturas u, v en una gran cantidad de hardware 3D, más las operaciones de bits de bits se benefician de la hoja de sprites.
Joe Plante
7

Además de las consideraciones de rendimiento, las hojas de sprites pueden ser útiles al crear el arte; cada personaje puede estar en su propia hoja, y puede ver todos los cuadros simplemente desplazándose. Me ayuda a mantener una apariencia consistente en todos los cuadros.

Además, codifico en AS3, y cada archivo de imagen requiere una instrucción de inserción separada. Prefiero tener una sola incrustación de una hoja de sprites completa que 24 incrustaciones para todos los marcos, incluso si estoy usando una clase de recurso separada.

Gregory Avery-Weir
fuente
1
+1. Aunque el OP no está utilizando Flash, la incrustación o carga también es un punto a favor de las hojas de sprites. También es relevante cuando los activos se cargan desde un servidor web donde se prefiere 1 solicitud sobre varios cientos de solicitudes (para cada "marco").
bummzack
Definitivamente desea reducir las solicitudes del servidor para un juego en línea. Consideramos si es mejor poner las imágenes en un .swf o una hoja de sprites porque ambas cumplen este objetivo principal (eventualmente decidimos que las hojas de sprites requieren menos mano de obra para nuestros artistas).
jhocking
7

Otra razón para usar las hojas de sprites con XNA es que con el desarrollo de Xbox360 hay un problema conocido con tiempos de implementación / carga lentos en relación con la cantidad de archivos que tiene en su proyecto. Por lo tanto, combinar muchos archivos de imagen pequeños en un solo archivo de imagen ayudará a combatir ese problema.

George Clingerman
fuente
5

No voy a mencionar la memoria / GPU aquí, ya que hay suficientes respuestas como esa.

En cambio, puede aclarar su arte mientras trabaja en él, y después. En lugar de tener que pasar 10 imágenes para ver el ciclo de caminata, puede ver todos los cuadros de una vez. Si desea distribuir esto, solo tiene 1 archivo para tratar en lugar de 10.

Y luego también es más limpio (desde el punto de vista de la organización) tener character.png y enemy.png sobre characterwalk01 hasta characterwalk10, luego characterattack01 a characterattack05, y continúa.

El pato comunista
fuente
4

La memoria de gráficos no tiene una administración de memoria inflada como los sistemas de archivos en discos; lo contrario es el caso: se mantiene simple y rápido .

Una gestión de memoria tan simple y rápida podría ser como tener un solo sprite gigante 4096x4096 que se corta en múltiples sprites más pequeños al reducir a la mitad su ancho y / o altura. (Existe una técnica de administración de memoria unidimensional similar, pero olvidé el nombre). Eventualmente, se debe realizar la desfragmentación.

Para omitir dicha gestión de memoria en tiempo de ejecución , los desarrolladores llenan sprites grandes individuales con múltiples sprites más pequeños automáticamente en tiempo de compilación o incluso durante el proceso de diseño de los gráficos.

comonad
fuente
3

Además, no olvide las operaciones de E / S que podría salvarlo durante la inicialización. Si está cargando desde una unidad de CD, cada archivo es una llamada de E / S adicional y puede tomar un tiempo para buscar (los discos duros modernos son de aproximadamente 15 ms por archivo, ¿los CD pueden ser de 50-100 ms?). Esto puede ahorrarle mucho tiempo de inicialización ya que solo se debe recuperar un archivo. También permite que su sistema operativo almacene en caché estas operaciones de E / S, en caso de que su aplicación esté haciendo otra cosa (como esperar a la GPU).

Calificación
fuente
1
En el otro extremo, si desea resolver el problema de búsqueda de archivos, puede tener todos sus sprites empaquetados en un solo archivo, utilizando un formato personalizado. La mayoría de los juegos hacen eso.
tigrou
0

Además de ser más eficiente en el rendimiento, lo encuentro simplemente más fácil. Se requiere un poco de trabajo extra para asegurarse de que sus imágenes siempre estén dentro de sus límites al crear el arte, pero es mucho más fácil poder ver todas las imágenes con las que está lidiando en este momento.

También es más fácil de codificar, en mi opinión. Simplemente tengo una serie de objetos como:

sheet = {
    img: (the actual image),
    rects: [
        {x:0, y:0, w:32, h:32},
        {x:32, y:0, w:32, h:32},
        {x:64, y:0, w:32, h:32}
    ]
};

sheets.push(sheet);

Luego, para cada elemento, le doy dos valores, hoja y rect. La hoja sería el índice en la matriz de objetos, y el rect sería el índice en la matriz de rectos de esa hoja.

Bridgerrholt
fuente