top of page

Primeros pasos de Sam en SCREEN 2 – Tiles y colisiones (Parte 1)

  • Foto del escritor: MoltS Xalats
    MoltS Xalats
  • 10 ago
  • 13 Min. de lectura
Imagen: Pantalla principal
Imagen: Pantalla principal

Introducción


Después del artículo Creando y moviendo sprites, donde Sam tomaba forma por primera vez, ahora lo dejamos salir a explorar su mundo: un escenario de tiles de 8x8 construido en el modo SCREEN 2. Nuestro gato protagonista da sus primeros pasos entre paredes, agua y pelotas amarillas, rebotando a izquierda y derecha mientras descubrimos cómo dibujar escenarios, animarlo y detectar colisiones. Esto apenas comienza… ¡y sí, el agua no le gusta nada!


Objectivo del artículo


En este primer paseo de Sam por el mundo de SCREEN 2, aprenderemos a:


  • Construir un escenario con tiles de 8x8, para que nuestro gato pueda caminar con estilo.

  • Cargar en la VRAM todo lo necesario: patrones, colores y la tabla que indica qué tile va en cada lugar.

  • Hacer que Sam se mueva de izquierda a derecha, animado y atento a las paredes con las que no quiere chocar.

  • Dejarlo todo listo para la siguiente aventura: hacer scroll horizontal y ampliar su mundo.


¿Qué es SCREEN 2?


SCREEN 2 es uno de los modos gráficos clásicos de MSX, ideal para crear videojuegos con escenarios modulares, es decir, construidos a partir de pequeñas piezas gráficas (tiles) reutilizables que permiten montar el escenario de forma eficiente. Trabaja con una resolución de 256×192 píxeles, organizada en una cuadrícula de 32×24 tiles de 8x8 píxeles cada uno.


A diferencia de otros modos más modernos como SCREEN 5, aquí no pintamos píxeles uno por uno, sino que construimos la pantalla a partir de tiles, pequeñas piezas gráficas reutilizables.


Patrones y tilemap: el mundo en mosaico


Un patrón es un gráfico de 8x8 píxeles, generalmente monocromático con un segundo color asignado mediante la tabla de color. En SCREEN 2 podemos tener hasta 768 patrones diferentes divididos en tres bancos de 256. Estos patrones no tienen una posición fija, sino que se colocan en pantalla a través de la tabla de nombres (Name Table).


Cada tile en pantalla es, por lo tanto:


  • Un gráfico (Pattern)

  • Un color asociado (Color Table)

  • Una posición (Name Table)


Colores de los patrones


SCREEN 2 permite definir un color de primer plano (foreground) y un color de fondo (background) para cada tile. Esta información se guarda en la tabla de colores (Color Table) de la VRAM.


Cada byte de esta tabla contiene:


  • Los 4 bits más altos (nibble alto) → color de fondo

  • Los 4 bits más bajos (nibble bajo) → color de dibujo (foreground)


Ejemplo 1:


Si un byte tiene el valor 0x47, esto significa:


  • Color de fondo: 4 (azul)

  • Color de primer plano: 7 (azul claro)


Cuando se dibuja un tile, cada línea de 8 píxeles proviene de un byte del patrón (Pattern Table):


  • Cada bit 0 indica “pinta con el color de fondo”

  • Cada bit 1 indica “pinta con el color de primer plano”


Esto nos permite usar un mismo tile con diferentes combinaciones de colores, ahorrando espacio y haciendo más variado el escenario gráfico.


Ejemplo 2:


Imagen: Pelota
Imagen: Pelota

0b00111100   //   ..####..
0b01111110   //   .######.
0b11011011   //   ##.##.##
0b11111111   //   ########
0b11111111   //   ########
0b11011011   //   ##.##.##
0b01111110   //   .######.
0b00111100   //   ..####..

unsigned char ball_pattern[8] = {
  0x3C, 0x7E, 0xDB, 0xFF,
  0xFF, 0xDB, 0x7E, 0x3C
};

Estructura de memoria en SCREEN 2


SCREEN 2 divide la VRAM en tres zonas principales:


Zona VRAM

Contenido

Dirección inicial

Pattern

Gráficos de los tiles (8x8)

0x0000

Color

Color de cada tile

0x2000

Name

Qué tile va en cada posición XY

0x1800

Tabla: División de memoria VRAM


Cada tile de pantalla apunta a un número de patrón del 0 al 255 (por banco). Esto nos permite reutilizar gráficos fácilmente y generar escenarios modulares.


¿Y la pantalla… se divide?


¡Sí! Una particularidad interesante de SCREEN 2 es que podemos dividir la pantalla en tres secciones horizontales de 8 líneas de tiles cada una (8 líneas × 8 píxeles = 64 píxeles). Esto se consigue asignando diferentes bancos de patrones a cada tercio de pantalla.


Esta técnica es muy útil para:


  • Crear HUDs o paneles informativos solo en la parte inferior

  • Separar escenario jugable e información del juego

  • Reutilizar patrones sin conflictos entre áreas


Imagen: Secciones horizontales SCREEN 2
Imagen: Secciones horizontales SCREEN 2

¿Sabías que…?


La división de la pantalla en tres tercios en SCREEN 2 no es solo una cuestión visual: es una optimización pensada para la memoria del MSX y su procesador Z80, una CPU de 8 bits.


Cada terç (64 línies) pot utilitzar un banc de 256 patrons (tiles), cosa que encaixa perfectament amb un byte (de 0 a 255). Aquesta divisió fa més fàcil i ràpid que el sistema gràfic (el VDP) accedeixi als patrons i colors, estalviant càlculs i temps en una màquina amb recursos molt limitats.

 

Cada tercio (64 líneas) puede utilizar un banco de 256 patrones (tiles), lo que encaja perfectamente con un byte (de 0 a 255). Esta división facilita y acelera el acceso del sistema gráfico (el VDP) a los patrones y colores, ahorrando cálculos y tiempo en una máquina con recursos muy limitados.


Gracias a ello, con solo 8 bits puedes gestionar escenarios modulares y reutilizables en una pantalla de 192 líneas… ¡todo un ejemplo de ingeniería retro!


Construyendo el mapa de tiles


Nuestro escenario se define con una matriz bidimensional llamada tilemap[15][32]. Esta matriz actúa como un plano que indica qué tile debe ir en cada posición de la cuadrícula de 32 columnas por 15 filas (recuerda que SCREEN 2 permite hasta 24 filas de tiles, pero usamos solo 15 para dejar margen para HUDs o experimentos futuros).


const unsigned char tilemap[MAP_HEIGHT][MAP_WIDTH] = {
  {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,1},  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,1},  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,1},  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,1},  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,1},  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,1},  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1},  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},  {1,0,0,0,0,0,0,0,0,0,0,0,3,0,3,0,3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
}; 

Esta matriz contiene valores numéricos que representan los distintos tipos de tile disponibles. En nuestro ejemplo, hemos definido:


0

Fondo negro

1

Pared

2

Agua

3

Pelota

Tabla: Tipos de tiles


Para dibujar este mapa en pantalla, recorremos la matriz y, para cada posición, llamamos a la función print_tile(x, y, tile_id), que escribe directamente en la VRAM mediante Vpoke().


void draw_tilemap() {
  for (int y = 0; y < 15; y++) {
    for (int x = 0; x < 32; x++) {
      print_tile(x, y, tilemap[y][x]);
    }
  }
}

Esta función copia el contenido del tilemap desde la RAM a la tabla de nombres (Name Table) de la VRAM. Es importante entender que definir el mapa en RAM no es suficiente: es necesario realizar explícitamente la transferencia a vídeo.


Sam entra en acción


Una vez hemos dibujado el escenario con tiles, es hora de que entre en juego nuestro protagonista: Sam.


Sam es un sprite de 16×16 píxeles, formado por cuatro sprites de 8×8 colocados uno al lado del otro. En este primer ejemplo lo haremos caminar automáticamente de izquierda a derecha y de derecha a izquierda, rebotando cuando encuentra una pared.


El movimiento se hace paso a paso y la detección de colisiones es muy sencilla: antes de mover a Sam, consultamos qué tile hay en su posición futura con la función get_tile(x, y).


unsigned char get_tile(char x, char y) {
    unsigned int vram_address = 0x1800 + (y * 32) + x;
    return Vpeek(vram_address);
}

Si el tile es una pared (valor 1), Sam cambia de dirección:


if (tile == 1) {
  d = -1; // Change Direction to the Left
}

Y cuando vuelve a chocar con una pared en el otro lado…


if (tile == 1) {
  d = +1; // Change Direction to the Right
}

La variable d nos indica la dirección del movimiento:


  • Si d = +1, Sam se mueve hacia la derecha

  • Si d = -1, Sam se mueve hacia la izquierda


En cada fotograma, incrementamos o decrementamos la coordenada X según este valor:


Así conseguimos un movimiento rebotador, muy típico de los juegos clásicos: Sam va y viene por la pantalla mientras el escenario permanece estable.


x = x + d;

Carga de patrones y colores


SCREEN 2 permite tener hasta 768 patrones (gráficos de tiles) y sus colores asociados, distribuidos en tres bancos de 256. Cada tile ocupa 8 bytes y se repite en tres secciones de VRAM para garantizar compatibilidad con toda la pantalla.


Para cargar estos patrones y colores, utilizamos dos funciones:


Patrones gráficos


void load_patterns(void) {
  unsigned int base_addresses[TILE_REPEAT] = {0x0000, 0x0800, 0x1000};
  int section, tile, i;

  for (section = 0; section < TILE_REPEAT; section++) {
    VpokeFirst(base_addresses[section]);
    for (tile = 0; tile < TILE_COUNT; tile++) {
      for (i = 0; i < TILE_SIZE; i++) {
        VpokeNext(patterns[tile][i]);
      }
    }
  }
}

Colores asociados


void load_colors(void) {
  unsigned int base_addresses[TILE_REPEAT] = {0x2000, 0x2800, 0x3000};
  int section, tile, i;

  for (section = 0; section < TILE_REPEAT; section++) {
    VpokeFirst(base_addresses[section]);
    for (tile = 0; tile < TILE_COUNT; tile++) {
      for (i = 0; i < TILE_SIZE; i++) {
        VpokeNext(colors[tile][i]);
      }
    }
  }
}

Estas dos funciones escriben, tres veces, los patrones y colores en la VRAM para cubrir los tres tercios horizontales de la pantalla (64 líneas cada uno). Esto asegura que todos los tiles se muestren correctamente, independientemente de su posición horizontal.


¿Y por qué son 8 bytes por tile?


Cada tile de SCREEN 2 es un cuadrado de 8×8 píxeles. Como cada línea de 8 píxeles se representa con 1 byte (8 bits), se necesitan 8 bytes por tile:


  • 1 byte por línea de 8 píxeles

  • 8 líneas por tile

    ⇒ 8 × 1 = 8 bytes por tile


Este formato se aplica tanto a la Pattern Table (que define la forma del tile) como a la Color Table (que define el color foreground y background de cada línea).


Por eso, un banco completo de 256 tiles ocupa:


  • 256 × 8 = 2048 bytes (2 KB) para patrones

  • 256 × 8 = 2 KB para colores


Todo encaja como un guante en las regiones definidas en VRAM:


  • 0x0000–0x17FF → Patrones (3 × 2 KB)

  • 0x2000–0x37FF → Colores (3 × 2 KB)


Concepto clave: tilemap en RAM vs. VRAM


Cuando definimos un mapa de tiles en nuestro código, normalmente lo hacemos en una variable de tipo matriz, como por ejemplo:


unsigned char tilemap[15][32];

Este tilemap está en la RAM del MSX, es decir, en la memoria principal donde se ejecuta el código. Pero para que esos tiles se vean en pantalla, hay que hacer algo muy importante: copiar explícitamente la información a la VRAM, que es la memoria de vídeo controlada por el VDP.


La VRAM es la única memoria que el chip de vídeo utiliza para dibujar la pantalla. Así que, aunque tengamos perfectamente definido el tilemap en RAM… el MSX no lo mostrará si no hacemos esta transferencia.


¿Cómo lo hacemos?


Lo hacemos recorriendo el tilemap y escribiendo cada tile en la posición correspondiente de la tabla de nombres (Name Table) en la VRAM:


void draw_tilemap(void) {
  for (int y = 0; y < 15; y++) {
    for (int x = 0; x < 32; x++) {
      print_tile(x, y, tilemap[y][x]);
    }
  }
}

La función print_tile(x, y, tile_id) se encarga de calcular la posición en la VRAM y hacer un Vpoke() con el valor correcto.


¡Esto nos da flexibilidad!


Este modelo RAM → VRAM nos permite:


  • Modificar el tilemap fácilmente desde el código sin afectar la pantalla de inmediato.

  • Reescribir solo partes concretas del escenario si hace falta (para optimizar).

  • Mantener una copia “lógica” del escenario en memoria para consultar o detectar colisiones, como hace get_tile().


¿Y después qué?


Este artículo nos ha servido para construir una escena sencilla pero completa: tenemos un escenario modular hecho con tiles, nuestro sprite protagonista animado y colisiones básicas que le dan comportamiento.


Pero esto es solo el comienzo.


SCREEN 2 tiene mucho más potencial. En el próximo artículo veremos cómo ampliar el mapa y hacerlo desplazarse con scroll horizontal, extendiendo nuestro mundo más allá de las 32 columnas iniciales hasta llegar a 64… ¡o más! Todo ello sin perder fluidez ni romper la ilusión de movimiento continuo de Sam.


¡Te proponemos un reto!


Ahora que tienes una base funcional, ¿por qué no intentas hacer que Sam salte? Solo necesitas:


  • Un nuevo estado o variable de salto

  • Detectar si hay espacio libre arriba

  • Simular gravedad y caída


¿Te animas?


Haz clic aquí para ver el ejemplo en funcionamiento.

Haz clic aquí para ver a Sam moviéndose en SCREEN 2.

Puedes descargar el código de este proyecto aquí.


Código del proyecto


//Documentation section

//
// part1.c
// Fusion-C SCREEN 2 tiles example
// Here we will learn how to populate SCREEN 2 with tiles
// MoltSXalats 2025
//

// Preprocessor section
#include "fusion-c/header/vdp_graph2.h"
#include "fusion-c/header/msx_fusion.h"
#include "fusion-c/header/vdp_sprites.h"


#define MAP_WIDTH 32
#define MAP_HEIGHT 15

#define TILE_COUNT 41
#define TILE_REPEAT 3
#define TILE_SIZE 8

const unsigned char tilemap[MAP_HEIGHT][MAP_WIDTH] = {
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,3,0,3,0,3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

const unsigned char patterns[TILE_COUNT][TILE_SIZE] = {
  {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0 background
  {0x00,0x7F,0x7F,0x7F,0x00,0xF7,0xF7,0xF7}, // 1 brick
  {0xFD,0xEE,0xDF,0x33,0xFD,0xEE,0xDF,0x33}, // 2 water
  {0x3C,0x7F,0xDB,0xFF,0xFF,0xDB,0x7E,0x3C}, // 3 ball
  {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, // cursor
  {0x02,0x06,0x0E,0x1E,0x3E,0x76,0xE6,0x76}, // letters
  {0xFE,0xCC,0xD8,0xFE,0xCC,0xD8,0xF0,0xE0},
  {0x7E,0xFC,0xC0,0xC0,0xC0,0xC0,0xFC,0x7E},
  {0xFE,0xFE,0xCC,0xD8,0xF0,0xE0,0xC0,0x80},
  {0xFE,0xFE,0x00,0xFE,0xFE,0x00,0xFE,0xFE},
  {0xFF,0xFE,0xC0,0xFC,0xF8,0xC0,0xC0,0x80},
  {0x7E,0xFC,0xC0,0xC0,0xC2,0xC6,0xFE,0x7F},
  {0xC6,0xC6,0xC6,0xFE,0xFE,0xC6,0xC6,0x84},
  {0x38,0x38,0x38,0x38,0x38,0x38,0x30,0x20},
  {0x02,0x06,0x06,0x06,0x06,0x06,0x7E,0xFE},
  {0xCE,0xDC,0xF8,0xF0,0xF8,0xDC,0xCE,0x86},
  {0x40,0xC0,0xC0,0xC0,0xC0,0xC0,0xFE,0xFC},
  {0x82,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6},
  {0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x42},
  {0x7C,0xFE,0xC6,0xC6,0xC6,0xC6,0xFE,0x7C},
  {0xFC,0xFE,0x06,0xFE,0xFC,0xC0,0xC0,0x80},
  {0x7C,0xFE,0xC6,0xC6,0xD6,0xFE,0x7C,0x10},
  {0xC8,0xDC,0xCE,0xDC,0xF8,0xDC,0xCE,0x86},
  {0xFE,0xFE,0x80,0xFE,0xFE,0x02,0xFE,0xFE},
  {0xFE,0xFC,0x30,0x30,0x30,0x30,0x30,0x20},
  {0x82,0xC6,0xC6,0xC6,0xC6,0xC6,0xFE,0x7C},
  {0x03,0x06,0xCC,0xD8,0xF0,0xE0,0xC0,0x80},
  {0xC6,0xC6,0xD6,0xFE,0xFE,0xEE,0xC6,0x82},
  {0x86,0xCE,0xFC,0x78,0x3C,0x7E,0xE6,0xC2},
  {0x87,0xCE,0xFC,0x78,0x30,0x30,0x30,0x20},
  {0x7E,0xFE,0x1C,0x38,0x70,0xE0,0xFE,0xFC},
  {0x7C,0x86,0x8A,0x92,0xA2,0xC2,0x7C,0x00}, // numbers
  {0x10,0x30,0x50,0x90,0x10,0x10,0xFE,0x00},
  {0x7C,0x82,0x02,0x0C,0x70,0x80,0xFE,0x00},
  {0x7C,0x82,0x02,0x0C,0x02,0x82,0x7C,0x00},
  {0x0C,0x14,0x24,0x44,0xFE,0x04,0x04,0x00},
  {0xFE,0x80,0xF8,0x04,0x02,0x04,0xF8,0x00},
  {0x3C,0x40,0x80,0xFC,0x82,0x82,0x7C,0x00},
  {0xFE,0x82,0x04,0x08,0x10,0x10,0x10,0x00},
  {0x7C,0x82,0x82,0x7C,0x82,0x82,0x7C,0x00},
  {0x7C,0x82,0x82,0x7E,0x02,0x04,0x78,0x00}
};

const unsigned char colors[TILE_COUNT][TILE_SIZE] = {
  {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // background
  {0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60}, // brick
  {0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47}, // water
  {0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0}, // ball
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0}, // cursor
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0}, // letters
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0}, // numbers
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0},
  {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0}
};



static const unsigned char body_idle_pattern_right_1[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00011000,
  0b00100000
};

static const unsigned char body_idle_pattern_right_2[] = {
  0b00100000,
  0b00100011,
  0b00011111,
  0b00001111,
  0b00000111,
  0b00000110,
  0b00000010,
  0b00000000
};

static const unsigned char body_idle_pattern_right_3[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000
};

static const unsigned char body_idle_pattern_right_4[] = {
  0b00010100,
  0b10011010,
  0b11110000,
  0b11110000,
  0b11110000,
  0b00111000,
  0b00100000,
  0b00000000
};

static const unsigned char detail_idle_pattern_right_1[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000
};

static const unsigned char detail_idle_pattern_right_2[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000001,
  0b00000000,
  0b00000000
};

static const unsigned char detail_idle_pattern_right_3[] = {  
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000
};

static const unsigned char detail_idle_pattern_right_4[] = {
  0b00000000,
  0b00000100,
  0b00001110,
  0b00001110,
  0b00001100,
  0b10000100,
  0b10011000,
  0b00000000
};

static const unsigned char body_walk_pattern_right_1[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00110000
};

static const unsigned char body_walk_pattern_right_2[] = {
  0b01000000,
  0b01000001,
  0b00100011,
  0b00011111,
  0b00001111,
  0b00011100,
  0b00110000,
  0b00100000
};

static const unsigned char body_walk_pattern_right_3[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00010100
};

static const unsigned char body_walk_pattern_right_4[] = {
  0b00011010,
  0b00010000,
  0b10110000,
  0b11110000,
  0b11111000,
  0b11101100,
  0b00000100,
  0b00000000
};

static const unsigned char detail_walk_pattern_right_1[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000
};

static const unsigned char detail_walk_pattern_right_2[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000010,
  0b00001100,
  0b00001000
};

static const unsigned char detail_walk_pattern_right_3[] = {  
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000
};

static const unsigned char detail_walk_pattern_right_4[] = {
  0b00000100,
  0b00001110,
  0b00001110,
  0b00001100,
  0b00000100,
  0b00000010,
  0b00000010,
  0b00000000
};

static const unsigned char body_idle_pattern_left_1[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000
  0b00000000
};

static const unsigned char body_idle_pattern_left_2[] = {
  0b00101000,
  0b01011001,
  0b00001111,
  0b00001111,
  0b00001111,
  0b00011100,
  0b00000100,
  0b00000000
};

static const unsigned char body_idle_pattern_left_3[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00011000,
  0b00000100
};

static const unsigned char body_idle_pattern_left_4[] = {
  0b00000100,
  0b11000100,
  0b11111000,
  0b11110000,
  0b11100000,
  0b01100000,
  0b01000000,
  0b00000000
};

static const unsigned char detail_idle_pattern_left_1[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000
};

static const unsigned char detail_idle_pattern_left_2[] = {
  0b00000000,
  0b00100000,
  0b01110000,
  0b01110000,
  0b00110000,
  0b00100001,
  0b00011001,
  0b00000000
};

static const unsigned char detail_idle_pattern_left_3[] = {  
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000
};

static const unsigned char detail_idle_pattern_left_4[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b10000000,
  0b00000000,
  0b00000000
};

static const unsigned char body_walk_pattern_left_1[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00101000
};

static const unsigned char body_walk_pattern_left_2[] = {
  0b01011000,
  0b00001000,
  0b00001101,
  0b00001111,
  0b00011111,
  0b00110111,
  0b00100000,
  0b00000000
};

static const unsigned char body_walk_pattern_left_3[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00001100
};

static const unsigned char body_walk_pattern_left_4[] = {
  0b00000010,
  0b00000010,
  0b11000100,
  0b11111000,
  0b11110000,
  0b00111000,
  0b00001100,
  0b00000100
};

static const unsigned char detail_walk_pattern_left_1[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000
};

static const unsigned char detail_walk_pattern_left_2[] = {
  0b00100000,
  0b01110000,
  0b01110000,
  0b00110000,
  0b00100000,
  0b01000000,
  0b01000000,
  0b00000000
};

static const unsigned char detail_walk_pattern_left_3[] = { 
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000
};

static const unsigned char detail_walk_pattern_left_4[] = {
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b00000000,
  0b01000000,
  0b00110000,
  0b00010000
};

static const unsigned char player_pattern[] = {
  0x38,
  0x7C,
  0xFE,
  0xFE,
  0xFE,
  0x7C,
  0x38,
  0x00
};

// Definition section of function prototypes
void load_patterns(void);
void load_colors(void);
void load_names(void);
void FT_Wait(int);
void print_tile(char,char,unsigned char);
unsigned char get_tile(char,char);
void draw_tilemap(void);

/* --------------------------------------------------------- */
void main( void ) {

    char x;
    char y;
    int d;

    x=8;
    y=80;
    d=1;

  if(ReadMSXtype()==3)  // IF MSX is Turbo-R Switch CPU to Z80 Mode
    {
       ChangeCPU(0);
    }

  SetColors(15,1,1);
  Screen(2);

  SpriteReset();

  // The 16 x 16 pixels Sprite is made of 4 patterns

  SetSpritePattern( 0, body_idle_pattern_right_1,8 );
  SetSpritePattern( 1, body_idle_pattern_right_2,8 );
  SetSpritePattern( 2, body_idle_pattern_right_3,8 );
  SetSpritePattern( 3, body_idle_pattern_right_4,8 );

  SetSpritePattern( 4, detail_idle_pattern_right_1,8 );
  SetSpritePattern( 5, detail_idle_pattern_right_2,8 );
  SetSpritePattern( 6, detail_idle_pattern_right_3,8 );
  SetSpritePattern( 7, detail_idle_pattern_right_4,8 );

  SetSpritePattern( 8, body_walk_pattern_right_1,8 );
  SetSpritePattern( 9, body_walk_pattern_right_2,8 );
  SetSpritePattern( 10, body_walk_pattern_right_3,8 );
  SetSpritePattern( 11, body_walk_pattern_right_4,8 );

  SetSpritePattern( 12, detail_walk_pattern_right_1,8 );
  SetSpritePattern( 13, detail_walk_pattern_right_2,8 );
  SetSpritePattern( 14, detail_walk_pattern_right_3,8 );
  SetSpritePattern( 15, detail_walk_pattern_right_4,8 );

  SetSpritePattern( 16, body_idle_pattern_left_1,8 );
  SetSpritePattern( 17, body_idle_pattern_left_2,8 );
  SetSpritePattern( 18, body_idle_pattern_left_3,8 );
  SetSpritePattern( 19, body_idle_pattern_left_4,8 );

  SetSpritePattern( 20, detail_idle_pattern_left_1,8 );
  SetSpritePattern( 21, detail_idle_pattern_left_2,8 );
  SetSpritePattern( 22, detail_idle_pattern_left_3,8 );
  SetSpritePattern( 23, detail_idle_pattern_left_4,8 );

  SetSpritePattern( 24, body_walk_pattern_left_1,8 );
  SetSpritePattern( 25, body_walk_pattern_left_2,8 );
  SetSpritePattern( 26, body_walk_pattern_left_3,8 );
  SetSpritePattern( 27, body_walk_pattern_left_4,8 );

  SetSpritePattern( 28, detail_walk_pattern_left_1,8 );
  SetSpritePattern( 29, detail_walk_pattern_left_2,8 );
  SetSpritePattern( 30, detail_walk_pattern_left_3,8 );
  SetSpritePattern( 31, detail_walk_pattern_left_4,8 );

  SetSpritePattern( 32, player_pattern,8 );

  load_patterns();
  load_colors();
  draw_tilemap();
  //load_names();

  Sprite16();

 
  // Player 2
  //PutSprite (3,32,8,8,2);

  // Pilota
  //print_tile(28, 13, 3);

  while (Inkey()!=27)
  {  
    x=(x+d)&255;
    char tile_x = x / 8;
    char tile_y = y / 8;

    if (d==1) {

      // Movement
      if( x % 2 == 0) 
      {
        PutSprite (1,0,x,y,4);
        PutSprite (2,4,x,y,14);
      }

      if( x % 2 == 1) 
      {
        PutSprite (1,8,x,y,4);
        PutSprite (2,12,x,y,14);          
      }
      // Collisions
      unsigned char tile = get_tile(tile_x + 2,tile_y);

    if (tile == 1) {
        // It's a wall → can't walk through
        d = -1;
        //break;
      } else if (tile == 2) {
        // It's water → lose a life
      } else if (tile == 3) {
        // Yellow ball → collect it
      }
    } // if (d==1) {

    if (d==-1) {

      //Movement
      if( x % 2 == 0) 
      {
        PutSprite (1,16,x,y,4);
        PutSprite (2,20,x,y,14);
      }

      if( x % 2 == 1) 
      {
        PutSprite (1,24,x,y,4);
        PutSprite (2,28,x,y,14);          
      }

      //Collisions
      unsigned char tile = get_tile(tile_x,tile_y);

    if (tile == 1) {
        // It's a wall → can't walk through
        d = -1;
        //break;
      } else if (tile == 2) {
        // It's water → lose a life
      } else if (tile == 3) {
        // Yellow ball → collect it
      }
    } // if (d==-1) {

    FT_Wait(3000);
  } // while

  Screen(0);
  Exit(0);
} // main

// User defined functions section

void load_patterns(void) {
  unsigned int base_addresses[TILE_REPEAT] = {0x0000, 0x0800, 0x1000};
  int section, tile, i;

  for (section = 0; section < TILE_REPEAT; section++) {
    VpokeFirst(base_addresses[section]);
    for (tile = 0; tile < TILE_COUNT; tile++) {
      for (i = 0; i < TILE_SIZE; i++) {
        VpokeNext(patterns[tile][i]);
      }
    }
  }
}

void load_colors(void) {
  unsigned int base_addresses[TILE_REPEAT] = {0x2000, 0x2800, 0x3000};

  int section, tile, i;

  for (section = 0; section < TILE_REPEAT; section++) {
    VpokeFirst(base_addresses[section]);
    for (tile = 0; tile < TILE_COUNT; tile++) {
      for (i = 0; i < TILE_SIZE; i++) {
        VpokeNext(colors[tile][i]);
      }
    }
  }
}

void load_names(void) {
    int x, y;
    unsigned int vram_address = 0x1800; // Name Table start for SCREEN 2

    VpokeFirst(vram_address); // Initialize VRAM write operation

    for (y = 0; y < MAP_HEIGHT; y++) {
        for (x = 0; x < MAP_WIDTH; x++) {
            VpokeNext(tilemap[y][x]); // Write the corresponding tile
        }
    }
}

void draw_tilemap(void) {
    int y, x;
    for (y = 0; y < MAP_HEIGHT; y++) {
        for (x = 0; x < MAP_WIDTH; x++) {
            print_tile(x, y, tilemap[y][x]);
        }
    }
}

void FT_Wait(int cicles) {
  unsigned int i;

  for(i=0;i<cicles;i++)
    {}
}

void print_tile(char x, char y, unsigned char tile_id) {
    unsigned int vram_address = 0x1800 + (y * 32) + x;
    Vpoke(vram_address, tile_id);
}

unsigned char get_tile(char x, char y) {
    unsigned int vram_address = 0x1800 + (y * 32) + x;
    return Vpeek(vram_address);
}


 
 
 

Comentarios


Envíanos un mensaje y dinos lo que piensas

¡Gracias por tu mensaje!

© 2035 Creado por Tren de ideas con Wix.com

bottom of page