Desarrollo de Videojuegos


Entornos 2D


Artículo realizado por
David Isla "CID"





PROGRAMACIÓN DE SPRITES

INTRODUCCION

Este articulo esta pensado para todos los lectores que deseen aprender sobre el mundo de la programación de videojuegos. En él, intento explicar de una forma llana, un "método" para comenzar a realizar tus primeros juegos.

En el mundo de programación, existen técnicas usadas por todos los programadores que no dependen, de un determinado tipo de juego, ni de las herramientas utilizadas, ni del lenguaje de programación o engine. Estas técnicas serán las que intentaré explicaros para que podáis adquirir una base.

Además de éste artículo, también os invito a leer el titulado "¿Qué género elegir?" (publicado en este mismo número y sección). Es una guía fantástica para evitar que vuestras intenciones para convertiros en programadores de videojuegos terminen en multitud de pruebas y juegos no terminados.

Ya, sin mas, entramos en el tema . Deseo que disfrutéis del documento tanto como yo me he divertido escribiéndolo.


Puedes conseguir las fuentes y el ejecutable del artículo pulsando aquí

RESOLUCION CONTRA PROFUNDIDAD

Esta es una cuestión que está empezando a tomar un carácter secundario con las nuevas tecnologías que existen hoy día. Cuando "un Byte era un Byte", ésta era una cuestión fundamental. La resolución, como ya sabéis, es el tamaño en pixel de la pantalla (los formatos comunes pueden ser 320x200, 640x480 o 800x600) y la profundidad es el número de colores que la paleta puede soportar. Cuando se realice un juego, se debe tomar en cuenta qué tipo de pantalla vamos a utilizar pues, cuanta más resolución tenga y contra mas colores tenga, nuestros gráficos serán mayores y ocuparán más en memoria. Para elegir, es conveniente saber que se suele sacrificar la resolución de una pantalla ante su profundidad; una pantalla 640x480 y 32000 colores alcanzará una definición mejor que una pantalla 800x600 y 256 colores, y por lo tanto, nuestros juegos ganarán en calidad, rapidez y ocuparan menos recursos.

BACKBUFFER

Esta técnica es, seguramente, la más utilizada en cualquier juego. Se basa en que el programador no dibuja directamente en pantalla, en vez de eso, crea una pantalla virtual en la que dibujará los gráficos necesarios y cuando finalice moverá dicha pantalla virtual hacia la pantalla real. Esto, que pudiera ser confuso, se realiza de la siguiente forma:

  1. El programador cambia la pantalla a modo vídeo 320x200x256 colores. Esta pantalla se maneja como un array de bytes de 64000 posiciones que comienza en la dirección 0xA000.
  2. Se crea un array de bytes, que será el BACKBUFFER, de igual tamaño a la pantalla, o sea, 64000 posiciones de bytes para que pueda guardar una pantalla completa.
  3. Se escribe en el array BACKBUFFER el gráfico o gráficos que se deseen como si fuera la pantalla real.
  4. Se copia el array BACKBUFFER en la pantalla para mostrar lo que habíamos dibujado. Esta copia es recomendable que se haga utilizando alguna función de alta velocidad (al estilo memcpy() de C) o usar una sencilla función en ensamblador (muy recomendable).
  5. Se repiten los pasos 3º y 4º hasta que el programa finalice o no se desee dibujar más en pantalla.

Usar esta técnica de programación evita que los gráficos parezcan estar 'a medias' en algunos momentos y, si estamos realizando animaciones, que la pantalla parpadee demasiado. Además de usar esta técnica, creando una pantalla virtual y actualizando la pantalla con continuos volcados del BackBuffer, podemos utilizar el mismo sistema pero tan solo para partes de la pantalla como veremos mas profundamente en los apartados siguientes dedicados a SPRITES.

También decir que habrá operaciones tan simples y rápidas que no necesitaran estas técnicas y que se accederá directamente a la memoria de vídeo, (por ejemplo un borrado de pantalla).

COLOR TRANSPARENTE

Ésta es otra de las ideas importantes. Si has practicado Backbuffer con diferentes imágenes, habrás observado que si la superpones una encima de la otra, la que queda abajo queda oculta, ¿normal no?, pues ¿qué ocurre si deseamos poner un fondo en la pantalla y una imagen encima de ésta, como algún dibujo o menú gráfico?, pues que al dibujar el menú gráfico o el dibujo, éste se muestra tal y como es, pudiéndose diferenciar el marco de la imagen sobre el fondo. Para que ésto no ocurra se elige un color transparente, es decir, un color que cuando el dibujo tenga que ser pintado en pantalla, o en el Backbuffer, se pase por alto y no borre lo que estuviera debajo. Para que lo veáis mas claro pongo un ejemplo:

Un menu que no respeta el fondo.
Un menu que respeta el fondo.

ANIMACION

Llegamos a lo más interesante del documento, ¡¡la animación!!. Ésta es la parte que nos va a permitir ver a nuestros "grafiquitos", correr y rebotar por pantalla, saltar, disparar, explotar e infinidad de efectos que deseemos realizar, todo basado en la técnica mas básica y sencilla en la programación de videojuegos, los SPRITES. A continuación paso a explicar dos formas de animar imágenes en pantalla.

ROTACION DE LA PALETA DE COLORES

Esta es una forma curiosa de animar imágenes estáticas en nuestros gráficos. Se basa en modificaciones que se realizan en la paleta de colores. Tomando como ejemplo el modo 320x200 y 256 colores (13h), la paleta de colores de este modo gráfico trabaja con un formato RGB de 3 bytes, es decir, cada color (256) se especifica con 3 bytes, cada byte corresponde a la intensidad de un color siendo el byte 1 para el color ROJO (RED), el byte 2 para el color VERDE (GREEN) y el tercero para el color AZUL (BLUE), estas tonalidades están entre 0 y 63, o sea, que el byte solo alcanzara valores entre ese rango. Veamos de nuevo un ejemplo sobre una posible configuración de la paleta de colores:

Como veis en esta tabla, el color 1 correspondería al negro, el 2 al rojo más intenso y el color 3 seria el blanco más brillante. Pues bien, llegados a este punto, podemos jugar con estas configuraciones de tonalidades para que imágenes en pantalla cambien de color, y así conseguir un cierto dinamismo, por ejemplo, colores que pasan de un color intenso a blanco simulando efectos de reflejos, radiactividad, sombras, etc...

SPRITES

Esta es la base de la programación de videojuegos. Se basa en las técnicas anteriormente descritas para conseguir animaciones con movimientos parecido al que se podría diseñar en una película. Dibujando los diferentes estados en los que debe pasar la imagen mientras se mueve por la pantalla. En este apartado final, explicaremos primero como trabaja cualquier sprite, seguiremos con algunos sistemas optimizados para mostrar el sprite y concluiremos explicando las formas en las que podremos mantener estos sprites en memoria y disco.

MOVER EL SPRITE Y GUARDAR EL FONDO

Bien, partamos de la idea de que tenemos una imagen de 32x32 pixel que debemos mover por la pantalla. Para conseguirlo, bastaría con dibujar este sprite en las diferentes posiciones que le corresponda, por ejemplo, si la imagen fuera un personaje que se mueve de izquierda a derecha en la pantalla, dibujaríamos el gráfico del personaje en posiciones x crecientes, es decir, primero en x=0, seguido de x=2, x=4, x=6, etc... hasta que el personaje cruzara por la pantalla entera, momento en que x volvería a ser 0 y comenzara de nuevo la operación. Pero si hacemos esta operación sin borrar las imágenes anteriores tendríamos multitud de gráficos por pantalla, es decir, un gráfico en x=0, otro en x=2, etc., uno en cada posición en el que hemos dibujado el personaje. Como la idea era que pareciera cómo se movía nuestro personaje (uno solo) y no llenar la pantalla completamente de dibujos, adoptaremos la idea de antes de dibujar un gráfico en pantalla, borraremos el anterior, así siempre tendremos una sola imagen en pantalla. Esta operación la podríamos realizar de la siguiente forma:

Dibujar un sprite sin borrar el fondo.

  • Posición = 0
  • Borramos la imagen en Posición dibujando encima de ésta un cuadro del color del fondo de la pantalla, seguramente en negro, para que el cuadro oculte la imagen.
  • Aumentamos Posición por ejemplo en dos unidades para así calcular la próxima posición del personaje.
  • Dibujamos el personaje en Posición
  • Se repiten las operaciones desde 2 a 5 hasta que se pulse una tecla
  • Fin
  • Este pseudocodigo muestras las operaciones a realizar. La primera vez que se ejecuta la orden 2º no tiene mucho sentido, pues aun no se ha dibujado el personaje en pantalla, pero en conjunto funciona (¿Qué pseudocodigo no funciona?).

    Bien, pues llegados a este punto tenemos a nuestro personajes moviéndose por pantalla, de izquierda a derecha, en un fondo negro. Sin embargo, muy pocos juegos usarían un fondo de un solo color, en realidad ninguno. En vez de eso, nuestro personaje debería estar caminando sobre una carretera, un edificio, sobre un cielo azul de nubes blancas o sobre el mar y tal como esta por ahora nuestro personaje al borrar la imagen en el paso 2º también borraría el dibujo de fondo con lo que nos encontraríamos con un problema ¿Qué hacer?, pues sencillo, en vez de machacar el fondo de la pantalla con un cuadrado en negro, lo que hacemos es guardar el fondo en memoria antes de dibujar nuestro personaje y cuando este vaya a cambiar de posición, dibujar de nuevo el fondo guardado donde estaba, aquí tenemos la modificación al pseudocodigo para que realice esta ultima función de guardar el fondo:

    1. Posición = 0
    2. Si hay fondo guardado en memoria lo pinto en la posición Posición.
    3. Aumentamos Posición en dos unidades
    4. Antes de imprimir guardo la zona del fondo donde voy a imprimir en memoria para mas tarde poder restaurarla
    5. Dibujo a nuestro personaje
    6. Se repiten las operaciones desde 2 a 6 hasta que se pulse una tecla
    7. Fin

    Como se puede observar solo hemos insertado una nueva función, la de guardar el fondo de la pantalla, en memoria y hemos sustituido el dibujar un cuadro en negro por dibujar lo que habíamos guardado en memoria.

    Ahora sí que nuestro personaje puede moverse por cualquier tipo de pantalla, tenga un fondo en negro, multicolor o como queramos. El siguiente paso que vamos a ver es cómo, además de este movimiento, el sprite debería tomar diferentes formas para conseguir un movimiento mas real, es decir, imaginemos a nuestro personaje visto lateralmente, si solo tuviera una imagen parecería como si flotara en pantalla, en vez de eso podríamos dibujar a nuestro personaje en distintas posiciones, por ejemplo, moviendo piernas y brazos con lo que conseguiríamos un mejor movimiento.

    FRAMES

    ¿Qué es un Frame?. Un Frame es un instante de la animación. Volviendo a nuestro personaje, un frame podría ser el estado, digamos de reposo, donde el dibujo queremos que esté parado, otro podría ser con la piernas separadas y brazos separados, el siguiente estaría mas separado y así sucesivamente, separándose y cerrándose imitando el movimiento humano, cada imagen descrita es un Frame.

    Un sprite lo forma, como mínimo, un Frame. Según eso podemos decir que contra mas Frames tenga el sprite, mejor estará definido el movimiento. Cuando dibujamos el sprite, en realidad vamos dibujando las distintas posiciones de éste por pantalla, se suele usar un contador de frames que indica por cuál vamos, es decir, qué frame del sprite toca dibujar. En nuestro ejemplo, tenemos un sprite con 5 frames que se ejecutan cíclicamente de izquierda a derecha. Del uno al cinco y de vuelta al uno. Pero no tiene por qué ser todos los sprites cíclicos, por ejemplo, el caso de una explosión debería mostrar cómo al principio una pequeña masa blanca crece convirtiéndose en amarilla y roja hasta desaparecer. Ese tipo de efecto seria un sprite en pantalla que mostraría sus frames por orden pero tan solo una vez.

    MODELOS Y ESTRUCTURAS DE MEMORIA

    Bueno, y una vez que tenemos pensado que sprites va a necesitar nuestro juego (nuestro personaje, los malos, explosiones, etc. ), ¿cómo los dibujamos? y más importante aún ¿cómo lo guardamos para que nuestra aplicación pueda usarlos?. Pues para estas preguntas hay varias respuesta que paso a explicar.

    USAR UN ARCHIVO GRAFICO

    Esta opción se basa en dibujar con nuestra herramienta de dibujo preferida los frames del sprite y después guardarlos todos en un archivo gráfico. La mayoría de juegos que usan este método adoptan los PCX aunque no todos (y no voy a explicar ahora el por qué este formato es idóneo para estas imágenes). Pero además de que el fichero contenga la imágenes, éste debe tener una estructura definida por nosotros para que podamos localizar rápidamente, en la imagen, dónde se encuentra el frame que buscamos. Para ello podemos optar en dividir el gráfico en celdillas. En cada una de estas celdillas, colocaremos un frame del sprite. Algo así, he hecho con la demo de estos artículos; he divido el archivo PCX en celdas de 64x64 pixel, o sea, que cada uno de mis frames empiezan en múltiplos de 64, el primero en la 0, el siguiente en el 64, el tercero en 128, y así hasta el quinto.

    Este método suele ser el mejor que podemos adoptar por varias razones. La primera seria que hay multitud de herramientas en el mercado para dibujar nuestros sprites en cualquier formato, PCX, BMP, JPG, etc. Otra gran ventaja es que estos tipos de gráficos ya tienen incorporado un sistema de compresión como puede ser el RLE del PCX, con lo que ocuparán menos en disco. Como única desventaja puedo destacar que estos ficheros no han sido diseñados para construir sprites y que pueden tener algunas pegas a la hora de aprovechar el espacio, es decir, en nuestro ejemplo decidí dividir las celdas entre 64 para que el tamaño del PCX (320x200) me diera para incluir 5 frames, sin en vez de un tamaño de 64 tuviera que haber elegido uno de 60x60, existiría espacio no usado por el sprite, y para poner un ejemplo mas claro, mirad todo el espacio que desaprovecho en el PCX de nuestro personaje, tan solo he utilizado los 64 pixel altos de los 200 que disponía la altura de la imagen.

    USAR UN FICHERO PROPIO DE ALMACENAMIENTO

    Esta opción también es muy usada aunque necesita de un mejor nivel para llevarla acabo. Se basa en la construcción de nuestro propio tipo de gráfico sprite. Aunque no quiero hablar mucho sobre el tema, pues en próximos números de Macedonia hablaremos sobre él (incluso en la construcción de nuestro propio editor de sprites), adelantare algunas ideas. Para realizar esta 'proeza', deberíamos primero en pensar el formato del fichero, por ejemplo, debería tener una cabecera donde indicara el tamaño en pixel de cada imagen, el tamaño en x y en y, el numero de frames, paleta adicional, nombre, etc. después de esta cabecera, vendrían los frames uno por uno. Entonces este formato creado tendría que ser usado en nuestros programas, e incluso estaría bien crear herramientas de conversión entre archivos gráficos comunes como los PCX y nuestro formato, a la inversa, y para finalizar aumentar nuestro fichero de sprites al concepto de librería de sprites, donde este nuevo fichero pudiera soportar varios sprites, del mismo modo que creamos una cabecera indicando cuantos frames tiene el sprite, ahora indicaríamos cuántos sprites tiene la librería, etc.

    Todo esto que he comentado será expuesto mas extensamente con la elaboración de nuestro propio formato de sprite y nuestro propio editor de sprites, además de demostrar en un programa real (el editor) las técnicas antes comentadas sobre colores transparente, backbuffer, etc.. Todo ello llegara en nuestros próximos número. Por ahora os dejo con la demostración de este numero en C, que la disfrutéis :).


    No olvides llevarte las fuentes y el ejecutable del artículo pulsando aquí



    ÚLTIMA REVISIÓN EN ABRIL DE 1999




    DESARROLLO DE VIDEOJUEGOS
    a
    MACEDONIA Magazine