Ehilà! Sono consapevole che è passato parecchio dall'ultima volta, ma ho finalmente trovato il tempo di terminare anche questo capitolo, se volete rivedere quello scorso lo trovate qui.
Regular Sprites
Con sprite si indica una (piccola) immagine generalmente bidimensionale che può muoversi in modo indipendente rispetto allo sfondo.
Gli sprite sono un po’ più difficili da usare rispetto alle immagini bitmap, poiché devono essere raggruppati in tile di 8x8 pixel.
Far apparire gli sprite sullo schermo richiede 3 passaggi:
Caricare l’immagine e la palette nell’Object VRAM (OVRAM) e nell’Object palette.
Impostare gli attributi nell’Object Attribute Memory (OAM) per usare i tile appropriati e per impostare correttamente la dimensione.
Abilitare gli oggetti nel REG_DISPCNT e impostare la modalità corretta.
Ora, i tile sono di due tipi: 4bpp (single-tile, lunghi 32 bytes) e 8bpp (double-tile, lunghi 64 bytes). I tile disponibili per gli sprite sono conservati nella OVRAM, che ha dimensione 32 KB ed è mappata dagli ultimi due charblocks di tile_mem, detti anche lower (block 4, indirizzo 0601:0020h) e higher (block 5, indirizzo 0601:4000h) sprite-blocks. Il conteggio inizia sempre al lower sprite-block ed è sempre fatto con passo di 32 byte, quindi il primo tile sarà sempre all’indirizzo 0601:0020h. In ogni charblock ci sono 512 tiles, per un totale di 1024 tiles.
Nella demo la memoria è già stata mappata, quindi se ad es. vi serve il tile 123 vi potete accedere facendo tile_mem[4][123].
Inoltre, gli sprite hanno la propria palette che inizia all’indirizzo 0500:0200h.
Sprite mapping mode
Gli sprite hanno due mapping modes: 1D e 2D. Di default usano la modalità 2D ma è possibile selezionare l’altra impostando il sesto bit di REG_DISPCNT.
Facciamo un esempio. L’immagine seguente mostra lo sprite di un metroid diviso in tile. Nella modalità 2D, interpretate i charblock dello sprite come una bitmap di 256x256 pixel e lo sprite è un rettangolo di quella bitmap. Tuttavia, potreste anche considerare i charblock come una sequenza (vettore) di tile.
I numeri rossi seguono la numerazione 1D, quelli blu quella 2D.
Dal punto di vista della programmazione, la modalità 1D è più semplice perché non bisogna preoccuparsi del passo di 32 Bytes quando inseriamo in memoria gli sprite. Tuttavia, mostrare lo sprite sullo schermo è più comodo in 2D.
Per convertire le immagini si usa grit dalla shell. Supponendo che foo.bmp è una bitmap con quattro oggetti 64x16 pixel, si converte in in una bitmap di 8x8 4bpp tile in questo modo:
grit foo.bmp -gB4
Per calcolare il numero di tile che compongono uno sprite si divide il numero di pixel per 8, ad esempio 64x64 pixel = 8x8 tile.
Object Attribute Memory
Per disegnare gli sprite sullo schermo si usa l’OAM, che inizia all’indirizzo 0700:0000h ed è lunga 1024 byte. L’OAM è costituita da due struct: la OBJ_ATTR per gli attributi degli sprite regular e la OBJ_AFFINE per gli attributi che riguardano rotazione, distorsione e scala dello sprite.
typedef struct tagOBJ_ATTR {
u16 attr0;
u16 attr1;
u16 attr2;
s16 fill;
} ALIGN4 OBJ_ATTR;
typedef struct OBJ_AFFINE {
u16 fill0[3];
s16 pa;
u16 fill1[3];
s16 pb;
u16 fill2[3];
s16 pc;
u16 fill3[3];
s16 pd;
} ALIGN4 OBJ_AFFINE;
ALIGN4 sta per la macro
__attribute__((aligned(4))
che serve per la copia nel buffer.
Della struct OBJ_AFFINE parleremo più avanti, quando tratteremo la rotazione degli sprite. Concentriamoci per ora su OBJ_ATTR.
Le tabelle seguenti illustrano la funzione dei campi di ogni attributo (3 da 16 bit l’uno).
ATTR0
ATTR1
ATTR2
ATTR3
È usato solo in OBJ_AFFINE, ne parleremo più avanti.
LA DEMO
Questa volta ho usato un approccio differente. Siccome il tutorial consisteva in un progetto di 10-15 file, al posto di riscriverlo da 0 ripreso alcune delle funzioni originali e ho riscritto la funzione main per cercare di capire come funzionasse il caricamento dello sprite in memoria. Vi allego sia la spiegazione che i file, quindi se volete potete modificarla ulteriormente per aggiungere il movimento come visto nei capitoli precedenti o per specchiare lo sprite ad esempio. In ogni caso questa demo consiste nel far apparire un metroid a schermo.
Come detto, dobbiamo fare 3 cose (non necessariamente in questo ordine):
copiare la grafica nella VRAM
impostare l'OAM in modo che usi la grafica
abilitare gli sprite nel display control register REG_DISPCNT
Abilitiamo gli sprite impostando il bit 12 del REG_DISPCNT (punto 2 nel codice in spoiler).
La funzione oam_init() nasconde tutti gli sprite in modo da partire in uno stato noto.
Nel punto 1 del codice carichiamo lo sprite nella object VRAM.
Infine nel punto 3 impostiamo OBJ_ATTR in modo che usi i tile del metroid attraverso la funzione obj_set_attr(...) e impostiamo la posizione.
Dopo esservi posizionati nella cartella del progetto attraverso la shell e aver fatto make il risultato dovrebbe essere:
Il risultato sull'emulatore è questo:
Vi allego anche la cartella del progetto così potete provare: sprite_demo.zip
Nel prossimo capitolo vedremo come far apparire gli sfondi, si tratta anche dell'ultimo capitolo della sezione sulle basi della programmazione per GBA.
Fonte: https://gbadev.net/tonc/regobj.html
Tag: @evilespeon @ZedeFire @VincyDarkHeart @Macca