Primeres passes d’en Sam a SCREEN 2 – Tiles i col·lisions (Part 1)
- MoltS Xalats
- Aug 10
- 13 min de lectura

Introducció
Després de l’article Creant i movent sprites, on en Sam prenia forma per primer cop, ara el deixem anar a voltar pel seu món: un escenari de tiles de 8x8 construït al mode SCREEN 2. El nostre gat protagonista fa les seves primeres passes entre parets, aigua i pilotes grogues, tot rebotant a esquerra i dreta mentre descobrim com dibuixar escenaris, animar-lo i detectar col·lisions. Això tot just comença… i sí, l’aigua no li agrada gens!
Objectiu de l’article
En aquest primer passeig d’en Sam pel món de SCREEN 2, aprendrem a:
Construir un escenari amb tiles de 8x8, perquè el nostre gat pugui caminar amb estil.
Carregar a la VRAM tot el necessari: patrons, colors i la taula que diu quin tile va on.
Fer que en Sam es mogui d’esquerra a dreta, animat i atent a les parets on no vol xocar.
Deixar-ho tot a punt per a la següent aventura: fer scroll horitzontal i ampliar el seu món.
Què és SCREEN 2?
SCREEN 2 és un dels modes gràfics clàssics dels MSX, ideal per fer videojocs amb escenaris modulars, és a dir, construïts a partir de petites peces gràfiques (tiles) reutilitzables que permeten muntar l’escenari de forma eficient. Treballa amb una resolució de 256×192 píxels, organitzada en una graella de 32×24 tiles de 8x8 píxels cadascun.
A diferència d’altres modes més moderns com SCREEN 5, aquí no pintem píxels un per un, sinó que construïm la pantalla a partir de tiles, petites peces gràfiques reutilitzables.
Patrons i tilemap: el món de mosaic
Un patró és un gràfic de 8x8 píxels, generalment monocromàtic amb un segon color assignat via la taula de color. A SCREEN 2 podem tenir fins a 768 patrons diferents dividits en tres bancs de 256. Aquests patrons no tenen una posició fixa, sinó que es col·loquen a la pantalla a través de la taula de noms (Name Table).
Cada tile a pantalla és, per tant:
Un gràfic (Pattern)
Un color associat (Color Table)
Una posició (Name Table)
Colors dels patrons
SCREEN 2 permet definir un color de davant (foreground) i un color de fons (background) per a cada tile. Aquesta informació es guarda a la taula de colors (Color Table) de la VRAM.
Cada byte d’aquesta taula conté:
Els 4 bits més alts (nibble alt) → color de fons
Els 4 bits més baixos (nibble baix) → color de dibuix (foreground)
Exemple 1:
Si un byte té el valor 0x47, això vol dir:
Color de fons: 4 (blau)
Color de davant: 7 (blau clar)
Quan es dibuixa un tile, cada línia de 8 píxels prové d’un byte del patró (Pattern Table):
Cada bit 0 indica “pinta amb el color de fons”
Cada bit 1 indica “pinta amb el color de davant”
Això ens permet fer servir un mateix tile amb diferents combinacions de colors, i així estalviar espai i fer més variat l’escenari gràfic.
Exemple 2:

0b00111100 // ..####..
0b01111110 // .######.
0b11011011 // ##.##.##
0b11111111 // ########
0b11111111 // ########
0b11011011 // ##.##.##
0b01111110 // .######.
0b00111100 // ..####..
unsigned char ball_pattern[8] = {
0x3C, 0x7E, 0xDB, 0xFF,
0xFF, 0xDB, 0x7E, 0x3C
};
Estructura de memòria a SCREEN 2
SCREEN 2 divideix la VRAM en tres zones principals:
Zona VRAM | Contingut | Adreça inicial |
Pattern | Gràfics dels tiles (8x8) | 0x0000 |
Color | Color de cada tile | 0x2000 |
Name | Quin tile va a cada posició XY | 0x1800 |
Taula: Divisió memòria VRAM
Cada tile de pantalla apunta a un número de patró del 0 al 255 (per banc). Això ens permet reutilitzar gràfics fàcilment i generar escenaris modulars.
I la pantalla… es divideix?
Sí! Una particularitat interessant de SCREEN 2 és que podem dividir la pantalla en tres seccions horitzontals de 8 línies de tiles cadascuna (8 línies × 8 píxels = 64 píxels). Això s’aconsegueix assignant diferents bancs de patrons a cada terç de pantalla.
Aquesta tècnica és molt útil per:
Fer HUDs o panells informatius només a la part inferior
Separar escenari jugable i informació de joc
Reutilitzar patrons sense conflictes entre àrees

Sabies que…?
La divisió de la pantalla en tres terços a SCREEN 2 no és només una qüestió visual: és una optimització pensada per la memòria del MSX i el seu processador 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.
Gràcies a això, amb només 8 bits pots gestionar escenaris modulars i reutilitzables en una pantalla de 192 línies… tot un exemple d’enginyeria retro!
Construint el mapa de tiles
El nostre escenari es defineix amb una matriu de dues dimensions anomenada tilemap[15][32]. Aquesta matriu actua com un plànol que indica quin tile ha d’anar en cada posició de la graella de 32 columnes per 15 files (recorda que SCREEN 2 permet fins a 24 files de tiles, però n’utilitzem només 15 per deixar marge per a HUDs o experiments futurs).
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}
};
Aquesta matriu conté valors numèrics que representen els diferents tipus de tile disponibles. En el nostre exemple, hem definit:
0 | Fons negre |
1 | Paret |
2 | Aigua |
3 | Pilota |
Taula: Tipus de tiles
Per dibuixar aquest mapa a la pantalla, recorrem la matriu i, per cada posició, cridem la funció print_tile(x, y, tile_id), que escriu directament a la VRAM mitjançant 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]);
}
}
}
Aquesta funció copia el contingut de la tilemap des de la RAM a la taula de noms (Name Table) de la VRAM. És important entendre que definir el mapa a RAM no és suficient: cal fer explícitament la transferència a vídeo.
En Sam entra en acció
Un cop hem dibuixat l’escenari amb tiles, és hora que entri en joc el nostre protagonista: en Sam.
En Sam és un sprite de 16×16 píxels, format per quatre sprites de 8×8 col·locats un al costat de l’altre. En aquest primer exemple el farem caminar automàticament d’esquerra a dreta i de dreta a esquerra, rebotant quan troba una paret.
El moviment es fa pas a pas i la detecció de col·lisions és molt senzilla: abans de moure en Sam, consultem quin tile hi ha a la seva posició futura amb la funció 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 és una paret (valor 1), en Sam canvia de direcció:
if (tile == 1) {
d = -1; // Change Direction to the Left
}
I quan torna a topar amb una paret a l’altra banda…
if (tile == 1) {
d = +1; // Change Direction to the Right
}
La variable d ens indica la direcció del moviment:
Si d = +1, en Sam es mou cap a la dreta
Si d = -1, en Sam es mou cap a l’esquerra
A cada fotograma, incrementem o decrementem la coordenada X segons aquest valor:
Així aconseguim un moviment rebotador, molt típic dels jocs clàssics: en Sam fa anades i vingudes per la pantalla mentre l’escenari roman estable.
x = x + d;
Càrrega de patrons i colors
SCREEN 2 permet tenir fins a 768 patrons (gràfics de tiles) i els seus colors associats, distribuïts en tres bancs de 256. Cada tile ocupa 8 bytes i es repeteix en tres seccions de VRAM per garantir compatibilitat amb tota la pantalla.
Per carregar aquests patrons i colors, utilitzem dues funcions:
Patrons gràfics
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]);
}
}
}
}
Colors associats
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]);
}
}
}
}
Aquestes dues funcions escriuen, tres vegades, els patrons i colors a la VRAM per cobrir els tres terços horitzontals de la pantalla (64 línies cadascun). Això assegura que tots els tiles es mostrin correctament, independentment de la seva posició horitzontal.
I per què són 8 bytes per tile?
Cada tile de SCREEN 2 és un quadrat de 8×8 píxels. Com que cada línia de 8 píxels es representa amb 1 byte (8 bits), calen 8 bytes per tile:
1 byte per línia de 8 píxels
8 línies per tile
⇒ 8 × 1 = 8 bytes per tile
Aquest format s’aplica tant a la Pattern Table (que defineix la forma del tile) com a la Color Table (que defineix el color foreground i background de cada línia).
Per això, un banc complet de 256 tiles ocupa:
256 × 8 = 2048 bytes (2 KB) per patrons
256 × 8 = 2 KB per colors
Tot plegat encaixa com un guant a les regions definides a VRAM:
0x0000–0x17FF → Patrons (3 × 2 KB)
0x2000–0x37FF → Colors (3 × 2 KB)
Concepte clau: tilemap en RAM vs. VRAM
Quan definim un mapa de tiles al nostre codi, ho fem normalment en una variable de tipus matriu, com ara:
unsigned char tilemap[15][32];
Aquest tilemap està a la RAM del MSX, és a dir, a la memòria principal on s’executa el codi. Però perquè aquests tiles es vegin a la pantalla, cal fer una cosa molt important: copiar explícitament la informació a la VRAM, que és la memòria de vídeo controlada pel VDP.
La VRAM és l’única memòria que el xip de vídeo utilitza per dibuixar la pantalla. Així que, encara que tinguem perfectament definit el tilemap a RAM… el MSX no el mostrarà si no fem aquesta transferència!
Com ho fem?
Ho fem recorrent el tilemap i escrivint cada tile a la posició corresponent de la taula de noms (Name Table) a 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ó print_tile(x, y, tile_id) s’encarrega de calcular la posició a la VRAM i fer un Vpoke() amb el valor correcte.
Això ens dona flexibilitat!
Aquest model RAM → VRAM ens permet:
Modificar el tilemap fàcilment des del codi sense afectar la pantalla immediatament.
Reescriure només parts concretes de l’escenari si cal (per optimitzar).
Mantenir una còpia “lògica” de l’escenari en memòria per consultar o detectar col·lisions, com fa get_tile().
I després què?
Aquest article ens ha servit per construir una escena senzilla però completa: tenim un escenari modular fet amb tiles, el nostre sprite protagonista animat, i col·lisions bàsiques que li donen comportament.
Però això és només el començament.
SCREEN 2 té molt més potencial. En el proper article veurem com ampliar el mapa i fer-lo desplaçar-se amb scroll horitzontal, estenent el nostre món més enllà de les 32 columnes inicials fins arribar a 64… o més! Tot això sense perdre fluïdesa ni trencar la il·lusió de moviment continu d’en Sam.
Et proposem un repte!
Ara que tens una base funcional, per què no intentes fer que en Sam salti? Només necessites:
Un nou estat o variable de salt
Detectar si hi ha espai lliure a sobre
Simular gravetat i caiguda
T’animes?
Clica aquí per a veure l'exemple funcionant.
Clica aquí per veure en Sam movent-se en SCREEN 2.
Pots descarregar el codi d’aquest projecte aquí.
Codi del projecte
//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);
}
Comentaris