Creando un juego para Game Boy

imagen

Hace unas semanas, decidí trabajar en un juego para Game Boy, cuya creación me dio un gran placer. Su nombre de trabajo es Aqua and Ashes. El juego tiene código abierto y está publicado en GitHub .

¿Cómo se me ocurrió esta idea?


Recientemente obtuve un trabajo de pasantía creando un backend en PHP y Python para el sitio web de mi universidad. Este es un trabajo bueno e interesante, por lo que estoy muy agradecido. Pero ... al mismo tiempo, todo este código de desarrollo web de alto nivel me ha infectado con un deseo insaciable. Y era el deseo de trabajo de bajo nivel con brocas.

Recibí un resumen semanal de itch.io sobre jams de juegos, en el que se anunció el lanzamiento de Mini Jam 4 . Fue un atasco de 48 horas (bueno, en realidad un poco más grande), en el que la limitación era crear gráficos al estilo de Game Boy. Mi primera reacción lógica fue crear un juego casero para Game Boy. El tema de la mermelada fue "estaciones" y "llama".

Después de pensar un poco en la trama y la mecánica que se pueden implementar en 48 horas y encajar en las limitaciones del tema, se me ocurrió un clon de una nueva interpretación del nivel del juego SNES de 1993 Tiny Toon Adventures: Buster Busts Loose!, En el que el jugador en el papel de Baster juega fútbol americano .


Siempre me gustó cómo los creadores de este nivel tomaron un deporte increíblemente complejo, se deshicieron de todos los trucos, posiciones y elementos estratégicos, como resultado de obtener un juego extremadamente interesante y fácil. Obviamente, una visión tan simplificada del fútbol americano no reemplazará a Madden, al igual que NBA Jam (una idea similar: solo 4 jugadores en un campo mucho más pequeño con un juego más directo que en un juego normal) no reemplazará a la serie 2K. Pero esta idea tiene cierto encanto, y las cifras de ventas de NBA Jam lo confirman.

¿Cómo se relaciona todo esto con mi idea? Decidí tomar este nivel de fútbol y rehacerlo para que siga siendo similar al original y al mismo tiempo esté fresco. En primer lugar, reduje el juego a solo cuatro jugadores: un defensor y un atacante. Esto se hizo principalmente debido a las limitaciones del hardware, pero al mismo tiempo me permitirá experimentar un poco con una IA más inteligente, no se limita al principio de "correr hacia la izquierda y, a veces, saltar" de jugar en SNES.

En aras de la conformidad con el tema, reemplazaré las puertas con columnas encendidas, o con hogueras, o con algo así (aún no lo he decidido), y un balón de fútbol con antorchas y cubos de agua. El ganador será el equipo que controla ambas hogueras, y en torno a este concepto simple, fácilmente puede llegar a una trama. Las estaciones también se tienen en cuenta: decidí que las estaciones cambiarán cada turno, para que el equipo de bomberos obtenga una ventaja en el verano y el equipo de bomberos en el invierno. Esta ventaja parece obstáculos en el campo que interfieren solo con el equipo contrario.

Por supuesto, al crear dos equipos, se necesitaban dos animales que amaran y no les gustara el fuego. Al principio pensé en hormigas de fuego y algunas chinches de agua, mantis religiosa y similares, pero después de estudiar el tema, no encontré ningún insecto activo en invierno, así que los reemplacé con zorros polares y gecos. A los zorros polares les gusta la nieve, a los geckos les gusta tumbarse al sol, por lo que todo parece lógico. Al final, es solo un juego para Game Boy.

Además, en caso de que esto aún no esté claro, al final del atasco el juego no estaba cerca de completarse. De todos modos, fue divertido de todos modos.

Game Boy Training


Primero debes determinar los requisitos. Decidí escribir para DMG (nombre interno para el modelo Game Boy, abreviatura de Dot Matrix Game). Principalmente para cumplir con los requisitos de un atasco de juego, pero también porque realmente quería hacerlo. Personalmente, nunca he tenido juegos para DMG (aunque hay varios juegos para Game Boy Color), pero considero que la estética de 2 bits es una limitación muy agradable e interesante para los experimentos. Quizás agregue color adicional para SGB y CGB, pero hasta ahora no he pensado en ello.

También decidí usar un cartucho con 32K ROM + sin RAM, en caso de que quiera crear una copia física del juego. CatSkull, que ha publicado varios juegos de Game Boy, como Sheep it Up !, tiene a la venta cartuchos flash de 32 kilobytes muy baratos que son perfectos para mí. Esta es otra limitación adicional, pero no creo que en un futuro cercano pueda superar el volumen de 32K con un juego tan simple. Lo más difícil será con los gráficos, y si todo está completamente mal, intentaré comprimirlo.

En cuanto al trabajo de Game Boy, entonces todo es bastante complicado. Sin embargo, para ser honesto, de todas las consolas retro con las que tuve que trabajar, Game Boy fue la más agradable. Comencé con un excelente tutorial (al menos por primera vez, porque nunca fue completado) por el autor de AssemblyDigest. Sabía que lo mejor es escribir en ASM, por más doloroso que sea a veces, porque el hardware no está diseñado para C, y no estaba seguro de que el lenguaje genial que Wiz mencionó en el tutorial sería adecuado a largo plazo. Además, hago esto principalmente porque puedo trabajar con ASM.

Comprueba el commit 8c0a4ea

Lo primero que debía hacer era arrancar el Game Boy. Si el logotipo de Nintendo no se encuentra en un desplazamiento de $104 , y el resto del encabezado no está configurado correctamente, entonces el equipo de Game Boy asumirá que el cartucho está insertado incorrectamente y se negará a cargar. Resolver este problema es muy simple, porque ya se han escrito muchos tutoriales sobre esto. Así es como resolví el problema del encabezado. No hay nada que merezca especial atención.

Será más difícil realizar acciones significativas después de la carga. Es muy simple hacer que el sistema entre en un ciclo ocupado infinito en el que ejecuta una línea de código una y otra vez. La ejecución del código comienza con la etiqueta main (donde indica el salto a la dirección $100 ), por lo que debe insertar un código simple allí. Por ejemplo:

 main: .loop: halt jr .loop 

y no hace absolutamente nada más que esperar a que comience la interrupción, después de lo cual vuelve a la etiqueta .loop . (En lo sucesivo, omitiré una descripción detallada de ASM. Si se confunde, consulte la documentación del ensamblador que uso ). Puede que tenga curiosidad por qué no vuelvo a la etiqueta main . Esto se debe a que quiero que todo antes de la etiqueta .loop sea ​​la inicialización del programa, y ​​todo después de que ocurra cada fotograma. Por lo tanto, no tengo que pasar por alto el ciclo de carga de datos del cartucho y borrar la memoria en cada cuadro.

Vamos a dar un paso más. El paquete ensamblador RGBDS que uso contiene un convertidor de imágenes. Como en esta etapa aún no he dibujado ningún recurso para el juego, decidí usar el botón monocromo de mi página Acerca de como mapa de bits de prueba. Usando RGBGFX, lo convertí al formato Game Boy y usé el comando ensamblador .incbin para insertarlo después de la función main .

imagen

Para mostrarlo en la pantalla, necesito lo siguiente:

  1. LCD apagado
  2. Establecer paleta
  3. Establecer posición de desplazamiento
  4. Borrar memoria de video (VRAM)
  5. Cargue gráficos de mosaico en VRAM
  6. Descargar el mapa de fondo del mosaico VRAM
  7. Encienda la pantalla LCD nuevamente

LCD apagado


Para los principiantes, este se convierte en el obstáculo más serio. En el primer Game Boy, es imposible simplemente escribir datos en VRAM en cualquier momento. Es necesario esperar el momento en que el sistema no dibuja nada. Imitando el brillo del fósforo en televisores CRT viejos, el intervalo entre cada cuadro cuando VRAM está abierto se llama Vertical-Blank, o VBlank (en CRT es un impulso para dejar en blanco el haz del kinescopio durante una exploración inversa). (También hay HBlank entre cada línea de la pantalla, pero es muy corto). Sin embargo, podemos solucionar este problema apagando la pantalla LCD, es decir, podemos grabar en VRAM independientemente de dónde se encuentre el "rastro de fósforo" de la pantalla CRT.

Si está confundido, entonces esta revisión debería explicarle mucho . En él, la pregunta se considera desde el punto de vista de SNES, así que no olvide que no hay haz de electrones y que los números son diferentes, pero en todo lo demás es bastante aplicable. Esencialmente, necesitamos establecer la bandera FBlank.

Sin embargo, el truco de Game Boy es que solo puedes apagar la pantalla LCD durante VBlank. Es decir, tenemos que esperar a VBlank. Para hacer esto, usa interrupciones. Las interrupciones son señales de que Game Boy envía hardware a la CPU. Si el controlador de interrupción está configurado, el procesador detiene su trabajo y llama al controlador. Game Boy admite cinco interrupciones, y una de ellas comienza cuando comienza VBlank.

Las interrupciones se pueden manejar de dos maneras diferentes. La primera y más común es la tarea de un controlador de interrupciones que funciona como expliqué anteriormente. Sin embargo, podemos habilitar una interrupción específica y deshabilitar todos los controladores configurando el indicador de habilitación para esta interrupción y usando el código di operación. Por lo general, no hace nada, pero tiene el efecto secundario de salir del código de operación HALT, que detiene la CPU antes de que ocurra una interrupción. (Esto también sucede cuando los controladores están activados, lo que nos permite salir del ciclo HALT en main ). En caso de que esté interesado, también crearemos un controlador VBlank con el tiempo, pero mucho dependerá de ciertos valores en ciertas direcciones. Como hasta ahora no se ha configurado nada en la RAM, un intento de llamar al controlador VBlank puede provocar un bloqueo del sistema.

Para establecer los valores, necesitamos enviar comandos a los registros de hardware de Game Boy. Hay direcciones de memoria especiales que están directamente relacionadas con varias partes del equipo, en nuestro caso, la CPU, que le permite cambiar la forma en que funciona. Estamos particularmente interesados ​​en las direcciones $FFFF (campo de bit de activación de interrupción), $FF0F (campo de bit de interrupción activado pero no controlado) y $FF40 (control LCD). Puede encontrar una lista de estos registros en las páginas asociadas con la sección "Documentación" de la lista Awesome Game Boy Development.

Para apagar la pantalla LCD, activamos solo la interrupción VBlank, asignando a $FFFF valor $01 , realizamos HALT hasta que se cumpla la condición $FF0F == $01 , y luego asignamos el bit 7 de la dirección $FF40 a 0.

Establecer la paleta y la posición de desplazamiento


Esto es fácil de hacer. Ahora que la pantalla LCD está apagada, no tenemos que preocuparnos por VBlank. Para establecer la posición de desplazamiento, es suficiente establecer los registros X e Y en 0. Con la paleta, todo es un poco más complicado. En Game Boy, puede asignar sombras del primer al cuarto gráfico de cualquiera de las 4 sombras de gris (o verde pantano, si lo desea), lo cual es útil para realizar transiciones y similares. Establecí un gradiente simple como una paleta, definida como una lista de bits %11100100 .

Borrar VRAM y cargar gráficos de mosaico


Cuando se inicie, todos los datos gráficos y el mapa de fondo consistirán solo en un logotipo de Nintendo desplazable, que se muestra cuando se inicia el sistema. Si enciendo sprites (están deshabilitados por defecto), serán basura diseminada por la pantalla. Debe borrar la memoria de video para comenzar desde cero.

Para hacer esto, necesito una función como memset de C. (También necesito una memcpy analógica para copiar los datos gráficos). La función memset establece el fragmento de memoria especificado en un determinado byte. Será fácil para mí implementar esto yo mismo, pero el tutorial de AssemblyDigest ya tiene estas funciones, así que las uso.

En este punto, puedo borrar VRAM con memset escribiendo $00 (aunque la primera confirmación usó el valor $FF , que también era adecuado), y luego cargar los gráficos de mosaico en VRAM usando memcpy . Más específicamente, necesito copiarlo a la dirección $9000 , porque estos son mosaicos utilizados solo para gráficos de fondo. (Las direcciones $8000-$87FF se usan solo para fichas de sprite, y las direcciones $8800-$8FFF son comunes para ambos tipos).

Configuración del mapa de mosaico


Game Boy tiene una capa de fondo, dividida en 8x8 mosaicos. La capa de fondo en sí toma aproximadamente 32x32 mosaicos, es decir, tiene un tamaño total de 256x256. (A modo de comparación: la pantalla de la consola tiene una resolución de 160x144.) Necesitaba indicar manualmente los mosaicos que componen mi imagen línea por línea. Afortunadamente, todos los mosaicos se organizaron en orden, por lo que solo tuve que llenar cada línea con valores de N*11 a N*11 + 10 , donde N es el número de línea y los 22 elementos restantes completan $FF .

Encendido de la pantalla LCD


Aquí no necesitamos esperar a VBlank, porque la pantalla todavía no se encenderá hasta VBlank, por lo que acabo de escribir nuevamente en el registro de control LCD. También incluí capas de fondo y sprites, y también indiqué las direcciones correctas del mapa de mosaico y los gráficos de mosaico. Después de eso obtuve los siguientes resultados. También encendí los manejadores de interrupciones nuevamente usando el ei opcode.

En este punto, para hacerlo aún más interesante, escribí un manejador de interrupciones muy simple para VBlank. Al agregar el código de operación de transición a $40 , puedo hacer que el controlador tenga cualquier función que necesite. En este caso, escribí una función simple que desplaza la pantalla hacia arriba y hacia abajo.

Aquí están los resultados finales. [Adición: me acabo de dar cuenta de que el GIF no está colocado correctamente, debe transferir constantemente la imagen.]


Hasta ahora, nada particularmente sorprendente, pero sigue siendo genial que, en teoría, pueda obtener mi viejo Game Boy Color y ver cómo se ejecuta mi propio código.

Diversión con sábanas a cuadros


Para dibujar algo en la pantalla, naturalmente necesito algún tipo de sprite. Después de estudiar la PPU (Picture Processing Unit) de la consola Game Boy, decidí centrarme en sprites 8x8 u 8x16. Probablemente, necesitaré la última opción, pero solo para sentir el tamaño, rápidamente garabateé una captura de pantalla del juego en una escala de 1: 8 en papel a cuadros.


Quería dejar la parte superior de la pantalla debajo del HUD. Me pareció que se vería más natural que desde abajo, porque cuando está arriba, si los personajes necesitan bloquear temporalmente el HUD, como en Super Mario Bros, pueden hacerlo. Este juego no tendrá plataformas complejas, y de hecho el diseño del nivel, así que no necesito mostrar una visión muy general del campo. La posición de los personajes en la pantalla y, posiblemente, los obstáculos que aparecen de vez en cuando serán suficientes. Por lo tanto, puedo permitirme sprites bastante grandes.

Entonces, si un cuadrado era un mosaico de 8x8, entonces un sprite no será suficiente, sin importar el tamaño que elija. Esto es especialmente cierto dado que casi no habrá movimiento vertical en el juego, con la excepción de los saltos. Entonces decidí crear sprites a partir de cuatro sprites de 8x16. La excepción fue la cola del zorro, que ocupa dos sprites de 8x16. Después de cálculos simples, quedó claro que dos zorros y dos geckos ocuparán 20 de los 40 sprites, es decir, será posible agregar muchos más sprites adicionales. (Los sprites 8x8 se quedarían rápidamente fuera de mi límite, lo que no quiero hacer en las primeras etapas de desarrollo).

Por ahora, solo necesito dibujar sprites. A continuación hay bocetos en papel a cuadros. Tengo un sprite en espera, un sprite "pensante" para elegir si pasar o correr, como en un juego de SNES ... y eso es todo. También planeé hacer sprites de personajes corriendo, saltando personajes y personajes que agarran los oponentes. Pero para empezar, dibujaba solo sprites que esperaban y pensaban, para no complicarme. Todavía no hice el resto, necesito hacer esto.


Sí, lo sé, no dibujo muy bien. La perspectiva es algo complicado. (Sí, y esta cara del zorro polar es terrible). Pero me queda perfectamente. El diseño de personajes no tiene características especiales, pero es adecuado para el juego jam. Por supuesto, usé geckos reales y zorros polares como referencias. ¿Es imperceptible?



No puedes decirlo. (Para el registro: después de haber visto estas imágenes nuevamente, me di cuenta de que hay una gran diferencia entre geckos y lagartos. No sé qué hacer con esto, excepto para considerarme estúpido ...) Creo que puedes adivinar que es la fuente de inspiración para Blaze the Cat de la serie de juegos de Sonic sirvió como la cabeza del zorro.

Inicialmente, quería que los defensores y delanteros en cada equipo fueran de diferentes sexos y era más fácil distinguirlos. (También iba a dejar que los jugadores elijan el género de su personaje). Sin embargo, esto requeriría mucho más dibujo. Así que me decidí por gecos machos y zorros hembras.

Y finalmente, dibujé una pantalla de bienvenida porque había un lugar para ello en un pedazo de papel a cuadros.


Sí, las poses de acción aún están lejos de ser ideales. El zorro polar debería estar más molesto y corriendo, y el gecko debería verse amenazador. Defender Fox en el fondo: una divertida referencia al arte en el cuadro Doom.

Digitalización de sprites.


Entonces comencé a convertir dibujos de papel en sprites. Para esto, utilicé el programa GraphicsGale, que recientemente se hizo gratuito. (Sé que podrías usar asesprite, pero prefiero GraphicsGale). El trabajo en sprites resultó ser mucho más complicado de lo que esperaba. Cada uno de estos cuadrados de los sprites que se muestran arriba ocupa hasta 4 píxeles en una cuadrícula de 2x2. Y estos cuadrados a menudo tenían MUCHO más detalles que 4 píxeles. Por lo tanto, tuve que deshacerme de muchos detalles de los bocetos. A veces incluso era difícil adherirse a una forma simple, porque era necesario dejar un lugar aceptable para los ojos o la nariz. Pero me parece que todo se ve bien, incluso si el sprite se ha vuelto completamente diferente.


Los ojos del zorro perdieron su forma almendrada y se convirtieron en una línea de dos píxeles de altura. Los ojos del gecko han conservado su redondez. La cabeza del gecko tuvo que agrandarse, deshaciéndose de los hombros anchos, y todas las curvas que el zorro podría haber tenido se suavizaron significativamente. Pero, sinceramente, todos estos cambios fáciles no son tan malos. A veces casi no puedo elegir cuál de las variaciones es mejor.


GraphicsGale también tiene funciones convenientes para capas y animaciones. Esto significa que puedo animar la cola del zorro por separado de su cuerpo. Esto ayuda mucho a ahorrar un valioso espacio VRAM porque no necesito duplicar la cola en cada cuadro. Además, esto significaba que podías mover la cola con una velocidad variable, disminuyendo la velocidad cuando el personaje está de pie y acelerando mientras corres. Sin embargo, esto hace que la programación sea un poco más complicada. Pero aún así voy a asumir esta tarea. Me decidí por 4 cuadros de animación, porque esto es suficiente.

Puede notar que el zorro polar usa los tres tonos más claros de gris, mientras que el gecko usa los tres tonos más oscuros de gris. En GameBoy, esto es aceptable, porque aunque solo puede haber tres colores en un sprite, la consola le permite especificar dos paletas. Lo hice para que la paleta 0 se use para los zorros, y la paleta 1 para los geckos. Eso es todo el conjunto de paletas disponible, pero no creo que necesite otros.

También necesitaba cuidar el fondo. No me molesté con sus bocetos, porque planeé que sería un color sólido o un patrón geométrico simple. Todavía no he digitalizado el protector de pantalla, porque no tuve suficiente tiempo.

Cargando sprites en el juego


Verifique con el compromiso be99d97 .

Después de guardar cada cuadro individual de gráficos de personajes, fue posible comenzar a convertirlos al formato GameBoy. Resultó que en RGBDS hay una utilidad muy conveniente para esto llamada RGBGFX. Puede llamarlo con el comando rgbgfx -h -o output.bin input.png y creará un conjunto de mosaicos compatibles con GameBoy. (El modificador -h especifica un modo de mosaico compatible con 8x16 para que la conversión se realice de arriba a abajo y no de izquierda a derecha). Sin embargo, no proporciona enlaces y no puede rastrear mosaicos duplicados cuando cada cuadro es una imagen separada. Pero dejaremos este problema para más adelante.

Después de generar los archivos .bin de salida, simplemente agréguelos en ensamblador usando incbin "output.bin" . Para mantener todo junto, creé un archivo común "gfxinclude.z80", que contiene todos los gráficos que se agregarán.

Sin embargo, fue muy aburrido regenerar manualmente los gráficos cada vez que algo cambiaba. Así que edité el archivo build.bat, agregando la línea for %%f in (gfx/*.png) do rgbds\rgbgfx -h -o gfx/bin/%%f.bin gfx/%%f , que convierte cada archivo. png en la carpeta gfx / en el archivo bin y lo guarda en gfx / bin. Me simplificó mucho la vida.

Para crear gráficos de fondo, utilicé una forma mucho más perezosa. RGBASM tiene una directiva dw ` . Le sigue una línea de 8 valores de 0 a 4 igual a una línea de datos de píxeles. Como los sprites de fondo eran muy simples, resultó más fácil copiar y pegar un patrón geométrico simple para crear un patrón sólido, rayado o de tablero de ajedrez. Aquí, por ejemplo, parece una baldosa terrestre.

 bg_dirt: dw `00110011 dw `00000000 dw `01100110 dw `00000000 dw `11001100 dw `00000000 dw `10011001 dw `00000000 

Crea una serie de rayas desplazadas con la ilusión de la perspectiva. Este es un enfoque simple pero inteligente. Con hierba era un poco más complicado. Inicialmente, era un grupo de líneas horizontales de 2 píxeles de altura, pero agregué manualmente algunos píxeles que agregan un poco de ruido que hace que el césped se vea mejor:

 bg_grass: dw `12121112 dw `12121212 dw `22112211 dw `11121212 dw `22112211 dw `21212121 dw `12121212 dw `12211222 

Renderizado de gráficos


En GameBoy, los sprites se almacenan en un área llamada OAM, o memoria de atributo de objeto. Contiene solo atributos (dirección, paleta y prioridad), así como el número de mosaico.Fue suficiente para mí llenar esta área de memoria para mostrar sprites en la pantalla.

Aunque hay pequeñas características. Primero, debe cargar los gráficos desde la ROM en VRAM. GameBoy solo puede renderizar mosaicos que están almacenados en un área especial de memoria llamada VRAM. Afortunadamente, para copiar de ROM a VRAM, es suficiente realizarlo memcpyen la etapa de inicialización del programa. Resultó que con solo 6 sprites de personajes y 4 fichas de cola, ya tomé una cuarta parte del área VRAM asignada para sprites. (VRAM generalmente se divide en áreas de fondo y sprites, y 128 bytes son comunes para ellos).

Además, el acceso a OAM solo es posible durante VBlank. Comencé diciendo que VBlank estaba esperando los cálculos de sprites, pero me encontré con problemas porque los cálculos de sprites duraron todo el tiempo asignado por VBlank y era imposible terminarlos. La solución aquí es escribir en un área de memoria separada fuera de VBlank y simplemente copiarlos a OAM durante VBlank.

Al final resultó que, GameBoy tiene un procedimiento especial de copia de hardware, un tipo de DMA (Acceso directo a la memoria, acceso directo a la memoria) que hace exactamente eso. Al escribir en un registro específico e ir al ciclo de ocupado en HiRAM (porque la ROM no está disponible durante DMA), puede copiar datos de RAM a OAM mucho más rápido que usar la funciónmemcpy. Si está interesado, puede encontrar detalles jugosos aquí .

En esta etapa, solo pude crear un procedimiento que determine lo que finalmente se escribirá en el DMA. Para hacer esto, necesitaba almacenar el estado de los objetos en otro lugar. Como mínimo, se requería lo siguiente:

  1. Tipo (gecko, zorro polar o artículo de transporte de uno de los equipos)
  2. Dirección
  3. Posición X
  4. Posición Y
  5. Marco de animación
  6. Temporizador de animación

En la primera decisión, muy desaliñada, verifiqué el tipo de objeto y, dependiendo de ello, cambié a un procedimiento que dibuja este tipo de objeto. El procedimiento del zorro polar, por ejemplo, tomó una posición en X, dependiendo de la dirección, agregó o resta 16, agregó dos sprites de cola y luego se movió hacia arriba y hacia abajo del sprite principal.

Aquí hay una captura de pantalla de cómo se veía el sprite en VRAM cuando se renderiza en la pantalla. La parte izquierda es sprites individuales, números hexadecimales junto a ellos, de arriba a abajo: posición vertical y horizontal, mosaico y banderas de atributos. A la derecha puedes ver cómo se veía todo después del ensamblaje.


Con la animación de cola, fue un poco más complicado. En la primera solución, simplemente realicé un incremento del temporizador de animación en cada cuadro y produje uno lógico andcon un valor %11para obtener el número de cuadro. Luego, simplemente podría agregar 4 * el número de cuadro (cada cuadro de animación consta de 4 cuadros) al primer cuadro de cola en VRAM para obtener 4 cuadros diferentes almacenados en VRAM. Funcionó (especialmente el que tiene la búsqueda de mosaico de la cola), pero la cola se movió increíblemente rápido, y necesitaba encontrar una manera de frenarlo.

En la segunda, mejor solución, realicé un incremento del temporizador global en cada cuadro , y cuando el valor de la operación andcon ély el grado de dos elegido por mí fue 0, se realizó el incremento del temporizador de objetos. Por lo tanto, cada objeto individual podría contar su temporizador de animación a cualquier velocidad que necesitara. Esto funcionó perfectamente y me permitió desacelerar la cola a un nivel razonable.

Dificultades


Pero si todo fuera tan simple. No olvide que administré todo esto en el código, usando mi propio subprocedimiento para cada objeto, y si necesita continuar, debe hacerlo en cada marco. Tuve que especificar cómo proceder al siguiente sprite, así como en qué mosaico consiste, manipulando manualmente los registros.

Era un sistema completamente inestable. Para dibujar un cuadro, era necesario hacer malabarismos con un número suficientemente grande de registros y tiempo de CPU. Era casi imposible agregar soporte para otro personal, e incluso si hubiera tenido éxito, el soporte del sistema sería muy doloroso. Créeme, fue un verdadero caos.Necesitaba un sistema en el que el código para renderizar sprites fuera generalizado y directo, de modo que no fuera una combinación de condiciones, manipulación de registros y operadores matemáticos.

¿Cómo solucioné esto? Hablaré de esto en la próxima parte del artículo.

imagen

Source: https://habr.com/ru/post/es436330/


All Articles