Sprites II: Animación de sprites (C y ASM con SDCC)

Click here to see in English

En este tutorial vamos a comprobar lo sencillo que es simular el movimiento de un personaje mediante sprites. Vamos a continuar usando el personaje Nick de Snow Bros. Vamos a hacer que el personaje camine de derecha a izquierda por la pantalla, para ello necesitamos conseguir los gráficos/sprites de todos los pasos de la animación. Para hacerlo sencillo he capturado de la recreativa original únicamente 3 sprites para caminar a la izquierda y los mismos 3 sprites invertidos horizontalmente para caminar a la derecha. (Si fuera necesario por problemas de espacio o memoria se podría hacer una rutina que dibujase los sprites invertidos horizontalmente, para únicamente necesitar 3 para izquierda y derecha, pero por ahora no nos vamos a complicar). Los graficos son los siguientes:

nick

 

He decidido hacerlo todo junto en un único grafico en vez de en 6 graficos individuales, también puede verse que hay un pixel en blanco a cada lado, para evitar dejar estela o tener que borrar al mover el sprite. Vamos a convertirlo a Modo 0, paleta de 16 colores para poder usarlo en el cpc. Para ello volvemos a usar ConvImgCPC como ya vimos en el tutorial Convirtiendo y mostrando una imagen en pantalla, yo lo he configurado de la siguiente manera:

ConvImgCpc

Como se puede ver he utilizado las opciones "Keep original size", "Overscan" poniendo 168 (28 * 6) de alto y 12 de ancho (12 bytes en modo 0 = 24 pixels). Vemos que Nick ha engordado un poco debido al aspect ratio del modo 0, pero bueno eso no nos preocupa ahora mismo. Para exportarlo seleccionamos las opciones "asm mode" y "Linear" y pulsamos "Save Picture". Guardamos el fichero en asm y lo convertimos a C fácilmente como ya vimos en el tutorial Convirtiendo y mostrando una imagen en pantalla, finalmente nos queda un fichero Nick.h como este:

/*
;Généré par ConvImgCpc Version 0.16
; Mode 0
; 12x168
; Linear
*/

#define NICK_WIDTH 12
#define NICK_HEIGHT 28
#define NUM_COLORS 16

const unsigned char NickPalette[NUM_COLORS] = {26, 0, 13, 2, 1, 10, 11, 20, 15, 3, 14, 0, 0, 0, 0, 0};

const char NickSprite[] = 
{
               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x84
        ,      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x48, 0xE8, 0x08, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x04, 0xC0, 0xC0, 0xD4, 0xFC
        ,      0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xC0
        ,      0x08, 0x0C, 0x5C, 0xF8, 0xE0, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x48, 0x08, 0x00, 0x00, 0x48, 0xF0
        ,      0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00
        ,      0x00, 0x00, 0x04, 0xD0, 0x80, 0x00, 0x00, 0x00
        ,      0x00, 0x04, 0x80, 0x00, 0x00, 0x00, 0x04, 0x48
        ,      0x08, 0x00, 0x00, 0x00, 0x00, 0x40, 0x48, 0x80
        ,      0x00, 0x00, 0x00, 0x0C, 0x80, 0x00, 0x00, 0x00
        ,      0x00, 0x40, 0x40, 0x80, 0x00, 0x00, 0x00, 0x0C
        ,      0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x80
        ,      0x41, 0x02, 0x04, 0x0C, 0x80, 0x00, 0x00, 0x00
        ,      0x00, 0x40, 0x08, 0x41, 0x83, 0x00, 0x0C, 0x48
        ,      0x08, 0x00, 0x00, 0x00, 0x00, 0x04, 0xC1, 0x83
        ,      0x02, 0x0C, 0xCC, 0x98, 0x84, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x81, 0x02, 0x00, 0xC4, 0x3C, 0x00
        ,      0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00
        ,      0x48, 0x8D, 0x28, 0x00, 0x00, 0x84, 0x00, 0x00
        ,      0x00, 0x00, 0x84, 0x0C, 0x84, 0x1C, 0x2C, 0x00
        ,      0x00, 0x48, 0x08, 0x00, 0x00, 0x00, 0x48, 0x0C
        ,      0x08, 0x14, 0x6C, 0x08, 0x00, 0x04, 0x80, 0x00
        ,      0x00, 0x00, 0x04, 0xC0, 0x00, 0x14, 0x3C, 0x24
        ,      0x00, 0x04, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40
        ,      0x00, 0x1C, 0x1E, 0x98, 0x0C, 0x04, 0x80, 0x00
        ,      0x00, 0x00, 0x00, 0x48, 0x00, 0x9C, 0x1E, 0xCC
        ,      0x24, 0x48, 0x08, 0x00, 0x00, 0x00, 0x00, 0x84
        ,      0x4C, 0x2D, 0x3C, 0xCC, 0x30, 0x84, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x80, 0xCC, 0x07, 0x6C, 0x98
        ,      0x60, 0x60, 0x08, 0x00, 0x00, 0x00, 0x00, 0x84
        ,      0x49, 0x8D, 0x6C, 0x30, 0xC0, 0x30, 0x80, 0x00
        ,      0x00, 0x00, 0x00, 0x48, 0xC4, 0x3C, 0xCC, 0x60
        ,      0x90, 0x30, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0xC4, 0x9C, 0x98, 0xC4, 0x98, 0x70, 0xE0, 0x00
        ,      0x00, 0x00, 0x00, 0x04, 0xC0, 0x98, 0x60, 0x48
        ,      0xC4, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x04, 0xC0
        ,      0xFC, 0xF8, 0xE0, 0xD0, 0xF0, 0xF0, 0x84, 0x00
        ,      0x00, 0x00, 0x40, 0xFC, 0xFC, 0xF0, 0xE0, 0xF0
        ,      0xF0, 0xE0, 0x08, 0x00, 0x00, 0x00, 0x04, 0xC0
        ,      0xC0, 0xC0, 0x84, 0xC0, 0xC0, 0x84, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x04, 0x84, 0x00, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xE8
        ,      0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04
        ,      0xC0, 0xC0, 0xD4, 0xFC, 0x84, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x04, 0xC0, 0x08, 0x0C, 0x5C, 0xF8
        ,      0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x08
        ,      0x00, 0x00, 0x48, 0xF0, 0x84, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x04, 0xD0
        ,      0x80, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x00
        ,      0x00, 0x00, 0x04, 0x48, 0x08, 0x00, 0x00, 0x00
        ,      0x00, 0x40, 0x48, 0x80, 0x00, 0x00, 0x00, 0x0C
        ,      0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x80
        ,      0x00, 0x00, 0x00, 0x0C, 0x80, 0x00, 0x00, 0x00
        ,      0x00, 0x40, 0x40, 0x80, 0x41, 0x02, 0x04, 0x0C
        ,      0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0x41
        ,      0x83, 0x00, 0x0C, 0x48, 0x08, 0x00, 0x00, 0x00
        ,      0x00, 0x04, 0xC1, 0x83, 0x02, 0x0C, 0x3C, 0x98
        ,      0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x02
        ,      0x00, 0x94, 0x00, 0x4C, 0x60, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x80, 0x00, 0x48, 0x28, 0x00, 0x04
        ,      0x60, 0x08, 0x00, 0x00, 0x00, 0x00, 0x84, 0x0C
        ,      0x84, 0x28, 0x00, 0x04, 0x30, 0x80, 0x00, 0x00
        ,      0x00, 0x00, 0x48, 0x0C, 0x08, 0x2C, 0x00, 0x00
        ,      0x30, 0x80, 0x00, 0x00, 0x00, 0x00, 0x04, 0xC0
        ,      0x00, 0x6C, 0x08, 0x00, 0x18, 0x80, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x40, 0x00, 0x9C, 0x8C, 0x00
        ,      0x18, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40
        ,      0x00, 0x9C, 0x8C, 0x00, 0x18, 0x80, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x40, 0x1C, 0x2D, 0x6C, 0x0C
        ,      0x90, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40
        ,      0xCC, 0x16, 0x6C, 0x60, 0x30, 0x80, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x04, 0xC1, 0xCC, 0xCC, 0x30
        ,      0x60, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0x48, 0xC4, 0x9C, 0x98, 0x84, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x9C, 0x6C
        ,      0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0x48, 0xD4, 0xFC, 0xF8, 0xE0, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0xD4, 0xFC, 0xF8, 0xF0
        ,      0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0x48, 0xC0, 0xC0, 0xC0, 0x08, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x84
        ,      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x48, 0xE8, 0x08, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x04, 0xC0, 0xC0, 0xD4, 0xFC
        ,      0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xC0
        ,      0x08, 0x0C, 0x5C, 0xF8, 0xE0, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x48, 0x08, 0x00, 0x00, 0x48, 0xF0
        ,      0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00
        ,      0x00, 0x00, 0x04, 0xD0, 0x80, 0x00, 0x00, 0x00
        ,      0x00, 0x04, 0x80, 0x00, 0x00, 0x00, 0x04, 0x48
        ,      0x08, 0x00, 0x00, 0x00, 0x00, 0x40, 0x48, 0x80
        ,      0x00, 0x00, 0x00, 0x0C, 0x80, 0x00, 0x00, 0x00
        ,      0x00, 0x40, 0x40, 0x80, 0x00, 0x00, 0x00, 0x0C
        ,      0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x80
        ,      0x41, 0x02, 0x04, 0x0C, 0x80, 0x00, 0x00, 0x00
        ,      0x00, 0x40, 0x08, 0x41, 0x83, 0x00, 0x0C, 0x48
        ,      0x08, 0x00, 0x00, 0x00, 0x00, 0x04, 0xC1, 0x83
        ,      0x02, 0x0C, 0x3C, 0x98, 0x84, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x81, 0x02, 0x00, 0x94, 0x6C, 0x6C
        ,      0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00
        ,      0x48, 0x28, 0x08, 0xCC, 0x60, 0x08, 0x00, 0x00
        ,      0x00, 0x00, 0x84, 0x0C, 0x84, 0x00, 0x00, 0x4C
        ,      0x30, 0x80, 0x00, 0x00, 0x00, 0x00, 0x48, 0x0C
        ,      0x08, 0x00, 0x00, 0x4C, 0x30, 0x80, 0x00, 0x00
        ,      0x00, 0x00, 0x04, 0xC0, 0x00, 0x00, 0x00, 0x90
        ,      0x30, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40
        ,      0x00, 0x00, 0x04, 0x90, 0x30, 0x80, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x90
        ,      0x30, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40
        ,      0x1C, 0x00, 0x48, 0x30, 0x98, 0x80, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x40, 0xCC, 0x0C, 0x90, 0x64
        ,      0x60, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04
        ,      0xC1, 0xCC, 0x9C, 0x6C, 0x98, 0x84, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x40, 0xCC, 0x30, 0xCC, 0x1E
        ,      0xCC, 0x60, 0x08, 0x00, 0x00, 0x00, 0x00, 0x40
        ,      0xCC, 0x30, 0xC4, 0xCC, 0x30, 0xF0, 0x80, 0x00
        ,      0x00, 0x00, 0x00, 0x40, 0x98, 0x30, 0xC0, 0x90
        ,      0xFC, 0xF0, 0x80, 0x00, 0x00, 0x00, 0x48, 0xC0
        ,      0xFC, 0xE0, 0x48, 0xD4, 0xF8, 0xE0, 0x08, 0x00
        ,      0x00, 0x00, 0xD4, 0xFC, 0xF8, 0xE0, 0xD4, 0xFC
        ,      0xE0, 0x84, 0x00, 0x00, 0x00, 0x00, 0x48, 0xC0
        ,      0xC0, 0x84, 0x48, 0xC0, 0x84, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0x48, 0x08, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04
        ,      0xD4, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x48, 0xFC, 0xE8, 0xC0, 0xC0
        ,      0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0
        ,      0xF4, 0xAC, 0x0C, 0x04, 0xC0, 0x08, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x48, 0xF0, 0x84, 0x00, 0x00
        ,      0x04, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40
        ,      0xE0, 0x08, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x04, 0x84, 0x08, 0x00, 0x00
        ,      0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x40
        ,      0x0C, 0x00, 0x00, 0x00, 0x40, 0x84, 0x80, 0x00
        ,      0x00, 0x00, 0x00, 0x40, 0x0C, 0x00, 0x00, 0x00
        ,      0x40, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40
        ,      0x0C, 0x08, 0x01, 0x82, 0x40, 0x80, 0x80, 0x00
        ,      0x00, 0x00, 0x00, 0x04, 0x84, 0x0C, 0x00, 0x43
        ,      0x82, 0x04, 0x80, 0x00, 0x00, 0x00, 0x00, 0x48
        ,      0x64, 0xCC, 0x0C, 0x01, 0x43, 0xC2, 0x08, 0x00
        ,      0x00, 0x00, 0x04, 0x80, 0x00, 0x3C, 0xC8, 0x00
        ,      0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00
        ,      0x00, 0x14, 0x4E, 0x84, 0x00, 0x40, 0x00, 0x00
        ,      0x00, 0x04, 0x84, 0x00, 0x00, 0x1C, 0x2C, 0x48
        ,      0x0C, 0x48, 0x00, 0x00, 0x00, 0x40, 0x08, 0x00
        ,      0x04, 0x9C, 0x28, 0x04, 0x0C, 0x84, 0x00, 0x00
        ,      0x00, 0x40, 0x08, 0x00, 0x18, 0x3C, 0x28, 0x00
        ,      0xC0, 0x08, 0x00, 0x00, 0x00, 0x40, 0x08, 0x0C
        ,      0x64, 0x2D, 0x2C, 0x00, 0x80, 0x00, 0x00, 0x00
        ,      0x00, 0x04, 0x84, 0x18, 0xCC, 0x2D, 0x6C, 0x00
        ,      0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x30
        ,      0xCC, 0x3C, 0x1E, 0x8C, 0x48, 0x00, 0x00, 0x00
        ,      0x00, 0x04, 0x90, 0x90, 0x64, 0x9C, 0x0B, 0xCC
        ,      0x40, 0x00, 0x00, 0x00, 0x00, 0x40, 0x30, 0xC0
        ,      0x30, 0x9C, 0x4E, 0x86, 0x48, 0x00, 0x00, 0x00
        ,      0x00, 0x48, 0x30, 0x60, 0x90, 0xCC, 0x3C, 0xC8
        ,      0x84, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xB0, 0x64
        ,      0xC8, 0x64, 0x6C, 0xC8, 0x00, 0x00, 0x00, 0x00
        ,      0x00, 0xD0, 0xF0, 0xC8, 0x84, 0x90, 0x64, 0xC0
        ,      0x08, 0x00, 0x00, 0x00, 0x00, 0x48, 0xF0, 0xF0
        ,      0xE0, 0xD0, 0xF4, 0xFC, 0xC0, 0x08, 0x00, 0x00
        ,      0x00, 0x04, 0xD0, 0xF0, 0xF0, 0xD0, 0xF0, 0xFC
        ,      0xFC, 0x80, 0x00, 0x00, 0x00, 0x00, 0x48, 0xC0
        ,      0xC0, 0x48, 0xC0, 0xC0, 0xC0, 0x08, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0x48, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x04, 0xD4, 0x84, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48
        ,      0xFC, 0xE8, 0xC0, 0xC0, 0x08, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0xD0, 0xF4, 0xAC, 0x0C, 0x04
        ,      0xC0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48
        ,      0xF0, 0x84, 0x00, 0x00, 0x04, 0x84, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x40, 0xE0, 0x08, 0x00, 0x00
        ,      0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04
        ,      0x84, 0x08, 0x00, 0x00, 0x00, 0x40, 0x08, 0x00
        ,      0x00, 0x00, 0x00, 0x40, 0x0C, 0x00, 0x00, 0x00
        ,      0x40, 0x84, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40
        ,      0x0C, 0x00, 0x00, 0x00, 0x40, 0x80, 0x80, 0x00
        ,      0x00, 0x00, 0x00, 0x40, 0x0C, 0x08, 0x01, 0x82
        ,      0x40, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x04
        ,      0x84, 0x0C, 0x00, 0x43, 0x82, 0x04, 0x80, 0x00
        ,      0x00, 0x00, 0x00, 0x48, 0x64, 0x3C, 0x0C, 0x01
        ,      0x43, 0xC2, 0x08, 0x00, 0x00, 0x00, 0x00, 0x90
        ,      0x8C, 0x00, 0x68, 0x00, 0x01, 0x42, 0x00, 0x00
        ,      0x00, 0x00, 0x04, 0x90, 0x08, 0x00, 0x14, 0x84
        ,      0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x40, 0x30
        ,      0x08, 0x00, 0x14, 0x48, 0x0C, 0x48, 0x00, 0x00
        ,      0x00, 0x00, 0x40, 0x30, 0x00, 0x00, 0x1C, 0x04
        ,      0x0C, 0x84, 0x00, 0x00, 0x00, 0x00, 0x40, 0x24
        ,      0x00, 0x04, 0x9C, 0x00, 0xC0, 0x08, 0x00, 0x00
        ,      0x00, 0x00, 0x40, 0x24, 0x00, 0x4C, 0x6C, 0x00
        ,      0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x24
        ,      0x00, 0x4C, 0x6C, 0x00, 0x80, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x40, 0x60, 0x0C, 0x9C, 0x1E, 0x2C
        ,      0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x30
        ,      0x90, 0x9C, 0x29, 0xCC, 0x80, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x04, 0x90, 0x30, 0xCC, 0xCC, 0xC2
        ,      0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48
        ,      0x64, 0x6C, 0xC8, 0x84, 0x00, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x48, 0x9C, 0x6C, 0x84, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0
        ,      0xF4, 0xFC, 0xE8, 0x84, 0x00, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x48, 0xF0, 0xF4, 0xFC, 0xE8
        ,      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04
        ,      0xC0, 0xC0, 0xC0, 0x84, 0x00, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0x48, 0x08, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04
        ,      0xD4, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x48, 0xFC, 0xE8, 0xC0, 0xC0
        ,      0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0
        ,      0xF4, 0xAC, 0x0C, 0x04, 0xC0, 0x08, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x48, 0xF0, 0x84, 0x00, 0x00
        ,      0x04, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40
        ,      0xE0, 0x08, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00
        ,      0x00, 0x00, 0x00, 0x04, 0x84, 0x08, 0x00, 0x00
        ,      0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x40
        ,      0x0C, 0x00, 0x00, 0x00, 0x40, 0x84, 0x80, 0x00
        ,      0x00, 0x00, 0x00, 0x40, 0x0C, 0x00, 0x00, 0x00
        ,      0x40, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40
        ,      0x0C, 0x08, 0x01, 0x82, 0x40, 0x80, 0x80, 0x00
        ,      0x00, 0x00, 0x00, 0x04, 0x84, 0x0C, 0x00, 0x43
        ,      0x82, 0x04, 0x80, 0x00, 0x00, 0x00, 0x00, 0x48
        ,      0x64, 0x3C, 0x0C, 0x01, 0x43, 0xC2, 0x08, 0x00
        ,      0x00, 0x00, 0x00, 0x90, 0x9C, 0x9C, 0x68, 0x00
        ,      0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x04, 0x90
        ,      0xCC, 0x04, 0x14, 0x84, 0x00, 0x40, 0x00, 0x00
        ,      0x00, 0x00, 0x40, 0x30, 0x8C, 0x00, 0x00, 0x48
        ,      0x0C, 0x48, 0x00, 0x00, 0x00, 0x00, 0x40, 0x30
        ,      0x8C, 0x00, 0x00, 0x04, 0x0C, 0x84, 0x00, 0x00
        ,      0x00, 0x00, 0x40, 0x30, 0x60, 0x00, 0x00, 0x00
        ,      0xC0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x40, 0x30
        ,      0x60, 0x08, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x40, 0x30, 0x60, 0x80, 0x00, 0x00
        ,      0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x64
        ,      0x30, 0x84, 0x00, 0x2C, 0x80, 0x00, 0x00, 0x00
        ,      0x00, 0x00, 0x04, 0x90, 0x98, 0x60, 0x0C, 0xCC
        ,      0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x64
        ,      0x9C, 0x6C, 0xCC, 0xC2, 0x08, 0x00, 0x00, 0x00
        ,      0x00, 0x04, 0x90, 0xCC, 0x2D, 0xCC, 0x30, 0xCC
        ,      0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0xF0, 0x30
        ,      0xCC, 0xC8, 0x30, 0xCC, 0x80, 0x00, 0x00, 0x00
        ,      0x00, 0x40, 0xF0, 0xFC, 0x60, 0xC0, 0x30, 0x64
        ,      0x80, 0x00, 0x00, 0x00, 0x00, 0x04, 0xD0, 0xF4
        ,      0xE8, 0x84, 0xD0, 0xFC, 0xC0, 0x84, 0x00, 0x00
        ,      0x00, 0x00, 0x48, 0xD0, 0xFC, 0xE8, 0xD0, 0xF4
        ,      0xFC, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48
        ,      0xC0, 0x84, 0x48, 0xC0, 0xC0, 0x84, 0x00, 0x00
};

 

Para nuestro programa utilizaremos las rutinas que ya vimos en el anterior tutorial Sprites I: Introducción al uso de sprites para poner la paleta y pintar los sprites. Vamos a hacer que se mueva de izquierda a derecha el personaje, intercalando los graficos que hemos exportado, para simular que el personaje camina. Para hacerlo de manera sencilla, vamos a introducir varios conceptos más avanzados de c como son enumeraciones, estructuras y punteros a puntero. Por ejemplo, para simplificar el sentido del movimiento usaremos el siguiente enum:

enum _eDirection
{
  Direction_Left,
  Direction_Right,
  Direction_Max
};

 

Para manejar el personaje, su posición, movimiento y animación usaremos la siguiente estructura:

struct _tSprite
{
  int nY;
  int nX;
  int nKey;
  unsigned int nSpeed;
  unsigned int nLastMoveTime;
  enum _eDirection eDirection;
}_tSprite;

 

Para manejar y almacenar los distintos 'frames' de la animación vamos a usar este array que apunta a cada uno de los 4 frames tanto para izquierda como para derecha:

char *aKeyFrames[Direction_Max][4];

Que rellenaremos de la siguiente manera:

aKeyFrames[Direction_Left][0] = (char *)NickSprite;
aKeyFrames[Direction_Left][1] = (char *)NickSprite + NICK_WIDTH * NICK_HEIGHT;
aKeyFrames[Direction_Left][2] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 2;
aKeyFrames[Direction_Left][3] = (char *)NickSprite + NICK_WIDTH * NICK_HEIGHT;
aKeyFrames[Direction_Right][0] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 3;
aKeyFrames[Direction_Right][1] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 4;
aKeyFrames[Direction_Right][2] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 5;
aKeyFrames[Direction_Right][3] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 4;

A pesar de que tenemos 3 graficos diferentes para cada sentido, hemos puesto 4 frames, esto es así ya que la secuencia queda de la siguiente forma: Pie izquierdo delante, pies juntos, pie derecho delante, pies juntos y luego volvería a empezar.

Finalmente el código fuente entero del programa queda de la siguiente manera:

////////////////////////////////////////////////////////////////////////
// sprite01.c
// Mochilote - www.cpcmania.com
////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Nick.h"

#define MAX_X 79
#define MAX_Y 199

void SetColor(unsigned char nColorIndex, unsigned char nPaletteIndex)
{
  __asm
    ld a, 4 (ix)
    ld b, 5 (ix)
    ld c, b
    call #0xBC32 ;SCR SET INK
    __endasm;
}

void SetPalette(const unsigned char *pPalette)
{
  unsigned char nColor = 0;

  for(nColor = 0; nColor < NUM_COLORS; nColor++)
    SetColor(nColor, pPalette[nColor]);
}

void PutSpriteMode0(unsigned char *pAddress, unsigned char nWidth, unsigned char nHeight, unsigned char *pSprite)
{
  __asm
    LD L, 4(IX) 
    LD H, 5(IX) 
    LD C, 6(IX) 
    LD B, 7(IX)            
    LD E, 8(IX) 
    LD D, 9(IX) 

    _loop_alto:
       PUSH BC
       LD B,C
       PUSH HL
    _loop_ancho:
       LD A,(DE)
       LD (HL),A
       INC DE
       INC HL
       DJNZ _loop_ancho
       POP HL
       LD A,H
       ADD #0x08
       LD H,A
       SUB #0xC0
       JP NC, _sig_linea
       LD BC, #0xC050
       ADD HL,BC
    _sig_linea:
       POP BC
       DJNZ _loop_alto
  __endasm;
}


////////////////////////////////////////////////////////////////////////
unsigned char char1,char2,char3,char4;

unsigned int GetTime()
{
  unsigned int nTime = 0;

  __asm
    CALL #0xBD0D ;KL TIME PLEASE
    PUSH HL
    POP DE
    LD HL, #_char3
    LD (HL), D
    LD HL, #_char4
    LD (HL), E
  __endasm;

  nTime = (char3 << 8) + char4;

  return nTime;
}
////////////////////////////////////////////////////////////////////////

enum _eDirection
{
  Direction_Left,
  Direction_Right,
  Direction_Max
};

char *aKeyFrames[Direction_Max][4];

struct _tSprite
{
  int nY;
  int nX;
  int nKey;
  unsigned int nSpeed;
  unsigned int nLastMoveTime;
  enum _eDirection eDirection;
}_tSprite;

#define NUM_SPRITES 1

struct _tSprite aSprites[NUM_SPRITES];

void main()
{
  //SCR_SET_MODE 0
  __asm
    ld a, #0
    call #0xBC0E
  __endasm;

  //SCR SET BORDER 0
  __asm
    ld b, #0 ;black
    ld c, b
    call #0xBC38
  __endasm;
  
  SetPalette(NickPalette);

  aKeyFrames[Direction_Left][0] = (char *)NickSprite;
  aKeyFrames[Direction_Left][1] = (char *)NickSprite + NICK_WIDTH * NICK_HEIGHT;
  aKeyFrames[Direction_Left][2] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 2;
  aKeyFrames[Direction_Left][3] = (char *)NickSprite + NICK_WIDTH * NICK_HEIGHT;
  aKeyFrames[Direction_Right][0] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 3;
  aKeyFrames[Direction_Right][1] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 4;
  aKeyFrames[Direction_Right][2] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 5;
  aKeyFrames[Direction_Right][3] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 4;

  aSprites[0].eDirection = Direction_Right;
  aSprites[0].nKey = 0;
  aSprites[0].nX = 0;
  aSprites[0].nY = 75;
  aSprites[0].nSpeed = 20;
  aSprites[0].nLastMoveTime = GetTime();

  while(1)
  {
    unsigned char nSprite = 0;

    for(nSprite = 0; nSprite < NUM_SPRITES; nSprite++)
    {
      if(GetTime() - aSprites[nSprite].nLastMoveTime < aSprites[nSprite].nSpeed)
        continue;

      aSprites[nSprite].nLastMoveTime += aSprites[nSprite].nSpeed;

      //move
      aSprites[nSprite].nX += (aSprites[nSprite].eDirection == Direction_Right ? 1 : -1);

      if(aSprites[nSprite].nX <= 0)
      {
        aSprites[nSprite].nX = 0;
        aSprites[nSprite].eDirection = Direction_Right;
        aSprites[nSprite].nKey = 0;
      }

      if(aSprites[nSprite].nX >= (MAX_X - NICK_WIDTH))
      {
        aSprites[nSprite].nX = MAX_X - NICK_WIDTH;
        aSprites[nSprite].eDirection = Direction_Left;
        aSprites[nSprite].nKey = 0;
      }

      aSprites[nSprite].nKey++;

      if(aSprites[nSprite].nKey > 3)
        aSprites[nSprite].nKey = 0;

      PutSpriteMode0((unsigned char *)(0xC000 + ((aSprites[nSprite].nY / 8u) * 80u) + 
        ((aSprites[nSprite].nY % 8u) * 2048u) + aSprites[nSprite].nX),
        NICK_WIDTH, NICK_HEIGHT, (unsigned char *)aKeyFrames[aSprites[nSprite].eDirection][aSprites[nSprite].nKey]);
    }
  }
}
////////////////////////////////////////////////////////////////////////

Si compilamos y ejecutamos en el emulador obtenemos lo siguiente:

 

Vamos ahora a modificar mínimamente el programa, para que maneje 4 personajes simultáneamente, con 4 velocidades diferentes:

////////////////////////////////////////////////////////////////////////
// sprite02.c
// Mochilote - www.cpcmania.com
////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Nick.h"

#define MAX_X 79
#define MAX_Y 199

void SetColor(unsigned char nColorIndex, unsigned char nPaletteIndex)
{
  __asm
    ld a, 4 (ix)
    ld b, 5 (ix)
    ld c, b
    call #0xBC32 ;SCR SET INK
    __endasm;
}

void SetPalette(const unsigned char *pPalette)
{
  unsigned char nColor = 0;

  for(nColor = 0; nColor < NUM_COLORS; nColor++)
    SetColor(nColor, pPalette[nColor]);
}

void PutSpriteMode0(unsigned char *pAddress, unsigned char nWidth, unsigned char nHeight, unsigned char *pSprite)
{
  __asm
    LD L, 4(IX) 
    LD H, 5(IX) 
    LD C, 6(IX) 
    LD B, 7(IX)            
    LD E, 8(IX) 
    LD D, 9(IX) 

    _loop_alto:
       PUSH BC
       LD B,C
       PUSH HL
    _loop_ancho:
       LD A,(DE)
       LD (HL),A
       INC DE
       INC HL
       DJNZ _loop_ancho
       POP HL
       LD A,H
       ADD #0x08
       LD H,A
       SUB #0xC0
       JP NC, _sig_linea
       LD BC, #0xC050
       ADD HL,BC
    _sig_linea:
       POP BC
       DJNZ _loop_alto
  __endasm;
}


////////////////////////////////////////////////////////////////////////
unsigned char char1,char2,char3,char4;

unsigned int GetTime()
{
  unsigned int nTime = 0;

  __asm
    CALL #0xBD0D ;KL TIME PLEASE
    PUSH HL
    POP DE
    LD HL, #_char3
    LD (HL), D
    LD HL, #_char4
    LD (HL), E
  __endasm;

  nTime = (char3 << 8) + char4;

  return nTime;
}
////////////////////////////////////////////////////////////////////////

enum _eDirection
{
  Direction_Left,
  Direction_Right,
  Direction_Max
};

char *aKeyFrames[Direction_Max][4];

struct _tSprite
{
  int nY;
  int nX;
  int nKey;
  unsigned int nSpeed;
  unsigned int nLastMoveTime;
  enum _eDirection eDirection;
}_tSprite;

#define NUM_SPRITES 4

struct _tSprite aSprites[NUM_SPRITES];

void main()
{
  //SCR_SET_MODE 0
  __asm
    ld a, #0
    call #0xBC0E
  __endasm;

  //SCR SET BORDER 0
  __asm
    ld b, #0 ;black
    ld c, b
    call #0xBC38
  __endasm;
  
  SetPalette(NickPalette);

  aKeyFrames[Direction_Left][0] = (char *)NickSprite;
  aKeyFrames[Direction_Left][1] = (char *)NickSprite + NICK_WIDTH * NICK_HEIGHT;
  aKeyFrames[Direction_Left][2] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 2;
  aKeyFrames[Direction_Left][3] = (char *)NickSprite + NICK_WIDTH * NICK_HEIGHT;
  aKeyFrames[Direction_Right][0] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 3;
  aKeyFrames[Direction_Right][1] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 4;
  aKeyFrames[Direction_Right][2] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 5;
  aKeyFrames[Direction_Right][3] = (char *)NickSprite + (NICK_WIDTH * NICK_HEIGHT) * 4;

  aSprites[0].eDirection = Direction_Right;
  aSprites[0].nKey = 0;
  aSprites[0].nX = 0;
  aSprites[0].nY = 25;
  aSprites[0].nSpeed = 20;
  aSprites[0].nLastMoveTime = GetTime();

  aSprites[1].eDirection = Direction_Left;
  aSprites[1].nKey = 0;
  aSprites[1].nX = 50;
  aSprites[1].nY = 25;
  aSprites[1].nSpeed = 15;
  aSprites[1].nLastMoveTime = GetTime();

  aSprites[2].eDirection = Direction_Right;
  aSprites[2].nKey = 0;
  aSprites[2].nX = 0;
  aSprites[2].nY = 75;
  aSprites[2].nSpeed = 5;
  aSprites[2].nLastMoveTime = GetTime();

  aSprites[3].eDirection = Direction_Right;
  aSprites[3].nKey = 0;
  aSprites[3].nX = 0;
  aSprites[3].nY = 150;
  aSprites[3].nSpeed = 10;
  aSprites[3].nLastMoveTime = GetTime();

  while(1)
  {
    unsigned char nSprite = 0;

    for(nSprite = 0; nSprite < NUM_SPRITES; nSprite++)
    {
      if(GetTime() - aSprites[nSprite].nLastMoveTime < aSprites[nSprite].nSpeed)
        continue;

      aSprites[nSprite].nLastMoveTime += aSprites[nSprite].nSpeed;

      //move
      aSprites[nSprite].nX += (aSprites[nSprite].eDirection == Direction_Right ? 1 : -1);

      if(aSprites[nSprite].nX <= 0)
      {
        aSprites[nSprite].nX = 0;
        aSprites[nSprite].eDirection = Direction_Right;
        aSprites[nSprite].nKey = 0;
      }

      if(aSprites[nSprite].nX >= (MAX_X - NICK_WIDTH))
      {
        aSprites[nSprite].nX = MAX_X - NICK_WIDTH;
        aSprites[nSprite].eDirection = Direction_Left;
        aSprites[nSprite].nKey = 0;
      }

      aSprites[nSprite].nKey++;

      if(aSprites[nSprite].nKey > 3)
        aSprites[nSprite].nKey = 0;

      PutSpriteMode0((unsigned char *)(0xC000 + ((aSprites[nSprite].nY / 8u) * 80u) + 
        ((aSprites[nSprite].nY % 8u) * 2048u) + aSprites[nSprite].nX),
        NICK_WIDTH, NICK_HEIGHT, (unsigned char *)aKeyFrames[aSprites[nSprite].eDirection][aSprites[nSprite].nKey]);
    }
  }
}
////////////////////////////////////////////////////////////////////////

Si compilamos y ejecutamos en el emulador obtenemos lo siguiente:

Como vemos, con apenas 3 graficos hemos hecho caminar a nuestro personaje e incluso a diferentes velocidades. Si nos fijamos en los dos personajes de la parte de arriba, vemos que al chocar entre ellos se produce un efecto muy típico en muchos juegos en nuestro cpc. Podéis bajar un zip con todos ficheros (código fuente, bat's para compilar, binarios y dsk's) aquí: Sprites_II.zip

 

www.CPCMania.com 2012