graphics - significa - traducir al español
Emular el parpadeo de los sprites de la vieja escuela(teoría y concepto) (2)
En primer lugar, me encanta la idea. Al hacer un juego retro, no sería lo mismo si ignoras las limitaciones de complejidad. Al escribir un marco que se aplica de alguna manera, es una gran idea.
¿Puedo decir que si resumiste el motor como un motor de gráficos de "Juego Retro" de código abierto, eso sería realmente genial y me aseguraría de unirme a él!
Con respecto a tus sprites de mayor prioridad, tal vez una cola de prioridad simplificaría esta parte, ya que puedes sacar los 64 sprites de mayor prioridad.
Con la ralentización, quizás en el motor podría tener un valor
limitValue
. Cada sprite como unlimitUse
apropiadoUso. Suma todos loslimitUse
vars y si está por debajo delimitValue
, no ralentiza. Ahora, paraslowDelay = f(limitUseTotal - limitValue)
donde f es una función que convierte el exceso de sprites en un valor de ralentización calculado. ¿Es esta la idea general que estás sugiriendo o he leído mal?
Intento desarrollar un videojuego de estilo NES antiguo, con parpadeos de sprites y desaceleración gráfica. He estado pensando en qué tipo de lógica debería usar para habilitar tales efectos.
Tengo que considerar las siguientes restricciones si quiero ir al estilo NES de la vieja escuela:
- No más de 64 sprites en la pantalla a la vez
- No más de 8 sprites por scanline, o para cada línea en el eje Y
- Si hay demasiada acción en la pantalla, el sistema congela la imagen de un marco para que el procesador se ponga al día con la acción.
Por lo que he leído, si hubiera más de 64 sprites en la pantalla, el desarrollador solo dibujaría sprites de alta prioridad sin tener en cuenta los de baja prioridad. También podrían alternar, dibujando cada sprite par en marcos opuestos de los impares.
El problema del scanline es interesante. A partir de mis pruebas, es imposible obtener una buena velocidad en el XBOX 360 XNA framework dibujando sprites píxel por píxel, como hizo el NES. Es por eso que en los juegos de la vieja escuela, si había demasiados sprites en una sola línea, algunos aparecerían si se redujeran a la mitad. Para todos los propósitos de este proyecto, estoy haciendo que las líneas de exploración tengan 8 píxeles de alto y que agrupen a los sprites por escaneo según su posición en Y.
Para aclarar, dibujaría sprites en la pantalla en lotes de 8x8 píxeles, no 1x1.
Entonces, atontado, necesito encontrar una solución que ...
- 64 sprites en pantalla a la vez
- 8 sprites por ''scanline''
- Puede dibujar sprites según la prioridad
- Puede alternar entre sprites por marco
- Emular la ralentización
Aquí está mi teoría actual
En primer lugar, una idea fundamental que surgió es abordar la prioridad de sprites. Asumiendo valores entre 0-255 (0 es bajo), puedo asignar niveles de prioridad de sprites, por ejemplo:
- 0 a 63 siendo bajo
- 63 a 127 siendo medianos
- 128 a 191 siendo alto
- 192 a 255 siendo máximo
Dentro de mis archivos de datos, puedo asignarle a cada sprite una cierta prioridad. Cuando se crea el objeto principal, se le asigna aleatoriamente un número entre su rango designado. Luego dibujaba los sprites en orden de mayor a menor, con el objetivo final de dibujar cada sprite.
Ahora, cuando un sprite se dibuja en un marco , lo generaría aleatoriamente un nuevo valor de prioridad dentro de su nivel de prioridad inicial. Sin embargo, si un sprite no se dibuja en un marco , podría agregar 32 a su prioridad actual. Por ejemplo, si el sistema solo puede dibujar sprites hasta un nivel de prioridad de 135, entonces se puede dibujar un sprite con una prioridad inicial de 45 después de que 3 fotogramas no se dibujen (45 + 32 + 32 + 32 = 141)
Esto, en teoría, permitiría a los sprites alternar cuadros, permitir niveles de prioridad y limitar sprites a 64 por pantalla.
Ahora, la pregunta interesante es ¿cómo limito los sprites a solo 8 por scanline?
Estoy pensando que si estoy clasificando los sprites de alta prioridad a baja prioridad, repite el ciclo hasta que haya dibujado 64 sprites. Sin embargo, no debería simplemente tomar los primeros 64 sprites en la lista.
Antes de dibujar cada sprite, pude verificar cuántos sprites se dibujaron en su respectiva línea de exploración a través de variables de contador. Por ejemplo:
- Los valores Y entre 0 y 7 pertenecen a Scanline 0, scanlineCount [0] = 0
- Los valores Y entre 8 y 15 pertenecen a Scanline 1, scanlineCount [1] = 0
- etc.
Podría restablecer los valores por línea de escaneo para cada fotograma dibujado. Mientras baja por la lista de sprites, agregue 1 al contador respectivo de la exploración si un sprite se dibuja en esa línea de exploración. Si es igual a 8, no dibuje ese sprite e ir al sprite con la siguiente prioridad más baja.
VE MÁS DESPACIO
Lo último que tengo que hacer es emular la ralentización. Mi idea inicial fue que si estoy dibujando 64 sprites por cuadro y todavía hay más sprites que necesitan ser dibujados, podría pausar el renderizado en 16ms más o menos. Sin embargo, en los juegos de NES que he jugado, a veces hay desaceleración si no hay ningún parpadeo de los sprites, mientras que el juego se mueve maravillosamente, incluso si hay algo de parpadeo de los sprites.
Quizás otorgue un valor a cada objeto que use sprites en la pantalla (como los valores de prioridad anteriores), y si los valores combinados de todos los objetos con sprites superan un umbral, ¿introduce desaceleración?
EN CONCLUSIÓN...
¿Todo lo que escribo suena legítimo y podría funcionar, o es un sueño imposible? ¿Qué mejoras pueden posiblemente pensar con esta teoría de programación de juegos?
Para prioridad, solo use el índice Z. Eso es lo que hace el GBA, al menos; la prioridad 0 se representa de frente, por lo tanto, tiene la prioridad más débil. Por lo tanto, renderice de alto a bajo, y si ha generado demasiados sprites, deténgase.
La prioridad de un sprite está determinada por su índice en la tabla de sprites. El GBA tenía 128 entradas [0,127] dispuestas como cuatro palabras consecutivas (16 bytes cada una). Para XNA, probablemente use una List<Sprite>
o similar en su lugar. Tal vez un Sprite[256]
para simplificar un poco las cosas.
Limitar la representación del sprite cae naturalmente de este mecanismo. Dado que renderiza de 255 a 0, puede realizar un seguimiento de las líneas de exploración que ha tocado. Así que básicamente:
int[] numPixelsDrawnPerScanline = new int[Graphics.ScreenHeight];
foreach(Sprite sprite in SpriteTable.Reverse()) {
int top = Math.Max(0, sprite.Y);
int bottom = Math.Min(Graphics.ScreenHeight, sprite.Y + sprite.Height);
// Could be rewritten to store number of pixels to draw per line, starting from the left.
bool[] shouldDrawOnScanline = new bool[Graphics.ScreenHeight];
for(int y = top; y < bottom; ++y) {
bool shouldDraw = numPixelsDrawnPerScanline[y] + sprite.Width <= PixelsPerScanlineThreshold;
shouldDrawOnScanline[y] = shouldDraw;
if(shouldDraw) {
numPixelsDrawnPerScanline[y] += sprite.Width;
}
}
Graphics.Draw(sprite, shouldDrawOnScanline); // shouldDrawOnScanline defines clipping
skipDrawingSprite:
}
No estoy muy seguro de lo que quiere decir con la desaceleración, por lo que no puedo responder a esa parte de su pregunta. Lo siento.
Espero que esto ayude.