2010-06-05 18 views
20

Estoy tratando de desarrollar un videojuego de estilo NES antiguo, con parpadeo de sprites y desaceleración gráfica. He estado pensando en qué tipo de lógica debería usar para habilitar tales efectos.Emulando el parpadeo de los sprites de la vieja escuela (teoría y concepto)

que tienen en cuenta las siguientes restricciones si quiero ir de la vieja escuela estilo NES:

  • no más de 64 sprites en la pantalla a la vez
  • No más de 8 sprites por línea de escaneo, o para cada línea en el eje y
  • Si hay demasiada acción va en la pantalla, el sistema se congela la imagen de un marco para que el procesador de ponerse 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 de la exploración 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.

Así, callados que necesito para llegar a una solución que ....

  • 64 sprites en pantalla a la vez
  • 8 sprites por 'línea de barrido'
  • Puede dibujar sprites basado en prioridad
  • puede alternar entre sprites por trama
  • Emulate desaceleración

aquí está mi teoría actual

Primero y ante todo, una idea fundamental que se me ocurrió es hacer frente a la prioridad de sprites. Suponiendo valores entre 0-255 (0 siendo baja), puedo asignar niveles de prioridad sprites, por ejemplo:

  • 0 a 63 siendo bajo
  • 63-127 ser medio
  • 128-191 siendo alta
  • 192 a 255 con una antigüedad máxima

Dentro de mis archivos de datos, que puede asignar a cada sprite para ser 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, se puede dibujar un sprite con una prioridad inicial de 45 después de 3 cuadros sin dibujar (45 + 32 + 32 + 32 = 141)

Esto, en teoría, permitiría a los sprites alternar fotogramas, 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 elemento, pude comprobar cuántos sprites se dibujaron en su respectiva línea de exploración a través de variables de contador. Por ejemplo:

  • Y-valores entre 0 y 7 pertenecen a la línea de exploración 0, scanlineCount [0] = 0
  • Y-valores entre 8 a 15 pertenecen a la línea de exploración 1, scanlineCount [1] = 0
  • etc.

Podría restablecer los valores por línea de escaneo para cada trama dibujada. 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.

SLOWDOWN

Lo último que necesito hacer es emular desaceleració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 w/sprites superan un umbral, ¿introduce desaceleración?

En conclusión ...

hace todo lo que escribí en realidad suena legítimo y podía trabajar, o se trata de una quimera? ¿Qué mejoras pueden posiblemente pensar con esta teoría de programación de juegos?

+0

todo esto es genial, y tienes mi +1 por ser exhaustivo ... pero ¿hay * una pregunta * aquí? –

+0

+1 porque lograste que mi lo leyera todo. –

+0

En "En conclusión". :) Pregunté si esto sonaba legítimo y si podría funcionar o si podría haber mejoras en mi teoría. Considero que esto es un problema, porque expuse mis objetivos y mi teoría actual para obtenerlos, y estoy pidiendo consejos sobre cómo hacerlo: D –

Respuesta

3

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 tipo de lo forzó es una gran idea .

¿Puedo decir que si resumió 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 participar!

  • En lo que respecta a sus sprites de más alta prioridad, tal vez un Priority Queue simplificaría esta parte en la que sólo puede sacar los 64 sprites de más alta prioridad.

  • Con la ralentización, quizás en el motor podría tener un limitValue. Cada sprite como apropiado limitUse. Sume todos los vars de limitUse y si está por debajo de limitValue, no disminuya la velocidad. Ahora, para slowDelay = f(limitUseTotal - limitValue) donde f es una función que convierte la cantidad en exceso de sprites en un valor de desaceleración calculado. ¿Es esta la idea general que estás sugiriendo o he leído mal?

+0

Supongo que Priority Queue básicamente sería lo mismo que crear una clase de sprite personalizada y clasificarla con un clasificador personalizado. :) Y para la desaceleración, esa es la idea en la cabeza. Sin embargo, personalmente me sentiría más cómodo colocándolo con un cierto valor, p. un retraso de no más de 16ms. Porque pude ver un error matemático que retrasa el ciclo de redibujo en 100 ms o así jaja. ¡Gracias! –

+0

Sí, lo que decida para su función f (x) podría incluir un límite superior. –

3

Para prioridad, simplemente 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 un List<Sprite> o similar en su lugar. Tal vez un Sprite[256] para simplificar un poco las cosas.

La limitación del renderizado de sprites 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 entendemos por la desaceleración, así que no puedo responder a esa parte de su pregunta. Lo siento.

Espero que esto ayude.

+0

El GBA tiene una arruga adicional con respecto a los sprites con rotación/escalado de hardware. Los sprites que tienen escala/escala de hw son más costosos de renderizar, por lo que el hardware puede generar menos de ellos. p.ej. si llena la pantalla estirando 4 sprites de 64x64 (el tamaño doble debería cubrir 256x256 en teoría), es probable que la parte inferior de la pantalla no tenga sus sprites dibujados en absoluto. – richq