Guida libreria SDL di Giovanni Di Liberto aka Oligoatria 2006-2007

Pagina creata da Marika Bartoli
 
CONTINUA A LEGGERE
Guida
                                libreria SDL
                                       di
                               Giovanni Di Liberto
                                 aka Oligoatria
                                   2006-2007

Introduzione

Cos’è la libreria SDL: Simple DirectMedia Layer - SDL, è una libreria multipiattaforma
per lo sviluppo di applicazioni multimediali. In parole semplici, si tratta di un’utile
concentrazione di funzioni riguardanti grafica, suoni, eventi, thread, e altro.
Queste si pongono tra l’hardware e l’applicazione, rendendo così possibile la creazione di
videogiochi, demo, emulatori, etc., molto performanti.
        Creata nel 1997 da Sam Lantinga, è stata utilizzata per effettuare il port di alcuni
famosi videogiochi dal sistema Windows a Linux. La libreria è open-source; in particolare è
rilasciata sotto licenza GNU LGPL (GNU LESSER GENERAL PUBLIC LICENSE – per
informazioni rimando al link http://www.gnu.org/licenses/lgpl.html), dunque può essere
utilizzata per lo sviluppo di applicazioni commerciali, anche closed-source, a patto che le
modifiche apportate al codice sorgente del software stesso vengano rese pubbliche e che sia
inserito un linking dinamico alla libreria stessa.

Requisiti minimi per affrontare questa guida:
              • Conoscenza discreta del C++;
              • M. Visual C++ (solo per la compilazione, senza modifiche, dei sorgenti);
              • Una release completa SDL, la mia è la versione 1.2.9.0;
              • Tanta voglia di imparare e sperimentare.

Il mio apprendimento: Il mio primo problema, quando ho deciso di provare ad utilizzare
questa libreria, è stato trovare una guida che mi facesse procedere passo passo in via pratica
(un po’ di teoria l’avevo già studiata e ciò che mi interessava era solo “disegnare un cerchio
sullo schermo”). Purtroppo ho trovato solo guide tecniche infinite in inglese, mentre in
italiano solo brevissimi tutorial. I sorgenti presenti in rete erano molto complicati (non tutti
solo in apparenza) e così il mio apprendimento è stato lento e pieno di lacune.
       La svolta è stata quando mi sono reso conto che la mia versione 1.2.9.0 aveva un
fastidioso bug riguardo la trasparenza delle immagini. Inoltre le guide che stavo studiando
non mi fornivano delle spiegazioni esaustive su tutte le possibilità della release. Così mi
sono deciso a personalizzare un po’ la libreria.

Perché questa guida: Sulla rete sono disponibili così tanti suggerimenti sull’SDL che ho
pensato: “copia e incolla, metto un titolo e divento famoso”! A parte gli scherzi, sono
convinto che ci sia molta gente interessata alla grafica, in particolar modo se mirata alla
creazione di videogiochi, e credo che l’SDL sia un ottimo punto di partenza per chiunque
intenda poi approfondire l’argomento programmazione grafica, sia 2D che 3D. Ovviamente
non è sufficiente limitarsi all’utilizzo delle funzioni, ma è necessario capirne il
funzionamento per poterle utilizzare al meglio e, se necessario, ampliarle.
       L’obiettivo di questa guida è fornire i concetti chiave e le relative funzioni SDL
per permettere di proseguire da soli, approfondire e implementare le proprie idee,
ancora carichi di interesse.

Quali possibilità fornisce la libreria SDL?: Ci vengono messe a disposizione molte
funzioni, raggruppabili in sette categorie principali:
              • Video
              • Gestione delle finestre
              • Gestione degli eventi
              • Timer
              • Multi-threading
              • Audio
              • Interfaccia CD-Audio

Nota fondamentale: Gli esempi relativi a questa guida sono scaricabili dal sito
http://www.pierotofy.it, sezione C++. E’ sufficiente cercare SDL e nella lista saranno
presenti i file SDL_Example* (1, 2, 3, …). La guida e gli esempi sono disponibili anche nel
sito http://www.oligoatria.it.
Perché usare l’SDL

       Nella rete sono disponibili molte soluzioni al nostro desiderio di realizzare
animazioni, screensaver, giochi o altro:
      • applicazioni che si concludono in eseguibili, come ad esempio Game Maker o
         Game Factory;
      • software come Flash, che necessitano anche di un player per eseguire il progetto
         creato;
      • librerie grafiche + proprie capacità di programmatore.

        Non intendo sminuire in alcun modo il lavoro svolto tramite software dei primi 2 tipi
elencati, in quanto ho lavorato per anni con Game Maker e ho avuto qualche esperienza in
Flash (ed il suo comodo action script); mi sento però di poter affermare che lavorare
utilizzando librerie grafiche, o loro evoluzioni in motore grafico, tramite qualsiasi
linguaggio di programmazione è decisamente più complesso e istruttivo, senza contare che
fornisce la possibilità di realizzare un software performante, portabile e di qualità.

        Per capire perché credo sia utile imparare a conoscere ed utilizzare questa libreria è
importante distinguerla tra le altre di grande importanza (premetto che parlerò di quello di
cui ho esperienza pratica, dunque nulla togliere a quelle che non conosco):
       • Allegro
          (da http://www.gameprog.it/?resource=358)
          “La libreria ha questi pregi:
          - Grande portabilità
          - Grande ottimizzazione del codice sotto Dos (motivo per il quale è molto usata per gli
          emulatori)
          - Sorgenti disponibili e modificabili a piacere
          - Gestisce anche periferiche di nuova concezione (force feedback)
          - Semplifica notevolmente la vita del programmatore di videogiochi/emulatori
          - Esistono TANTISSIMI pacchetti sviluppati con Allegro per gestire un sacco di cose
          (grafica 3D con Open-GL, nuove istruzioni dei processori, Font sfumati, funzioni
          ottimizzate per i tile,
          Moduli sonorie (.mod, .xm e Mp3) e pensate, anche un piccolo browser HTML!)

          La libreria ha questi difetti
          - Il supporto 3D è molto limitato, ma esiste un plug-in per utilizzare le Open-GL con le
          funzioni 3D di Allegro.
          - Sotto Windows la libreria è un semplice Wrapper per DirectX, e questo purtroppo mina
          molto le sue prestazioni (l'ottimizzazione spinta è per la versione DOS).
          - Le versione più nuova (WIP) non è ancora perfettamente debuggata per tutti i sistemi.”

             Sono d’accordo con quasi tutta la descrizione, tranne che per il fatto che troppo
          poco vengono fatti pesare i difetti.. oltre che per il fatto che SDL non è da meno,
          riguardo gli emulatori!

       • SDL
            Rispetto ai pregi di ALLEGRO modificherei solo la semplicità, che non è più
         notevole ma non eccessiva. Il supporto 3D è decisamente migliore (anche se
sconveniente) e, con un po’ di impegno, è possibile controllare più che pienamente
           le operazioni anche a basso livello. L’unico aspetto da curare molto è
           l’ottimizzazione.
               Si può realizzare di tutto e di più, dal gioco allo screensaver all’emulatore. Ed è
           proprio un emulatore open-source che mi ha nuovamente sorpreso poco tempo fa;
           è facilmente reperibile in rete, si chiama DOSBox (ho scaricato la versione 0.70):
           emula dos 5 in maniera quasi impeccabile, così da poter utilizzare vecchi giochetti
           – o altro – a prestazioni ottimali.

       • OpenGL
             Si tratta di una specifica che definisce API per più linguaggi e sistemi per la
         realizzazione di applicazioni 2D o – principalmente - 3D. E’ incredibilmente
         performante e con la possibilità di lavorare bene sia ad alto che a basso livello.
             E’ la mia passione del momento, ma sono convinto che sia molto importante
         imparare prima l’SDL e solo in seguito l’OpenGL! E’ utile prendere
         dimestichezza con la mentalità grafica 2D prima di passare alla terza dimensione,
         altrimenti si rischia di andare incontro a malintesi, e ad avere grosse lacune che si
         rischierebbe di non colmare mai. Invece imparare i concetti dell’OpenGL partendo
         da una base di SDL renderà le cose molto più naturali.

       • DirectX
             E’ qualcosa in più dell’OpenGL, ovvero, non si occupa solo dell’aspetto
         grafico, ma anche di quello dei suoni, della riproduzione video, delle periferiche di
         input e molto altro. Utilizzando le DirectX si può concludere un’applicazione
         completa, con l’OpenGL no, in quanto mancherebbero aspetti fondamentali,
         colmabili attraverso altre librerie o singole funzioni, comunque esterne al progetto
         OpenGL. Per questo aspetto le DirectX sono simili alla libreria SDL, nel senso
         che conglobano più aspetti.
             Problema.. è sviluppato dalla Microsoft, quindi è, diciamo, esclusivo di
         Windows, a differenza dell’OpenGL che presenta un’architettura nota e può essere
         sfruttata da più piattaforme.

       Ritengo fondamentale sottolineare la differenza profonda tra le librerie descritte
sopra: OpenGL e Direct3D sono API grafiche che offrono un'astrazione software della GPU o della
scheda video; queste hanno sostituito i render software come le API Glide. DirectX, SDL, OpenAL
(non citata sopra) sono librerie a basso livello che offrono un accesso diretto anche ad altri
componenti hardware (suoni, periferiche di input, …). Quindi si può realizzare un software che
sfrutta le OpenGL per la grafica e l’SDL, o le DirectX, per tutto il resto. Per questo è necessaria una
conoscenza non solo del codice, ma anche dei concetti base stessi.
Come impostare un progetto SDL con Visual Studio 6.0

Copia delle librerie: Puoi scaricare la libreria SDL precompilata dal sito
http://www.libsdl.org/download.php, oppure compilarla da te. Non tratterò quest’ultimo caso
dato che lo ritengo un passaggio superfluo – inutile spiegare qualcosa che interessa solo chi
ne è già capace.

Creare ed impostare un nuovo progetto per l’SDL:
   •   Creare una Win32 Application;
   •   Se non presente (ne dubito) creare un file .cpp principale per il progetto;
   •   Nel menu: “Project|Settings|C/C++ tab|Code Generation|Runtime Library“
       imposta il ‘C runtime’ su "Multithreaded DLL" ;
   •   Nel menu: “Project|Settings|C/C++ tab|Preprocessor|Additional include
       directories“ aggiungi la directory ‘include’ dell’SDL (quella con i .h);
   •   Copia il file ‘SDL.dll’ nella directory del progetto
   •   ʎ Aggiungi i file ‘SDL.lib’ e ‘SDLmain.lib’ al tuo progetto (non è necessario copiarli nella
       cartella come il .dll) – per aggiungerli, click destro sul progetto, seleziona ‘Add file sto
       project’;
   •   ʎ In alternativa al precedente e forse necessario al rilascio della release finale, si possono
       aggiungere i due file nelle opzioni del linker (Project|Properties|Linker|Command Line),
       aggiungendone i nomi nelle ‘Additional Options’;
   •   Includi ‘SDL.h’ nel file .cpp principale.

      Attenzione: per poter utilizzare l’eseguibile, d’ora in poi, sarà sempre necessaria la
presenza del file ‘SDL.dll’ nella sua stessa directory.

       Per impostare il progetto con un IDE diverso da Visual Studio non cambia
praticamente nulla: ovvio che il menù sarà diverso, ma le operazioni da eseguire sono le
stesse (dopo averlo fatto la prima volta vi potreste domandare: “mmm, ma cosa ho fatto?”,
se è così andatevi a studiare il funzionamento di un compilatore e gli argomenti correlati).
La prima dialog
        La prima cosa da imparare è l’inizializzazione video. Una delle comodità di questa
libreria è la semplicità delle operazioni necessarie alla creazione di una dialog.

Inizializzazione: per utilizzare la libreria è necessario inizializzare i sottoinsiemi interessati
(SDL_INIT_VIDEO, SDL_INIT_AUDIO, SDL_INIT_CDROM, SDL_INIT_TIMER)
tramite la funzione SDL_Init(FLAGS). Al termine dell’applicazione è necessario chiamare la
funzione SDL_Quit() per terminare questi sottosistemi; un metodo alternativo per la chiusura
è la funzione atexit(SDL_Quit), solitamente posta subito dopo l’inizializzazione, per chi ha
paura di dimenticarsi la chiamata alla funzione di chiusura. In questo caso, però, nel main si
dovrà utilizzare l’exit().
        Subito dopo possiamo inizializzare la finestra. In un’applicazione multipiattaforma
non si può prendere nessuna decisione a priori; è necessario, invece, gestire il software in
relazione alle possibilità hardware della macchina e al sistema operativo. A questo proposito
la libreria SDL mette a disposizione una serie di funzioni per ottenere informazioni
riguardanti i driver video (SDL_GetVideoInfo()) e le potenzialità dell’hardware video
(SDL_GetVideoInfo()). Non mi soffermo però su questo; vediamo invece i passi base per la
creazione di una dialog:

SDL_Surface *screen;
screen = SDL_SetVideoMode( x, y, bits, Flags );

if(! screen ) return -1; // gestione del possible errore

SDL_LockSurface(screen);
SDL_Surface  questo sarà d’ora in poi il tipo fondamentale, infatti è questo che contiene
le informazioni relative a un’immagine. Non si deve però confondere questo tipo con la
dialog, la quale viene “collegata” ad una variabile di questo tipo; infatti molto spesso si
caricano immagini non visualizzate completamente o del tutto, cosa che avremo modo di
approfondire con gli sprite.

SDL_SetVideoMode( int x, int y, int bits, int Flags )  questa è appunto la funzione che
alloca la nuova SDL_Surface e crea una nuova Dialog a essa riferita.
  x – larghezza della superficie
  y – altezza della superficie
  bits – numero bit di colori
  Flags – SDL_OPENGL | SDL_FULLSCREEN | SDL_DOUBLEBUF | ...
SDL_LockSurface  si rende utile quando si inizia lo sviluppo di funzioni più complesse;
il suo scopo è quello di bloccare eventuali operazioni di scrittura non desiderate, che
potrebbero causare errori.
       Piccolo fastidio: per ogni blocco di operazioni di disegno (blit) sarà necessario
sbloccare e poi riboccare la superficie, con appunto SDL_UnlockSurface(SDL_Surface*) e
SDL_LockSurface(SDL_Surface*)
Se si intende caricare un’immagine la superficie si adatterà al formato della stessa;
al contrario è necessario definirne i parametri base se la propria intenzione è di colorare
“manualmente” l’immagine, o comunque senza appoggiarsi a file esterni.
PrimaryLayer = SDL_CreateRGBSurface( (Flags & SDL_HWSURFACE), x, y, bits,
screen->format->Rmask,
screen->format->Gmask,
screen->format->Bmask,
screen->format->Amask
);
if(! PrimaryLayer ) return -1; // gestione del possible errore
Creare un ciclo di gioco
        Per rendere utilizzabile una Dialog, utilizzando il procedimento classico di
sfruttamento della libreria SDL, è necessaria la creazione di un ciclo di RunTime (1);
all’interno di esso si dovrà svolgere l’intero programma, o una parte.
        Si tratta di re-implementare dalle basi la programmazione oggi definita ad eventi; in
realtà non è nulla di logicamente complicato. Mi spiego con un esempio:
SDL_Event event, test_event;
Uint8 *keys;
for( ; ; )                         // ciclo principale di gioco
{
        SDL_Delay(frame_skip);       // rallenta il ciclo di RunTime

      if (SDL_PushEvent(&test_event) == 0)         // se si verifica un qualsiasi evento
                                                   // causato dall’utente/
      {
             SDL_PollEvent(&event);  // “cattura l’evento”; concretamente, aggiorna la
                                     // rilevazione eventi da periferiche esterne;
             keys = SDL_GetKeyState(NULL);            // aggiorna l’array puntato da keys
             if ( keys[SDLK_ESCAPE] == SDL_PRESSED ) // esco dal ciclo su pressione
                                                              // di Escape
                     break;
      }
}
SDL_Delay(int)  è un comando fondamentale, anche concettualmente (vedi il capitolo “I
Timer”: se viene omesso il ciclo di RunTime rischia di essere eseguito con una frequenza
tale da “moltiplicare gli eventi” creando effetti sgradevoli. Per essere più chiari faccio un
esempio: in un menù premendo SU si sposta un cursore di una casella, se il RunTime è così
veloce da fare più di un ciclo mentre premo il tasto potrei ritrovarmi con il cursore sul
soffitto  deve essere limitato, o meglio gestito attraverso un parametro variabile intero
(FrameSkip) impostabile manualmente o automaticamente per avere prestazioni ottimali su
ogni PC. Tutto questo senza considerare che, se non viene utilizzata questa funzione, il
software assorbe una parte considerevole della CPU, rendendo così instabile il sistema.

if (SDL_PushEvent(&test_event) == 0)  non è necessario che, ad ogni ciclo, si ripetano
tutti i controlli sugli eventi, così, per lasciare un po’ di respiro al processore, è stato
“inventato” un controllo generico necessario al funzionamento in RunTime del sistema ad
eventi.

if (keys[SDLK_ESCAPE] == SDL_PRESSED)  gestione dell’evento “tasto ESCAPE
premuto”; nel caso riportato se la condizione è verificata si esce dal ciclo. L’evento
SDL_PRESSED è verificabile in keys (di tipo Uint8) grazie all’operazione di aggiornamento
svolta nella riga precedente ( keys = SDL_GetKeyState(NULL) ).
La lista di tutti gli eventi DA tastiera e mouse è presente nei file
SDL_Events.h e SDL_keysym.h; per completezza l’ho riportata
nell’appendice1.

  (1) Nella programmazione visuale (un esempio sono Java, Delphi, Visual Basic, le MFC app. in Visual C++)
      questo ciclo è presente per definizione, anche se non è immediato il suo riconoscimento nel sorgente –
      programmazione ad eventi.
Un primo esempio di visualizzazione di un’immagine

    Una volta impostato il ciclo principale di gioco non è difficile imparare a far visualizzare
un’immagine BMP all’utente; cominciamo con la teoria:
    • Si deve caricare la BMP in una SDL_Surface (*image);
    • Si deve copiare *image in una determinata posizione (x=20, y=20) nella
      SDL_Surface principale (*screen).

    Quest’ultima operazione consiste nel prendere ogni pixel dell’immagine e copiarla in
*screen, dunque si tratta di un semplice ciclo annidato (questo verrà approfondito per
spiegare la trasparenza).

Il codice seguente è fatto per essere inserito appena prima del ciclo di gioco

SDL_Surface *image;
image = SDL_LoadBMP( “file_name” ); // carica l’immagine nella superficie, la quale
                                     // viene allocata automaticamente

SDL_Rect dst; // struttura composta da x,y,w (larghezza), h (altezza)
               // è necessaria in SDL_BlitSurface per indicare ‘rettangolo’ sorgente(src)
               // o destinazione(dst)
dst.x = 20 ; // nel caso di rect di destinazione è sufficiente indicare x e y
dst.y = 20 ;

SDL_UnlockSurface(screen); // sblocco screen // necessario per poter modificare questa
                            // Surface
  // SDL_BlitSurface(SDL_Surface*image, SDL_Rect *src, SDL_Surface*screen,
SDL_Rect *dst);
SDL_BlitSurface(image, NULL, screen, &dst)

SDL_LockSurface(screen); // riblocco screen

  //SDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h);
SDL_UpdateRect(screen, 0, 0, 0, 0); // aggiorna la superficie visualizzata
 // i parametri a 0 stanno a significare che l’aggiornamento interessarà tutto lo schermo,
 // altrimenti qualcosa del tipo 1,1,10,50 avrebbe preso il rettangolo con estremi
 // A(1,1) B(11,51)

       Nel caso qui specificato si copia un’immagine BMP caricata da file nella superficie
principale a partire dalle coordinate 20,20.
       Il fatto che il 2° parametro di SDL_BlitSurface sia NULL sta a significare che si
copia l’immagine completa; e’ dunque chiaro che basterebbe passargli un SDL_Rect src per
copiarne solo la parte desiderata.
Per andare sul concreto, faccio l’esempio degli sprite: in molti tipi di gioco le
immagini di un personaggio vengono unite in un unico file; nella fase di disegno se ne
prenderà solo una parte alla volta.
Ma questo lo vedremo nel capitolo degli sprite.. andiamo per gradi.
Muovi l’immagine nello schermo

   Arrivati a questo punto, per riuscire a muovere l’immagine interessata è sufficiente
aggiungere due punti principali:
   • La gestione degli eventi da tastiera.. mi spiego:
      Quando premo la freccia destra, sposto l’immagine a destra, e così via;
   • L’aggiornamento della Superficie e poi della visualizzazione.

    Prima di proseguire ricordo che per indicare la posizione dell’immagine abbiamo
utilizzato la variabile dst (.x e .y).

Qui di seguito riporto il codice del ciclo di gioco modificato ( le parti nuove sono in
grassetto )

for( ; ; )   // ciclo principale di gioco
        {
             SDL_Delay(frame_skip);         // rallenta il ciclo di RunTime

             if (SDL_PushEvent(&test_event) == 0)         //
             {
                    SDL_PollEvent(&event);

                    /* gestione degli eventi da tastiera */
                    keys = SDL_GetKeyState(NULL); // aggiorna la situazione attuale dei
                                                        // tasti premuti o no
                    if ( keys[SDLK_ESCAPE] == SDL_PRESSED ) // esco dal ciclo su
                                                                     // pressione di Escape
                            break;

                    if ( keys[SDLK_UP] == SDL_PRESSED ) dst.y--;
                    if ( keys[SDLK_DOWN] == SDL_PRESSED ) dst.y++;
                    if ( keys[SDLK_LEFT] == SDL_PRESSED ) dst.x--;
                    if ( keys[SDLK_RIGHT] == SDL_PRESSED ) dst.x++;
             }

             SDL_UnlockSurface(screen);               // sblocco screen
             SDL_BlitSurface(image, NULL, screen, &dst);
             SDL_LockSurface(screen);                // blocco screen

             SDL_UpdateRect(screen, 0, 0, 0, 0);
        }
Reblitting: muovere un’immagine su uno sfondo statico

    Con l’esigenza di uno sfondo caricato da una BMP sorgono le prime difficoltà.
    Per poter muovere la nostra immagine su uno sfondo statico è necessario, ad ogni ciclo,
disegnare prima lo sfondo, poi l’immagine interessata (ricordo che l’UpdateRect deve essere
effettuato una sola volta per ogni ciclo, altrimenti si ottengono brutti effetti visivi (1)). Per ora
questo è tutto ciò che ci basta sapere; nel prossimo capitolo analizzeremo il problema del
rallentamento dell’applicazione, ma prima è necessario sperimentare.

   (1) Carichiamo in una nuova superficie la nostra BMP di sfondo;
             SDL_Surface *sfondo;
                   sfondo = SDL_LoadBMP( "sfondo1.bmp" ); // NEW

   (2) Prima del blit dell’immagine da muovere, nel ciclo di gioco, ridisegnamo lo sfondo.
       Nell’esempio: src==NULL: disegniamo l’intero sfondo
                      dst==NULL: lo sfondo verrà disegnato da (0,0)

               SDL_BlitSurface(sfondo, NULL, screen, NULL); // NEW

   (1) Le librerie SDL utilizzano automaticamente un algoritmo per sincronizzare l’UpdateRect con l’aggiornamento
       fisico dello schermo. Se nel ciclo di gioco ci fossero più aggiornamenti visivi della superficie
       (SDL_UpdateRect o anche SDL_Flip, nel caso del DOUBLE_BUFFERING) si visualizzerebbero effetti visivi
       che renderebbero l’applicazione normalmente inutilizzabile.
Reblitting: il parziale è più rapido?

       Uno dei più grandi problemi che ci possono assillare è l’ottimizzazione
dell’applicazione, così da poterla rendere funzionale nei PC più lenti.
       I più attenti, riguardo il capitolo precedente, si saranno chiesti: “Ridisegnando l’intera
superficie ad ogni ciclo di gioco non rallenterà di molto l’applicazione?”. Per capire se
questo è vero tentiamo di realizzare un Reblitting parziale, cioè…
Sappiamo che l’UpdateRect è già ottimizzato, in quanto aggiorna visivamente solo la
superficie modificata con il BlitSurface, quindi perché non migliorare la nostra tecnica di
Blitting?
       L’idea è questa: dobbiamo spostare l’immagine da un’area A ad una B; dopo
aver calcolato l’area B, copiamo in una SDL_Rect temporanea A e, nel ciclo successivo
prima si ridisegnerà lo sfondo solo nell’area A, poi si procederà con il Blitting
dell’immagine in B (chiaramente tutto questo si farà solo se (A != B)). Per sfruttare
questo concetto anche con più oggetti dinamici è sufficiente creare una lista (preferibilmente
puntata) di SDL_Rect che, ad ogni ciclo, conterranno le aree degli oggetti che si stanno
spostando, basterà dunque ridisegnare lo sfondo solo dove c’è movimento.
        La domanda più ovvia alla quale voglio dare una risposta è: “Dov’è il problema?
Implementiamo questo concetto e tutto sarà più veloce”. Non sono d’accordo perché penso
al caso più pessimistico, e cioè ad un’applicazione con un numero di oggetti dinamici tale da
rendere le operazioni di copia delle SDL_Rect temporanee complesse a tal punto da
rallentare l’applicazione. E’ evidente che è più rapido ridisegnare il meno possibile dello
sfondo, ma si deve capire che la funzione SDL_BlitSurface è relativamente lenta, quindi è
meglio richiamarla il meno volte possibile: è più efficace quindi disegnare una volta
l’intero sfondo che molte volte piccoli pezzetti di esso. A sommarsi a questo c’è il tempo
impiegato per la gestione delle aree temporanee, anche se si tratta di un problema minimo in
confronto al richiamare più volte la funzione BlitSurface.
       Ulteriore difficoltà da affrontare: se si intende utilizzare una mappa scorrevole e più
sfondi, si devono gestire contemporaneamente il Reblitting e lo scroll, il che non è cosa da
poco. Nelle applicazioni più avanzate viene calcolato un limite, valicato il quale questa
tecnica diventa svantaggiosa; quindi utilizzo del metodo più vantaggioso.
        Negli esempi preferisco non trattare questo problema in quanto intendo mantenere
una semplicità di codice tale da renderne l’analisi il più facile possibile.
Primo approccio alla trasparenza

        Con gli aspetti visti finora si potrebbe realizzare un menù, o al massimo un tetris,
comunque di scarsa qualità. Le nostre possibilità cambiano radicalmente con l’utilizzo della
trasparenza, tecnica tramite la quale è possibile stampare a video anche figure rotonde o
irregolari.
        Ogni oggetto SDL_Surface può avere un colore di trasparenza diverso e, nel nostro
esempio, utilizzando un’immagine a 8 bit ,ovvero una palette di 256 colori, è sufficiente
utilizzare la funzione SDL_SetColorKey per impostare il colore di trasparenza. La logica è
questa: quando disegno in *screen l’immagine interessata copio solo i pixel diversi da quello
di trasparenza impostato tramite la funzione così richiamata:

       if ( image->format->palette ) {
                     SDL_SetColorKey(image, (SDL_SRCCOLORKEY|SDL_RLEACCEL),
                                             *(Uint8 *)image->pixels);
       }

Questa riga di codice può essere inserita subito dopo
      image = SDL_LoadBMP( "file_name.bmp" ).

Il prototipo della funzione è il seguente:

       int SDL_SetColorKey(SDL_Surface *surface,
                           Uint32 flag, Uint32 key);

        Ho optato per un’immagine ad 8 bit perché a profondità di colore maggiore si
possono riscontrare dei problemi. In particolare con questa versione delle librerie si
visualizza un brutto effetto visivo che “mi ha perseguitato” per parecchio tempo. Per
ovviarlo, invece di cambiare versione della libreria, ho pensato di implementare una mia
personale versione di blitting trasparente. Questa mi si è poi rivelata fondamentale per la
realizzazione di un’applicazione definibile carina, anche perché fornisce altre possibilità,
come ad esempio la rotazione dell’immagine e l’ingrandimento. Purtroppo non è affatto
veloce, quindi ho dovuto cercare altre soluzioni.
        Chi intende provare ad approfondire può passare all’APPENDICE 2, dove spiego
logica e contenuti della mia implementazione.
        Per chi abbia intenzione di utilizzare le funzioni predefinite può facilmente reperirle
nella documentazione ufficiale, nulla di difficile se si possiede la versione giusta della
libreria; quindi potete passare direttamente al capitolo successivo.
        Qui di seguito riporto come utilizzare la funzione SDL_SetColorKey anche con
immagini di profondità di colore maggiore a 8 bit, dunque con i metodi true-color:

        SDL_SetColorKey(image, SDL_SRCCOLORKEY | SDL_RLEACCEL,
                           SDL_MapRGB(image ->format, 0xff, 0x00, 0xff));
(0xff, 0x00, 0xff) – indicano il colore RGB (red==255, green==0, blue==255), ovvero
violetto, spesso come colore di default.
Blit parziale dell’oggetto e introduzione agli sprite

       Dopo aver visto come disegnare immagini utilizzando la trasparenza con colore
chiave, non è difficile avvicinarsi alla mentalità degli sprite. Prima, però, è necessario vedere
come disegnare porzioni di un’immagine sulla superficie.

Blit parziale: Come per indicare le coordinate dello schermo dove disegnare la BMP, si
utilizza una variabile di tipo SDL_Rect:
        SDL_Rect src;
        src.x = 0;
        src.y = 0;
        src.w = 23;
        src.h = 10;
Eseguendo poi la funzione SDL_BlitSurface(image, &src, screen, &dst ) si comunica alla
libreria di disegnare in screen dst.x, dst.y, la porzione dell’immagine image contenuta nel
rettangolo di larghezza 23 pixel ed altezza 10.

Cos’è uno sprite: Per definizione uno sprite è un’immagine bidimensionale che può essere
spostata rispetto ad uno sfondo. Nei primi videogame questo veniva gestito direttamente via
hardware, per ottenere quindi l’effetto di trasparenza.
        Oggi per sprite intendiamo un’immagine 2D, generalmente formata da n frame; questi
vengono visualizzati uno alla volta in successione, in maniera da essere sovrapposti e da
dare l’impressione di una continuità, di un movimento, fluido il più possibile.
        Queste immagini sono caratteristiche di varie tipologie di applicazioni, dai giochi di
ruolo 2D, dove sono utilizzati praticamente per ogni cosa, ad applicazioni con gestione del
testo, dove gli stessi font hanno uno sprite per ogni stile di caratteri, o charset.
        Per essere eloquenti al massimo, è lo stesso sistema adottato per le gif animate,
sostanzialmente.

Come applicare il concetto: Pensando come sviluppare un codice che realizzi questo
concetto, il più facile che mi viene alla mente è il seguente (vedi SDL_Example4):

// Prima del ciclo di loop
/* Variabili riguardanti la gestione degli sprites */
       unsigned int frame_attuale=0;
       SDL_Rect src;
       src.x = (LARGHEZZA_IMMAGINE)*(frame_attuale);
       src.y = 0 ;
       src.w = LARGHEZZA_IMMAGINE;
       src.h = ALTEZZA_IMMAGINE;

// Nel ciclo di loop, prima di disegnare
/* Cambio frame */
        if (++frame_attuale >= NUMERO_FRAMES)
               frame_attuale=0;
        src.x = (LARGHEZZA_IMMAGINE)*(frame_attuale);
/* Disegno la frame */
SDL_BlitSurface_transparent(image, &src, screen, &dst, 0, 0, 0, 1, 0, 0);
Una prima implementazione di motore fisico

         Ho pensato di includere in questa guida anche un piccolo esempio che possa far
capire cosa si può sviluppare utilizzando questa libreria.
         Ora che abbiamo la padronanza della tecnica degli sprite è necessario almeno dare
un’occhiata al movimento, comandato dall’utente tramite tastiera, sullo stile platform; quindi
il classico omino che subisce la forza di gravità.

Prima di tutto dobbiamo dichiarare due nuove variabili:

       /* Gravità */
             int gravita=0;

       /* Direzione */
              bool sinistra=false;

       Poi, nel ciclo di loop togliamo l’effetto in seguito alla pressione della freccia verso il
basso, e sostituiamo a quella verso l’alto:

if ( (keys[SDLK_UP] == SDL_PRESSED) && ((dst.y+ALTEZZA_IMMAGINE)>=y) )
{
        gravita=-20;
}

       L’intento è di far saltare il personaggio, ora vedremo cosa abbiamo sostanzialmente
fatto con questi comandi.

Prima di disegnare, aggiungiamo il codice seguente:

/* Gravità */
      dst.y+=gravita++;
      if ((dst.y+ALTEZZA_IMMAGINE)>=y)
      {
              gravita=0;
              dst.y=y-ALTEZZA_IMMAGINE;
      }

        In questo modo la posizione dell’immagine si sposterà verso il basso di un valore
crescente.. nel mondo reale la crescita è esponenziale, nei platform questa non sempre lo è;
la tipologia di gioco influenza la forza della gravità.
        Come soglia di collisione prendiamo i piedi del personaggio, quindi, essendo che le
coordinate partono dall’angolo in alto a sinistra dello schermo (0, 0), posizione y attuale +
altezza immagine.
        A questo punto si può capire che per avere un salto è sufficiente, alla pressione del
tasto interessato, assegnare alla gravità un valore negativo; è chiaro che tanto più grande sarà
questo numero (in valore assoluto), tanto maggiore sarà la forza del salto.
L’importanza della console

         Spesso mi è capitato di perdere molto tempo nel cercare un problema la cui causa
sembrava introvabile. Intoppi del genere sono alla base del mondo della programmazione, in
particolare web. I linguaggi compilati, come il C++, hanno un fantastico sistema di debug, il
quale facilita sicuramente la ricerca degli errori. Sarebbe però sicuramente meno frustrante
se, al verificarsi di un problema, la sua causa fosse arginata all’interno di poche righe di
codice; queste, certo, sono tanto più ridotte tanto è maggiore la nostra bravura, esperienza ed
intuizione. Non basta, però!
         Il mio consiglio è di sfruttare al meglio la console predefinita. Con questo intendo
dire che dobbiamo fare un po’ di fatica in più e gestire gli errori tramite try/catch, ed in caso
di eccezioni segnalarle con un printf, o cout, nella console. Si possono classificare i tipi di
errori, se il software diventa complesso.
         Oltre alla classificazione e segnalazione degli errori si può utilizzare la console per
segnalare gli eventi, l’accesso a funzioni o condizioni, lo stato di thread, connessioni, suoni.
         E’ molto importante decidere se impostare un progetto sotto questo profilo fin
dall’inizio dello sviluppo, altrimenti poi, quando il codice raggiunge uno sviluppo avanzato,
risulta molto faticoso scegliere i punti giusti dove inserire i controlli, anche perché capita che
non ci si ricorda più come è fatta una funzione, quindi si perde ulteriore tempo.
         Chiaro che, se intenzione dell’autore, la console verrà nascosta nelle release stabili, o
ridotta a funzione di indicatore di caricamento.
Audio

        La libreria SDL gestisce bene anche l’aspetto riguardante i suoni. Nel dettaglio, la
libreria ci mette a disposizione una serie di funzioni che rendono facile la riproduzione di
file audio: SDL_LoadWav(), SDL_OpenAudio(), SDL_PauseAudio(). SDL chiama
automaticamente una funzione di callback che copia i campioni dalla memoria di sistema al
buffer della scheda audio. Questa funzione dev’essere creata dall’utente e il suo indirizzo,
assieme ai valori della frequenza di campionamento e della risoluzione dei campioni, deve
essere specificato nella struttura SDL_AudioSpec:

      typedef struct{
       int freq;
       Uint16 format;
       Uint8 channels;
       Uint8 silence;
       Uint16 samples;
       Uint32 size;
       void (*callback)(void *userdata, Uint8 *stream, int len);
       void *userdata;
      } SDL_AudioSpec;

      freq - Frequenza di campionamento
Il numero di campioni copiati nel buffer della scheda audio al secondo. I valori più comuni
sono 11025, 22050 e 44100. Più il valore è alto, migliore è il suono.

       format - Formato audio (1)
Specifica la dimensione ed il tipo di ogni campione:
              AUDIO_U8 – Campioni di tipo Unsigned 8-bit
              AUDIO_S8 – Signed 8-bit
              AUDIO_U16 o AUDIO_U16LSB – Unsigned 16-bit little-endian
              AUDIO_S16 o AUDIO_S16LSB – Signed 16-bit little-endian
              AUDIO_U16MSB – Unsigned 16-bit big-endian
              AUDIO_S16MSB – Signed 16-bit big-endian
              AUDIO_U16SYS – Sia AUDIO_U16LSB che AUDIO_U16MSB dipendenti
              dal sistema endianness specifico
              AUDIO_S16SYS – Sia AUDIO_S16LSB che AUDIO_S16MSB dipendenti
              dal sistema endianness specifico.

      channels – Numero di canali: 1 mono, 2 stereo
      silence – Valore di silenzio del buffer audio (calcolato automaticamente)
      samples – Dimensione del buffer audio, in campioni
      size – Dimensione, in byte, del buffer audio (calcolato automaticamente)
      callback(..) – Funzone di callback per copiare i campioni nel buffer della
scheda audio
      userdata – Puntatore ai parametri da passare alla funzione di callback.
Riporto qui di seguito un esempio di funzione di callback, presa da http://www.libsdl.org:
#define NUM_SOUNDS 2
struct sample {
    Uint8 *data;
    Uint32 dpos;
    Uint32 dlen;
} sounds[NUM_SOUNDS];

void mixaudio(void *unused, Uint8 *stream, int len)
{
    int i;
    Uint32 amount;

     for ( i=0; i len ) {
             amount = len;
         }
         SDL_MixAudio(stream, &sounds[i].data[sounds[i].dpos],
                      amount, SDL_MIX_MAXVOLUME);
         sounds[i].dpos += amount;
     }
}

Questo invece è un esempio di struttura SDL_AudioSpec.
SDL_AudioSpec fmt;

/* Setta l'audio a 16-bit stereo 22Khz */
fmt.freq = 22050;
fmt.format = AUDIO_S16;
fmt.channels = 2;
fmt.samples = 512;        /* Un buon valore per i giochi */
fmt.callback = mixaudio;
fmt.userdata = NULL;

SDL permette anche la gestione dell’audio da CD-ROM. Riporto qui di seguito la semplice
funzione ricavata dal sito http://www.libsdl.org:

void PlayTrack(SDL_CD *cdrom, int track)
{
    if ( CD_INDRIVE(SDL_CDStatus(cdrom)) ) {
        SDL_CDPlayTracks(cdrom, track, 0, track+1, 0);
    }
    while ( SDL_CDStatus(cdrom) == CD_PLAYING ) {
        SDL_Delay(1000);
    }
}

Si spiega da sola.

    (1) Big, Middle e Little Endian sono le tre tipologie alternative alla memorizzazione di dati con dimensione base
        superiore al byte. Per endianness si intende generalmente l’ordine dei byte.
Applicazioni Multi-threaded

        A volte capita di dover affrontare problemi che trovano soluzione solo con
operazioni svolte in parallelo; purtroppo la gestione di 2 processi pesa parecchio al sistema,
quindi sono stati creati i thread, definibili come “processi leggeri”; ogni processo può avere
più thread, i quali condividono lo stesso spazio di indirizzamento.
        La libreria SDL ci mette a disposizione funzioni per creare thread e per poterli
gestire. Prima di vedere quali sono queste funzioni è necessario sapere a cosa è necessario
prestare attenzione per evitare effetti indesiderati:
   • Prima di tutto pensa a tutte le possibili soluzioni alternative all’utilizzo di un nuovo
       thread;
   • Se non c’è altro modo, o se questo è il più efficace, è bene evitare l’utilizzo di
       variabili globali su thread differenti;
   • Non terminare i threads, utilizza invece un flag e fai in modo che si chiuda
       autonomamente; questo sia per evitare errori di esecuzione che il possibile memory-
       leak;
   • E’ preferibile non fare chiamate SDL Video/Event su più threads.

      SDL_Thread *SDL_CreateThread(int (*fn)(void *), void *data);
Questa funzione permette la creazione di un nuovo thread, avviando la funzione fn con
parametri data.
      void SDL_WaitThread(SDL_Thread *thread, int *status);
Aspetta che un thread finisca; status è il valore restituito dalla funzione main del thread
terminato.
      void SDL_KillThread(SDL_Thread *thread);
Termina un determinato thread.

        SDL permette anche la risoluzione del problema delle variabili condivise tramite
mutex, ovvero il procedimento di sincronizzazione che impedisce l’accesso contemporaneo
di più threads alla stessa risorsa, evitando così errori parecchio fastidiosi. La conoscenza del
loro utilizzo và oltre lo scopo di questa guida.
        Un esempio di operazione da svolgere in multi-threading è la gestione della
comunicazione di rete; in questo caso, a meno che non si intenda bloccare il software in
attesa di un determinato pacchetto, o fino alla scadenza di un timeout, risulta fondamentale
l’utilizzo di un secondo thread che gestisca in maniera autonoma la connessione.
I Timer

        Tutte le operazioni svolte da un computer sono intervallate da un determinato tempo,
denominato clock. Questo dev’essere necessariamente gestito dal programmatore in questo
genere di applicazioni, ovvero dove si mette mano direttamente al codice del ciclo di loop. Il
non affrontare questo argomento compromette sensibilmente l’esecuzione del software, il
quale sfrutterebbe tutta la memoria concessagli, rischiando di mandare in crash il sistema,
oltre che l’applicazione stessa.
        La libreria SDL mette a disposizione delle funzioni per la gestione del tempo, una
delle quali è SDL_GetTicks(), che restituisce il tempo trascorso dall’avvio dell’applicazione
(in ms); anzi, per essere precisi, questo tempo è riferito all’inizializzazione della libreria.
L’altra funzione fondamentale è SDL_Delay(), che attende il tempo, in ms, passatogli come
parametro. Questa funzione è particolarmente precisa, se la rapportiamo ad altre disponibili
nelle librerie standard. Un’altra possibilità fornitaci dalla libreria è quella di creare timer e
collegarci funzioni; si tratta quindi del classico evento OnTimer disponibile in quasi tutti i
linguaggi visuali.

        ATTENZIONE: SDL mette a disposizione funzioni comode e molto utili, ma è
compito del programmatore gestire in maniera efficace quello che nelle applicazioni grafiche
sono chiamati FPS (Frame per second). Non si tratta affatto di un argomento banale, anzi, ci
si sbatte la testa più volte, nello sviluppo di applicazioni grafiche.
SDL sopra OpenGL

         SDL mette a disposizione una serie di funzioni per l’accesso al buffer video, quindi
permette un approccio a basso livello. Tuttavia non ci viene fornita alcuna funzione per il
disegno di primitive. La caratteristica che ci può far dimenticare questa piccola mancanza è
la facile integrazione con la libreria OpenGL (per dettagli a riguardo rimando al capitolo
“Perché usare l’SDL”). Quest’ultima ha la caratteristica di non interfacciarsi direttamente
con il S.O., bensì attraverso un contesto grafico in cui poter disegnare la scena. E’ qui che
entra in campo l’SDL, mettendo a disposizione questo contesto attraverso una serie di
funzioni:

SDL_GL_SetAttribute() – consente l’impostazione degli attributi del contesto grafico (bit per
colore, flag per il DOUBLE_BUFFERING o per il DEPTH_TEST);

In SDL_SetVideoMode() è necessario passare come flag SDL_OPENGL;

SDL_GL_SwapBuffers() – Effettua lo scambio tra back-buffer e frame-buffer;

SDL_GL_GetProcAddress() – Restituisce l’indirizzo di una funzione contenuta in
un’estensione OpenGL.

       Ci sarebbe molto da dire in questo capitolo, ma andrei al di fuori dell’argomento
SDL. C’è già abbastanza da capire in questa libreria. Penso che solo la convinzione di essere
interessati ad approfondire possa portare a qualche risultato; altrimenti lo studio si riduce a
semplice lettura, di scarsa utilità, oltretutto. Il dare un’occhiata può servire solo a spaventare
e ad allontanare la persona da un argomento del quale è meglio pensare più ai concetti che al
codice.
Appendice1 – lista degli eventi da tastiera e mouse
L’utilizzo è il seguente:
  Per la tastiera:

      Uint8 *keys;
      keys = SDL_GetKeyState(NULL);          // aggiorna il vettore keys
      if ( keys[SDLK_EVENTO] == SDL_PRESSED )

   Per il mouse:

      Uint8 mouse;
      mouse = SDL_GetMouseState(mx,my); // mx e my sono puntatori a int
                      // in essi saranno salvate le coordinate del puntatore del mouse
                      // possono essere NULL
      if ( mouse == SDL_EVENTO )

EVENTI TASTIERA

/* The keyboard syms have been cleverly chosen to map to ASCII */
       SDLK_UNKNOWN                    = 0,
       SDLK_FIRST               = 0,
       SDLK_BACKSPACE                  = 8,
       SDLK_TAB          = 9,
       SDLK_CLEAR               = 12,
       SDLK_RETURN              = 13,
       SDLK_PAUSE               = 19,
       SDLK_ESCAPE              = 27,
       SDLK_SPACE               = 32,
       SDLK_EXCLAIM             = 33,
       SDLK_QUOTEDBL                   = 34,
       SDLK_HASH                = 35,
       SDLK_DOLLAR              = 36,
       SDLK_AMPERSAND                  = 38,
       SDLK_QUOTE               = 39,
       SDLK_LEFTPAREN                  = 40,
       SDLK_RIGHTPAREN                 = 41,
       SDLK_ASTERISK            = 42,
       SDLK_PLUS                = 43,
       SDLK_COMMA               = 44,
       SDLK_MINUS               = 45,
       SDLK_PERIOD              = 46,
       SDLK_SLASH               = 47,
       SDLK_0                   = 48,
       SDLK_1                   = 49,
       SDLK_2                   = 50,
       SDLK_3                   = 51,
SDLK_4                   = 52,
SDLK_5                   = 53,
SDLK_6                   = 54,
SDLK_7                   = 55,
SDLK_8                   = 56,
SDLK_9                   = 57,
SDLK_COLON               = 58,
SDLK_SEMICOLON                  = 59,
SDLK_LESS                = 60,
SDLK_EQUALS              = 61,
SDLK_GREATER             = 62,
SDLK_QUESTION            = 63,
SDLK_AT                  = 64,
/*
  Skip uppercase letters
 */
SDLK_LEFTBRACKET = 91,
SDLK_BACKSLASH                  = 92,
SDLK_RIGHTBRACKET= 93,
SDLK_CARET               = 94,
SDLK_UNDERSCORE                 = 95,
SDLK_BACKQUOTE                  = 96,
SDLK_a                   = 97,
SDLK_b                   = 98,
SDLK_c                   = 99,
SDLK_d                   = 100,
SDLK_e                   = 101,
SDLK_f                   = 102,
SDLK_g                   = 103,
SDLK_h                   = 104,
SDLK_i                   = 105,
SDLK_j                   = 106,
SDLK_k                   = 107,
SDLK_l                   = 108,
SDLK_m                   = 109,
SDLK_n                   = 110,
SDLK_o                   = 111,
SDLK_p                   = 112,
SDLK_q                   = 113,
SDLK_r                   = 114,
SDLK_s                   = 115,
SDLK_t                   = 116,
SDLK_u                   = 117,
SDLK_v                   = 118,
SDLK_w                   = 119,
SDLK_x                   = 120,
SDLK_y                   = 121,
SDLK_z                  = 122,
SDLK_DELETE             = 127,
/* End of ASCII mapped keysyms */

/* International keyboard syms */
SDLK_WORLD_0               = 160,        /* 0xA0 */
SDLK_WORLD_1               = 161,
SDLK_WORLD_2               = 162,
SDLK_WORLD_3               = 163,
SDLK_WORLD_4               = 164,
SDLK_WORLD_5               = 165,
SDLK_WORLD_6               = 166,
SDLK_WORLD_7               = 167,
SDLK_WORLD_8               = 168,
SDLK_WORLD_9               = 169,
SDLK_WORLD_10                     = 170,
SDLK_WORLD_11                     = 171,
SDLK_WORLD_12                     = 172,
SDLK_WORLD_13                     = 173,
SDLK_WORLD_14                     = 174,
SDLK_WORLD_15                     = 175,
SDLK_WORLD_16                     = 176,
SDLK_WORLD_17                     = 177,
SDLK_WORLD_18                     = 178,
SDLK_WORLD_19                     = 179,
SDLK_WORLD_20                     = 180,
SDLK_WORLD_21                     = 181,
SDLK_WORLD_22                     = 182,
SDLK_WORLD_23                     = 183,
SDLK_WORLD_24                     = 184,
SDLK_WORLD_25                     = 185,
SDLK_WORLD_26                     = 186,
SDLK_WORLD_27                     = 187,
SDLK_WORLD_28                     = 188,
SDLK_WORLD_29                     = 189,
SDLK_WORLD_30                     = 190,
SDLK_WORLD_31                     = 191,
SDLK_WORLD_32                     = 192,
SDLK_WORLD_33                     = 193,
SDLK_WORLD_34                     = 194,
SDLK_WORLD_35                     = 195,
SDLK_WORLD_36                     = 196,
SDLK_WORLD_37                     = 197,
SDLK_WORLD_38                     = 198,
SDLK_WORLD_39                     = 199,
SDLK_WORLD_40                     = 200,
SDLK_WORLD_41                     = 201,
SDLK_WORLD_42   = 202,
SDLK_WORLD_43   = 203,
SDLK_WORLD_44   = 204,
SDLK_WORLD_45   = 205,
SDLK_WORLD_46   = 206,
SDLK_WORLD_47   = 207,
SDLK_WORLD_48   = 208,
SDLK_WORLD_49   = 209,
SDLK_WORLD_50   = 210,
SDLK_WORLD_51   = 211,
SDLK_WORLD_52   = 212,
SDLK_WORLD_53   = 213,
SDLK_WORLD_54   = 214,
SDLK_WORLD_55   = 215,
SDLK_WORLD_56   = 216,
SDLK_WORLD_57   = 217,
SDLK_WORLD_58   = 218,
SDLK_WORLD_59   = 219,
SDLK_WORLD_60   = 220,
SDLK_WORLD_61   = 221,
SDLK_WORLD_62   = 222,
SDLK_WORLD_63   = 223,
SDLK_WORLD_64   = 224,
SDLK_WORLD_65   = 225,
SDLK_WORLD_66   = 226,
SDLK_WORLD_67   = 227,
SDLK_WORLD_68   = 228,
SDLK_WORLD_69   = 229,
SDLK_WORLD_70   = 230,
SDLK_WORLD_71   = 231,
SDLK_WORLD_72   = 232,
SDLK_WORLD_73   = 233,
SDLK_WORLD_74   = 234,
SDLK_WORLD_75   = 235,
SDLK_WORLD_76   = 236,
SDLK_WORLD_77   = 237,
SDLK_WORLD_78   = 238,
SDLK_WORLD_79   = 239,
SDLK_WORLD_80   = 240,
SDLK_WORLD_81   = 241,
SDLK_WORLD_82   = 242,
SDLK_WORLD_83   = 243,
SDLK_WORLD_84   = 244,
SDLK_WORLD_85   = 245,
SDLK_WORLD_86   = 246,
SDLK_WORLD_87   = 247,
SDLK_WORLD_88   = 248,
SDLK_WORLD_89                      = 249,
SDLK_WORLD_90                      = 250,
SDLK_WORLD_91                      = 251,
SDLK_WORLD_92                      = 252,
SDLK_WORLD_93                      = 253,
SDLK_WORLD_94                      = 254,
SDLK_WORLD_95                      = 255,   /* 0xFF */

/* Numeric keypad */
SDLK_KP0           = 256,
SDLK_KP1           = 257,
SDLK_KP2           = 258,
SDLK_KP3           = 259,
SDLK_KP4           = 260,
SDLK_KP5           = 261,
SDLK_KP6           = 262,
SDLK_KP7           = 263,
SDLK_KP8           = 264,
SDLK_KP9           = 265,
SDLK_KP_PERIOD                   = 266,
SDLK_KP_DIVIDE                   = 267,
SDLK_KP_MULTIPLY = 268,
SDLK_KP_MINUS             = 269,
SDLK_KP_PLUS              = 270,
SDLK_KP_ENTER             = 271,
SDLK_KP_EQUALS                   = 272,

/* Arrows + Home/End pad */
SDLK_UP                  = 273,
SDLK_DOWN                = 274,
SDLK_RIGHT               = 275,
SDLK_LEFT                = 276,
SDLK_INSERT              = 277,
SDLK_HOME                = 278,
SDLK_END          = 279,
SDLK_PAGEUP              = 280,
SDLK_PAGEDOWN                   = 281,

/* Function keys */
SDLK_F1                   = 282,
SDLK_F2                   = 283,
SDLK_F3                   = 284,
SDLK_F4                   = 285,
SDLK_F5                   = 286,
SDLK_F6                   = 287,
SDLK_F7                   = 288,
SDLK_F8                   = 289,
SDLK_F9                      = 290,
    SDLK_F10            = 291,
    SDLK_F11            = 292,
    SDLK_F12            = 293,
    SDLK_F13            = 294,
    SDLK_F14            = 295,
    SDLK_F15            = 296,

    /* Key state modifier keys */
    SDLK_NUMLOCK                      = 300,
    SDLK_CAPSLOCK                     = 301,
    SDLK_SCROLLOCK                    = 302,
    SDLK_RSHIFT                = 303,
    SDLK_LSHIFT                = 304,
    SDLK_RCTRL                 = 305,
    SDLK_LCTRL                 = 306,
    SDLK_RALT                  = 307,
    SDLK_LALT                  = 308,
    SDLK_RMETA                 = 309,
    SDLK_LMETA                 = 310,
    SDLK_LSUPER                = 311,        /* Left "Windows" key */
    SDLK_RSUPER                = 312,        /* Right "Windows" key */
    SDLK_MODE                  = 313,        /* "Alt Gr" key */
    SDLK_COMPOSE               = 314,        /* Multi-key compose key */

    /* Miscellaneous function keys */
    SDLK_HELP                  = 315,
    SDLK_PRINT                 = 316,
    SDLK_SYSREQ                = 317,
    SDLK_BREAK                 = 318,
    SDLK_MENU                  = 319,
    SDLK_POWER                 = 320,      /* Power Macintosh power key */
    SDLK_EURO                  = 321,      /* Some european keyboards */
    SDLK_UNDO                  = 322,      /* Atari keyboard has Undo */

EVENTI MOUSE

#define SDL_BUTTON(X)      (SDL_PRESSED
Appendice2 – funzioni di blitting personalizzate
Le seguenti sono due funzioni fondamentali in SDL_BlitSurface_transparent; il loro
sorgente è in SDL_Example3.
La fonte è http://docs.huihoo.com/sdl/1.2/guidevideo.html - SDL Library Documentation

Uint32 getpixel(SDL_Surface *surface, int x, int y) ;

/*
 * Imposta il pixel di coordinate (x, y)
 * ATTENZIONE: la superficie deve essere bloccata prima di eseguire questa funzione
 */
void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel) ;

       Utilizzando getpixel e putpixel ho scritto SDL_BlitSurface_transparent, una funzione
che mi era necessaria per la realizzazione di un motore di rendering che mi permettesse di
disegnare immagini utilizzando la trasparenza. Per farlo è stato sufficiente disegnare
l’immagine tralasciando i pixel di colore uguale a quello di (x, y)==(0, 0).
       Oltre a questo la funzione permette di gestire altri aspetti, come il flip dell’immagine,
la rotazione, la scala.

int SDL_BlitSurface_transparent(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst,
SDL_Rect *dstrect, int ang_rotaz, int flip, int decentramento_y, int scala, int dec_scala, int
fantasma)
/*
*src -> source - sorgente
*srcrect -> parte(rettangolo) della sorgente da copiare
*dst -> destinazione
*dstrect -> dove copio nella superficie di dst
ang_rotaz -> 0 - 360 rotazione immagine *** DA IMPLEMENTARE ***
flip   -> 0 none
        1 specchio orizzontalmente
                    2 specchio verticalmente
scala -> 1 normale
        -2 metà
                    2 doppio
decimale -> come inserire 3.1: scala.decimale
        concretamente disegno ogni volta che l'accumulo dei decimali mi dà 1
                    ..
fantasma -> 0 normale
        1 fantasma (immagine "tirata su")
                    2 + immagine sempre più tirata verso l'alto
*/

{
// SDL_Surface sono già sbloccate
int i,j,decimale=0,aggiunta;
int xs, ys, xd, yd, ystmp, ydtmp;
int condizione_x, condizione_y;
Uint32 color, trpcolor;

trpcolor=getpixel(src, 0, 0);

    if ( (srcrect==NULL) && (dstrect==NULL) )
    {
             xs=0;
             ystmp=0;
             xd=0;
             ydtmp=0+decentramento_y;
             condizione_x=src->w;
             condizione_y=src->h;

              if (flip==1) // flip orizzontale
                      xd+=src->w;
    }
    else
       if (srcrect==NULL)
             {
                   xs=0;
                   ystmp=0;
                   xd=dstrect->x;
                   ydtmp=dstrect->y+decentramento_y;
                   condizione_x=src->w;
                   condizione_y=src->h;

                     if (flip==1) // flip orizzontale
                             xd+=src->w;
              }
       else
                     if (dstrect==NULL)
                     {
                             xs=srcrect->x;
                             ystmp=srcrect->y;
                             xd=0;
                             ydtmp=0+decentramento_y;
                             condizione_x=(srcrect->w + srcrect->x);
                             condizione_y=(srcrect->h + srcrect->y);

                            if (flip==1) // flip orizzontale
                                    xd+=srcrect->w;
                     }
                     else
                     {
xs=srcrect->x;
                             ystmp=srcrect->y;
                             xd=dstrect->x;
                             ydtmp=dstrect->y+decentramento_y;
                             condizione_x=(srcrect->w + srcrect->x);
                             condizione_y=(srcrect->h + srcrect->y);

                             if (flip==1) // flip orizzontale
                                     xd+=srcrect->w;
                      }

       for (; xs
else xd+=scala;
    }

    return 0;
}
Bibliografia
Using SDL with Microsoft Visual C++ 5,6 and 7 – di Sam Lantinga, modificato da
       Lion Kimbro and revisionato da James Turk;
SDL Library Documentation - http://docs.huihoo.com/sdl/1.2/guidevideo.html;
http://www.gameprog.it;
http://www.libsdl.org, sito ufficiale della libreria;
http://www.gnu.org/licenses/lgpl.html, citato relativamente alla licenza della libreria
       SDL.
Puoi anche leggere