SMF - Just Installed!
Pagine: [1] 2
16 Lug 2014 - [7] Free Pascal per NDS: gli spritesNel gergo videoludico è chiamato sprite ogni elemento grafico che si muove attraverso lo schermo: i fantasmini di Pac-Man, le navicelle spaziali di Space Invaders, gli omini di Prince of Persia, i boss di fine livello di Metal Slug sono tutti esempi di sprites. Il Nintendo DS è equipaggiato con un sistema (in realtà sono due, lo vedremo tra poco) che li gestisce via hardware, chiamato sprite engine, che risparmia al programmatore il compito di scrivere il codice per disegnarli e muoverli; come rovescio della medaglia, questo sistema non gestisce le collisioni, che vanno perciò controllate via software; è inoltre limitato ad un numero massimo di 128 sprites per motore, dei quali soltanto 32, sempre per motore, possono essere ruotati e scalati. Gli sprites possono avere una dimensione variabile da 8x8 a 64x64 pixel e forma quadrata o rettangolare, sia con il lato lungo disposto orizzontalmente che verticalmente:
Essi sono composti da una serie di tiles di dimensione 8x8 pixel che vanno immagazzinate in memoria con un procedimento del tutto analogo a quello già visto per i background e, come per i background, possono disporre di 16 palette da 16 colori, oppure di una da 256 colori, oltre alla possibilità di utilizzare una palette estesa.
La tile di partenza di ogni sprite ha bisogno di essere indicizzata per permettere allo sprite engine di ricomporne l'immagine sullo schermo:
InizializzazionePrima di potere utilizzare gli sprite engine del DS, essi vanno inizializzati tramite la procedura oamInit: procedure oamInit(var oam: OamState; mapping: SpriteMapping; extPalette: cbool); cdecl; external; Essa richiede tre parametri; il primo indica lo sprite engine che intendiamo utilizzare (oamMain per l'engine principale e oamSub per il secondario); il secondo fa riferimento al tipo di layout che gli sprites assumono nella memoria video e all'offset utilizzato per la loro indicizzazione; il terzo fa riferimento all'utilizzo della palette estesa. Vediamo in dettaglio questi tre parametri. Dicevamo di una distinzione tra due sprite engine, il principale e il secondario, associati rispettivamente al motore 2D principale e al motore 2D secondario. Essi sono rappresentati internamente da due record, chiamati OAM (Object Attribute Memory), che occupano una specifica posizione di memoria, situata a partire dagli indirizzi $07000000 (oamMain) e $07000400 (oamSub). I bit di queste locazioni di memoria funzionano da "interruttori" per attivare e disattivare le caratteristiche degli sprite; così, ad esempio, impostare un bit a 1 invece che a 0 può significare l'attivazione o la disattivazione dell'effetto mosaico, oppure l'impostazione dello sprite alla dimensione 16x16, o ancora il flipping verticale dello stesso, e così via. Altri gruppi di bit rappresentano invece la posizione dello sprite sullo schermo. Non ci complicheremo la vita manipolando direttamente i bit; ci avvarremo piuttosto delle comode funzioni di alto livello che vengono messe a disposizione dalla libreria libnds. Nei prossimi paragrafi vedremo le più importanti e il loro utilizzo. Torniamo ai parametri della procedura oamInit, analizzando il secondo. Abbiamo parlato in precedenza di layout degli sprites nella memoria video. Esistono due layout utilizzabili: il layout 1D e il layout 2D. Senza farsi intimorire - e fuorviare - dalla definizione, basti sapere che gli sprites 1D sono organizzati nella memoria in maniera consecutiva, uno di seguito all'altro, suddivisi in blocchi da 8x8 pixel; gli sprites 2D sono invece organizzati come se si trovassero in un array, quindi sono disposti in memoria esattamente come vengono disegnati con un programma di grafica. A differenza dei background, gli sprites non hanno bisogno di una mappa per ricomporre sullo schermo le tiles in cui sono suddivisi. È sufficiente indicare all'engine la tile 8x8 di partenza, la forma dello sprite, la sua dimensione e il layout assunto in memoria dall'immagine (1D o 2D), e il gioco è fatto. Le immagini qui in basso mostrano i layout che gli sprites assumono in memoria, rispettivamente nella modalità 1D e in quella 2D:
I "quadratini" rappresentano le tiles 8x8 che compongono gli sprites; le tiles, da sinistra a destra, dall'alto verso il basso, occupano posizioni consecutive nella memoria video. È facile capire che nella modalità 1D ogni sprite è contenuto in blocchi di memoria contigui; nell'esempio incontreremo prima tutte le tiles dell'uovo, poi tutte le tiles della stella. Personalmente ritengo la modalità 2D poco attraente perché, anche se a prima vista potrebbe sembrare più semplice da capire, limita l'utilizzo degli sprites ad un massimo di 32 kb (più altri 32 kb per il motore secondario), contro i 256 kb utilizzabili nella modalità 1D (più altri 128 kb per il motore secondario).
Gestione degli spritesLa caratteristica interessante degli sprites nel DS è la possibilità di muoverli attraverso gli schermi senza doversi preoccupare di ridisegnarli di frame in frame. La funzione-mostro che se ne occupa è oamSet, definita come segue: procedure oamSet(var oam: OamState; id, x, y, priority, palette_alpha: cint; size: SpriteSize; format: SpriteColorFormat; const gfxOffset: pointer; affineIndex: cint; sizeDouble, hide, hflip, vflip, mosaic: cbool); cdecl; external; Prima di vedere come utilizzarla, diamo un'occhiata ai tanti parametri che la compongono:
Un esempio vale più di mille parole, perciò proviamo a caricare uno sprite in memoria, a disegnarlo e ad animarlo.
10-17: Definiamo un record con le caratteristiche dello sprite: le coordinate della posizione sullo schermo (x, y), l'indirizzo in memoria dove è memorizzato lo sprite (gfx_mem) e quello dove è memorizzato il primo frame (gfx_frame), il frame corrente dell'animazione (anim_frame) e la pausa tra un frame e l'altro (anim_delay), il flag per stabilire se lo sprite vada disegnato ribaltato orizzontalmente (mirror).
Per commenti e chiarimenti: http://www.lazaruspascal.it/index.php?topic=78.0
16 Lug 2014 - [6] Free Pascal per NDS: rotazione e zoom dei backgroundCome si è potuto constatare dalla puntata precedente, lavorare sui background è davvero semplice. La libreria libnds ci mette a disposizione un numero incredibile di funzioni e procedure che coprono qualsiasi aspetto relativo alla gestione dei layer, dal movimento allo zoom, dalla priorità alle rotazioni. Ed è appunto di rotazioni e zoom che ci occuperemo nel prossimo esempio. Per prima cosa occorre cambiare la modalità dello schermo, perché il Mode 0 non gestisce rotazioni e zoom sui background. Potremmo usare, per esempio, il Mode 5, che permette di gestire due background di tipo text e due di tipo extended rotation. Vediamo subito il codice di esempio, che non varia poi molto da quanto già visto:
Come al solito, scorriamo insieme le righe di codice più significative. [32]: Dopo le solite impostazioni e dopo avere attivato il Mode 5, impostiamo il background 3 che ruoteremo e scaleremo. [34-35]: Centriamo il background nello schermo e impostiamo il punto di rotazione in modo tale che sia al centro dell'immagine (provate a giocare con i valori per vedere come varia la rotazione). [51]: Effettuiamo lo zoom, utilizzando lo stesso valore di x, in modo tale da avere uno zoom proporzionato. Cambiando i due valori è possibile ottenere anche un effetto stretching sul background. La funzione che si occupa dello zoom è bgSetScale(id: cint; sx, sy: cint32); che richiede come parametri il background da zoomare (id) e il livello di zoom orizzontale e verticale (sx, sy). Un valore pari a 256 indica che non c'è nessuno zoom; un valore inferiore ingrandisce il background (128 indica un fattore di zoom di 2X) e un valore superiore rimpicciolisce il background (512 indica un fattore di zoom di 1/2X). Da notare che nei background (extended) rotation la tilemap non viene ripetuta, come accade invece nei background di tipo text. [52]: La rotazione di un background può essere effettuata in due modi, utilizzando una delle funzioni bgRotate(id, angle: cint); bgSetRotate(id, angle: cint); Queste due funzioni non sono equivalenti. Infatti, in bgRotate la rotazione è cumulativa, quindi è sufficiente passare un valore costante come parametro e l'angolo di rotazione viene incrementato di quel valore ad ogni frame. bgSetRotate non è cumulativa, quindi ad ogni frame il valore dell'angolo viene riportato a 0. Esiste poi una funzione che si occupa di ruotare e scalare un background in un sol colpo: bgSetRotateScale(id, angle: cint; sx, sy: cint32); Il suo uso è del tutto simile alle due funzioni appena viste. Ricordate però che la rotazione non è cumulativa e si comporta come bgSetRotate. L'esempio seguente dovrebbe chiarire ogni dubbio in proposito:
L'unica differenza con quanto visto in precedenza è la chiamata alla funzione bgSetRotateScale.
Per commenti e chiarimenti: http://www.lazaruspascal.it/index.php?topic=78.0
11 Lug 2014 - [5] Free Pascal per NDS: background a tiles e mappeProcediamo ora con un argomento che risulta utilissimo nello sviluppo di giochi: la gestione di tiles e mappe. Nella tabella relativa alle modalità grafiche, abbiamo visto che ogni modalità video permette di accedere a un numero di background differenti. In passato, nelle vecchie versioni di libnds, l'unico modo che si aveva per impostare ed utilizzare i background era quello di utilizzare direttamente i registri, eseguendo operazioni sui bit. Oggi libnds offre un layer di astrazione di livello più alto, che rende molto più semplice l'utilizzo dei background. Vediamo due esempi: nel primo imposteremo un background utilizzando il vecchio sistema, attivando bit nei registri; nel secondo eseguiremo lo stesso compito, ma utilizzando il nuovo set di istruzioni. In questi esempi utilizzeremo il Mode 0, che ci permetterà di avere 4 background di tipo text. program Mode0_a; {$mode objfpc} uses ctypes, nds9; begin // Attivazione del Mode 0 e del Background 0 REG_DISPCNT^ := MODE_0_2D or DISPLAY_BG0_ACTIVE; VRAM_A_CR^ := VRAM_ENABLE or VRAM_A_MAIN_BG; // Impostare il Background 0 attraverso il suo registro BGO REG_BG0CNT^ := BG_32x32 or BG_COLOR_256 or BG_MAP_BASE(0) or BG_TILE_BASE(1); while true do; // loop infinito! end. Vediamo il codice in dettaglio. Il primo registro utilizzato è REG_DISPCNT e serve per impostare la modalità grafica Mode0 (con MODE_0_2D) e per attivare il background 0 (tramite DISPLAY_BG0_ACTIVE). Il secondo registro è VRAM_A_CR e si riferisce al banco di memoria A. Tramite gli switch VRAM_ENABLE e VRAM_A_MAIN_BG lo attiviamo e lo impostiamo per l'utilizzo come background sul main engine. Utilizziamo poi il registro REG_BG0CNT del BG0 per attivare un background di tipo testo di 32x32 tiles (BG_32x32), in modo che supporti 256 colori (BG_COLOR_256), dove la mappa sia memorizzata in Map Base 0 (BG_MAP_BASE(0)) e il tileset sia memorizzato a partire da Tile Base 1 (BG_TILE_BASE(1)). Da ricordare che REG_BG0CNT (più in generale REG_BGnCNT, dove n indica il numero del background, da 0 a 3), così come VRAM_A_CR e REG_DISPCNT, sono puntatori a indirizzi di memoria; per questo motivo c'è bisogno di dereferenziarli per avere accesso al contenuto di quelle zone di memoria. Vediamo ora lo stesso esempio, ma utilizzando il nuovo set di funzioni. program Mode0_b; {$mode objfpc} uses ctypes, nds9; var bg0: integer; begin // Attivazione del Mode 0 e del Background 0 videoSetMode(MODE_0_2D or DISPLAY_BG0_ACTIVE); vramSetBankA(VRAM_A_MAIN_BG); // Impostare il Background 0 attraverso la funzione BgInit bg0 := BgInit(0, BgType_Text8bpp, BgSize_R_256x256, 0, 1); while true do; // loop infinito! end. La funzione videoSetMode() si occupa di impostare la modalità video Mode 0 (tramite MODE_0_2D) e di attivare il background 0 (con DISPLAY_BG0_ACTIVE). Dovreste già conoscere il significato di or; In caso contrario, consideratelo come una semplice addizione, del tipo "Attivare il Mode 0 2D e il background 0". La funzione vramSetBankA() serve ad impostare il banco di memoria A in modo tale che la sua memoria possa essere utilizzata come background principale (VRAM_A_MAIN_BG). Come è facile notare, la funzione BgInit richiede cinque parametri:
Da tenere presente che ogni map base può immagazzinare una mappa di 32x32 tile, (256x256 pixel) e un singolo background. Per questo motivo, se si ha bisogno di una mappa di 64x64 tile, bisognerà ricordare che la mappa richiederà 4 blocchi nella map base. Allo stesso modo, una mappa di 32x32 tiles su 3 background richiederà 3 blocchi. Il vantaggio più grande nell'usare la funzione BgInit è il controllo sui parametri implementato in essa. Grazie a questo meccanismo, se ad esempio si prova ad usare un background di tipo extended rotation su un background di tipo text, verrà mostrato a schermo un messaggio che indicherà il tipo di errore riscontrato, nonché il file e la riga dove l'errore si è verificato. Lanciando la ROM generata dal codice qui sopra, noterete che sullo schermo non viene mostrato niente. In effetti fino ad ora abbiamo soltanto impostato la console, senza disegnare niente. Come detto in precedenza, avremo bisogno di un tool per convertire i files contenenti la grafica in un formato utilizzabile con il DS. La scelta migliore è GRIT, una utility a linea di comando (disponibile anche con una comoda interfaccia grafica, WIN GRIT) presente sia nella distribuzione del devkitARM che in fpc4nds. Nel nostro caso, supponiamo di avere un'immagine bitmap di 256x256 pixel, con una profondità di colore di 8bpp, contenente la nostra immagine/tileset; vogliamo convertirla in tiles per utilizzarla in una delle modalità grafiche a tiles e, per risparmiare memoria, vogliamo rimuovere le tiles ripetute. La linea di comando da usare con GRIT sarà: grit MyImage.bmp -fts -gt -gB8 -mRtpf Una piccola descrizione:
GRIT è davvero un programma molto potente: il mio consiglio è quello di prendervi il tempo necessario per leggerne il manuale, che contiene anche molti esempi, in modo tale da padroneggiarlo. La linea di comando mostrata in precedenza restituisce due files, MyImage.s e MyImage.h. Il primo è il file che contiene i dati relativi all'immagine, alla mappa e alla palette suddivisi 3 array; il secondo file è un header in c, che ovviamente non possiamo usare direttamente con Free Pascal, ma che ci permette di conoscere alcune informazioni che ci saranno utili. Prima di poter essere utilizzato nel nostro programma in Pascal, il file asm generato da GRIT deve essere assemblato: arm-none-eabi-as -o MyImage.o MyImage.s Convertiamo ora l'header. Aprendo il file MyImage.h con un editor di testo ci apparirà qualcosa di simile: #ifndef GRIT_MYIMAGE_H #define GRIT_MYIMAGE_H #define MyImageTilesLen 16448 extern const unsigned int MyImageTiles[4112]; #define MyImageMapLen 2048 extern const unsigned short MyImageMap[1024]; #define MyImagePalLen 512 extern const unsigned short MyImagePal[256]; #endif // GRIT_MYIMAGE_H La conversione in Pascal di queste linee ci permetterà di accedere ai dati generati da GRIT. Il procedimento è davvero molto semplice: const MyImageTilesLen = 16448; var MyImageTiles: array [0..0] of cuint; cvar; external; const MyImageMapLen = 2048; var MyImageMap: array [0..0] of cushort; cvar; external; const MyImagePalLen = 512; var MyImagePal: array [0..0] of cushort; cvar; external; Come potete vedere, le chiamate contenenti #define sono state tradotte con delle semplici costanti; le altre variabili sono degli array, che è sufficiente dichiarare di un solo elemento. Il compilatore è abbastanza intelligente da stabilirne da solo la dimensione. Ora abbiamo davvero tutto quello che ci serve per creare la nostra demo in Mode 0: program Mode0_c; {$L build/wood.o} {$mode objfpc} uses ctypes, nds9; const woodTilesLen = 16448; var woodTiles: array [0..0] of cuint; cvar; external; const woodMapLen = 2048; var woodMap: array [0..0] of cushort; cvar; external; const woodPalLen = 512; var woodPal: array [0..0] of cushort; cvar; external; var bg0: cint; begin videoSetMode(MODE_0_2D); vramSetBankA(VRAM_A_MAIN_BG); bg0 := bgInit(0, BgType_Text8bpp, BgSize_T_256x256, 0,1); dmaCopy(@woodTiles, bgGetGfxPtr(bg0), woodTilesLen); dmaCopy(@woodMap, bgGetMapPtr(bg0), woodMapLen); dmaCopy(@woodPal, BG_PALETTE, woodPalLen); while true do; // loop infinito end. Analizziamo il codice. Tramite la direttiva $L del compilatore linkiamo il file oggetto contenente la grafica (ricordate? Quello generato con arm-none-eabi-as), quindi inseriamo le variabili e le costanti ricavate dal file header .h generato da GRIT. Inizializziamo quindi la console come visto in precedenza negli altri esempi: il background è di tipo text (anche perché in Mode0 non è possibile utilizzare altri tipi di background!); la mappa risiede nel blocco 0, mentre le tiles sono memorizzate nel blocco 1. L'ultimo passo è quello di copiare la grafica, la mappa e la palette nelle giuste locazioni di memoria. A tale scopo utilizzeremo la funzione dmaCopy(), che nel 99% dei casi è il metodo più veloce per copiare dati da una regione di memoria all'altra:
Con opportuni accorgimenti si potrebbe utilizzare anche la funzione pascal move(): Move(MyImageTiles, bgGetGfxPtr(bg0)^, MyImageTilesLen); Move(MyImageMap, bgGetMapPtr(bg0)^, MyImageMapLen); Move(MyImagePal, BG_PALETTE^, MyImagePalLen); oppure la funzione c memcpy(): memcpy(bgGetGfxPtr(bg0), @MyImageTiles, MyImageTilesLen); memcpy(bgGetMapPtr(bg0), @MyImageMap, MyImageMapLen); memcpy(BG_PALETTE, @MyImagePal, MyImagePalLen); Solitamente move e memcpy sono da 2 a 5 volte più lente di dmaCopy nella velocità di scrittura. Bisogna però segnalare che, in alcuni casi specifici, dmaCopy potrebbe non essere il modo più veloce per copiare i dati. Non c'è comunque bisogno di preoccuparsi, perché per i nostri scopi è più che sufficiente.
Muovere e ruotare i backgroundUna delle caratteristiche più interessanti del Nintendo DS è la possibilità di muovere, scalare e ruotare i background direttamente dall'hardware della console. Effettuare lo scrolling di un background richiede una semplice, singola chiamata ad una procedura: bgScroll(id, dx, dy: integer);
oppure l'equivalente: bgSetScroll(id, dx, dy: integer);
Queste procedure richiedono come parametri l'ID del background restituito da bgInit (o da bgInitSub) e il valore in pixel dello spostamento desiderato del background lungo gli assi x e y. Potreste nel caso utilizzare anche: bgScrollf(id, dx, dy: integer);
oppure l'equivalente: bgSetScrollf(id, dx, dy: integer);
In questo secondo caso dx e dy devono essere dei valori fixed point, in virgola fissa. Modifichiamo l'ultimo esempio visto per provare a muovere il background: while true do // loop infinito begin bgScroll(bg0, 1, 1); // scrolling del background bgUpdate(); // aggiorna il background swiWaitForVBlank(); // attende il VBlank per evitare // il flickering end; La procedura bgScroll() da sola non è sufficiente per muovere il background. Occorre chiamare anche la procedura bgUpdate(), che aggiorna lo stato dei registri impiegati nello scrolling. Nel codice precedente viene introdotta una nuova, utile procedura: swiWaitForVBlank(). Lo schermo del DS funziona come un qualsiasi schermo: una sorta di "pennello" passa su ogni pixel, colonna per colonna, riga per riga, e copia il framebuffer dalla memoria video allo schermo. Questo compito richiede circa 1/60 di secondo, quindi è buona norma attendere che il framebuffer sia completamente copiato sullo schermo prima di cambiarlo. Il DS ci viene in aiuto per mezzo di un interrupt, che viene lanciato ogni volta che il redraw dello schermo viene completato. La procedura swiWaitForVBlank() si occupa di attendere questo evento, fermando il flusso dell'esecuzione fino a quando l'interrupt non si attiva. Come si può vedere dalla esecuzione dell'esempio, il background viene ripetuto infinitamente. Quando viene raggiunto il limite della mappa, lo scroll ricomincia dal lato opposto. Negli esempi precedenti abbiamo usato un singolo layer, ma ne abbiamo altri tre da utilizzare. Nel prossimo (e ultimo, per il Mode 0) esempio posizioneremo sullo schermo tre layer, utilizzando trasparenza e scrolling. Da ricordare che i layer utilizzano un sistema di priorità che va da 0 (il livello - per così dire - più vicino al vetro dello schermo, ed è il layer più in alto) a 3 (il layer più in profondità).
Vediamo di commentare il codice dell'esempio. Come prima cosa sono stati linkati i file oggetto contenenti la grafica e le mappe, come già visto nell'esempio precedente. Sono state quindi dichiarate variabili e costanti generate da GRIT. Alle righe 43-45 sono stati creati i tre background. Da prestare particolare attenzione ai valori assegnati alle tre base map e alle tre tile map. Le righe 47-55 si occupano di copiare i dati nelle giuste locazioni di memoria. Notare che la palette è unica per tutti e tre i background, quindi viene copiata soltanto una volta. La palette infatti è stata ottimizzata per utilizzare 256 colori, suddivisi in 16 palette di 16 colori ciascuna. In questo caso stiamo utilizzando soltanto le prime 3 minipalette. Il codice nella linea 60 si occupa di eseguire lo scroll del background "mounts" di (1 shl 8), cioè un pixel per frame; il background "clouds" (riga 61) invece verrà spostato di un valore pari a -(1 shl 6), che equivale a un pixel ogni 4 frames, ma nella direzione opposta rispetto al movimento delle montagne (tramite il segno "-"). A questo punto non ci rimane che compilare l'esempio e ammirare la scena, di cui potete ammirare una cattura in figura.
Un'ultima cosa da dire sui background riguarda la possibilità di leggere la loro priorità e cambiarla al volo, nel caso in cui si volesse spostarne uno in primo o secondo piano: function bgGetPriority(id: cint): cint; procedure bgSetPriority(id: cint; priority: cuint); dove id è il valore restituito dalla funzione bgInit(). Nel nostro codice di esempio, le nuvole sono visualizzate davanti alle montagne; nel caso in cui volessimo spostarle dietro, basterebbe inserire questa riga di codice appena dopo la creazione dei background (dopo le righe 43-45): bgSetPriority(clouds, 2);
Per commenti e chiarimenti: http://www.lazaruspascal.it/index.php?topic=78.0
Se avete seguito gli articoli precedenti, dovreste avere una vaga idea sul funzionamento della memoria e delle modalità video sul Nintendo DS. Vedremo ora come utilizzare le informazioni apprese fino a questo punto, iniziando a scrivere del codice. Nel nostro primo esempio inizializzeremo la console in modalità framebuffer, quindi proveremo a disegnare qualcosa sullo schermo. Per prima cosa bisogna aggiungere ctypes e nds9 nella clausola uses: si tratta di due units, la prima contenente alcune definizioni di tipo che possono essere utili nella conversione di codice dal c al Pascal (nota: tutte le informazioni sull'argomento che troverete in rete sono scritte in c/c++), la seconda che ci permette di utilizzare libnds, la libreria per il Nintendo DS. Nel blocco begin end principale posizioneremo il loop principale del programma; occorre quindi impostare una modalità video: le funzioni dedicate a questo compito sono videoSetMode() per il motore principale e videoSetModeSub() per il motore secondario. Tali funzioni accettano come argomento le definizioni delle varie modalità grafiche, come possiamo vedere nella relativa tabella proposta nell'articolo precedente. Nel nostro caso abbiamo deciso di impostare la modalità framebuffer, mappata direttamente sulla memoria principale. Il passo successivo, probabilmente il più importante, è quello di assegnare della memoria per utilizzarla come framebuffer. Per questo compito possiamo utilizzare diverse funzioni, una per ogni banco di memoria: procedure vramSetBankA(a: VRAM_A_TYPE); procedure vramSetBankB(b: VRAM_B_TYPE); procedure vramSetBankC(c: VRAM_C_TYPE); procedure vramSetBankD(d: VRAM_D_TYPE); procedure vramSetBankE(e: VRAM_E_TYPE); procedure vramSetBankF(f: VRAM_F_TYPE); procedure vramSetBankG(g: VRAM_G_TYPE); procedure vramSetBankH(h: VRAM_H_TYPE); procedure vramSetBankI(i: VRAM_I_TYPE); function vramSetMainBanks(a: VRAM_A_TYPE; b: VRAM_B_TYPE; c: VRAM_C_TYPE; d: VRAM_D_TYPE) : cuint32; procedure vramRestoreMainBanks(vramTemp: cuint32);
Come si può facilmente intuire, le procedure vramSetBankX(...) servono ad impostare il banco specificato dalla lettera X; la funzione vramSetMainBanks(...) invece viene utilizzata per impostare contemporaneamente i banchi A, B, C e D. La particolarità di questa funzione è quella di restituire la configurazione precedente dei banchi A, B, C e D che, eventualmente, potrà essere passata alla funzione vramRestoreMainBanks per ripristinare l'impostazione precedente della memoria video. I parametri da passare a queste funzioni sono rappresentati dalle locazioni di memoria alle quali vogliamo che il banco punti. Il compito ci viene facilitato ancora una volta dalla libnds tramite una serie di costanti che rappresentano, appunto, le locazioni di memoria che possono essere utilizzate. Un elenco di queste costanti è indicato nelle tabelle dell'articolo sulla gestione della memoria video. Nel nostro esempio utilizzeremo una modalità framebuffer mappata sulla memoria principale. Il codice dunque sarà: program demo; {$mode objfpc} uses ctypes, nds9; begin videoSetMode(MODE_FB0); // imposta la modalità framebuffer vramSetBankA(VRAM_A_LCD); // assegna il banco A di memoria video al framebuffer while true do; // loop infinito! end. Compiliamo l'esempio e, lanciando la ROM ottenuta sul NDS o sull'emulatore, potremo ammirare uno splendido... schermo nero! Niente paura, è esattamente quello che abbiamo chiesto alla console. Ora che tutto è stato impostato correttamente possiamo provare a disegnare qualcosa sullo schermo, accedendo direttamente alla memoria video, come se si trattasse di un array: program demo; {$mode objfpc} uses ctypes, nds9; var i: integer; begin videoSetMode(MODE_FB0); vramSetBankA(VRAM_A_LCD); for i := 0 to (256 * 192) - 1 do VRAM_A[i] := integer(RGB15(0, 0, i mod 32)); while true do; // loop infinito! end.
Il loop esegue un ciclo attraverso tutti i pixel dello schermo (il DS ha uno schermo di 256*192 pixel) e assegna una tonalità di blu ad ognuno di essi, scrivendo direttamente sul banco VRAM_A. La funzione RGB15() richiede tre parametri, rispettivamente per la componente rossa, verde e blu del colore che vogliamo ottenere. Ogni valore occupa 5 bit, con valori che vanno da 0 a 31; la funzione trasforma questi tre parametri in un valore di 15 bit, adatto ad essere copiato in memoria.
Per commenti e chiarimenti: http://www.lazaruspascal.it/index.php?topic=78.0
Per mostrare la grafica sullo schermo, il Nintendo DS utilizza una struttura a strati, chiamati anche layer o background. Il programmatore ha a disposizione un determinato numero di background che possono essere mostrati contemporaneamente su ciascuno dei due schermi, a seconda della modalità grafica impostata. Esistono 5 tipologie differenti di background:
Una cosa importante da sapere è che ognuno dei due motori grafici può mostrare contemporaneamente a schermo un numero massimo di 4 background. Inoltre il motore principale può muovere delle scene 3D, ma solamente sul background 0. In aggiunta ai layer per i background esiste un ulteriore layer che viene utilizzato per gli sprites, cioè tutte quelle parti grafiche in movimento che sono tipiche dei giochi 2D: si pensi alle navicelle spaziali di Space Invaders oppure ai fantasmini di Pacman. Parleremo più approfonditamente della gestione di questo layer più avanti.
Le modalità graficheCome già accennato, lo schermo del Nintendo DS mette a disposizione diverse modalità grafiche di funzionamento, ognuna delle quali dotata di una differente combinazione di background.
Nella tabella seguente sono schematizzate tutte le modalità grafiche, con le tipologia di background disponibili e il motore (main o sub) con il quale si possono utilizzare.
Come si è già accennato, la modalità 3D è disponibile nativamente solo sul motore principale e solo sul background 0; inoltre è possibile attivare il 3D in ogni modalità video, eccezion fatta per la modalità framebuffer. Utilizzando degli artifici è possibile visualizzare delle scene tridimensionali su entrambi gli schermi, ma al costo di un dimezzamento del frame rate.
Per commenti e chiarimenti: http://www.lazaruspascal.it/index.php?topic=78.0 Pagine: [1] 2 |
Free Pascal per NDS: come scaricare i sorgenti da SVN e ricompilarli da Legolas Impostare Lazarus per l'utilizzo con Freepascal per Nintendo DS da Legolas [0] Free Pascal per NDS: Primi passi da Legolas [1] Free Pascal per NDS: Gestione dei files da Legolas [2] Free Pascal per NDS: Gestione della memoria video da Legolas [3] Free Pascal per NDS: i tipi di background e le modalità grafiche da Legolas [4] Free Pascal per NDS: configurazione dei banchi di memoria da Legolas [5] Free Pascal per NDS: background a tiles e mappe da Legolas [7] Free Pascal per NDS: gli sprites da Legolas [6] Free Pascal per NDS: rotazione e zoom dei background da Legolas Free Spider concetti base e prima applicazione da xinyiman L'oggetto request da xinyiman L'oggetto response da xinyiman L'oggetto TSpiderAction da xinyiman L'oggetto TSpiderTable da xinyiman L'oggetto TSpiderForm da xinyiman L'oggetto TSpiderPage da xinyiman Il ciclo di vita di una CGI da xinyiman Richieste simultanee da xinyiman Le sessioni utente da xinyiman Download/Upload File da xinyiman Smart Module Loading Design da xinyiman Performance da xinyiman Free Spider e database da xinyiman JQuery UI da xinyiman JQuery - Oggetto template da xinyiman JQuery - Oggetto JQGrid da xinyiman Redirect da xinyiman ExtPascal: panoramica sulle licenze da nomorelogic ExtPascal: Installazione e configurazione da nomorelogic ExtPascal: Installazione e configurazione / ExtPascal: Installation and configuration da nomorelogic Le griglie in ExtPascal - ExtPascalToolkit / Grids into ExtPascal - ExtPascalToolkit da xinyiman |