Logo


Produzione Gio Project
Forum Shopping
Forum Shopping è il più grande centro comerciale naturale della valle del Metauro.


Valid XHTML 1.0 Transitional    CSS Valido!

Programmazione con Sdl e C++
Nota importante: visto il successo di questo tutorial nella versione precedente del sito, riporto per intero tutto il pezzo.
Purtroppo, essendo stato scritto all'età di 16 anni, il codice manca di eleganza e dei requisiti fondamentali della programmazione. Ho scelto di lasciare intatto il testo per non mutarne l'atmosfera quasi magica che permanea leggendo.

Purtroppo, essendo anche codice vecchio, non posso garantire il perfetto funzionamento su tutte le macchine: se avete comunque dei problemi non esitate a scrivermi all'indirizzo di posta elettronica dei contatti. Se possibile cercheremo di risolvere i problemi insieme.

Buona Lettura

Le SDL (Simple DirectMedia Layer)


Descrizione: Motore grafico 2D sviluppato grazie all'utilizzo delle librerie SDL;

Requisiti essenziali:

  • Dev-C++: Compilatore gratuito in grado di competere con il costoso Visual C++; scaricabile da www.borland.com
  • librerie SDL:


Creare il progetto


Innanzitutto installiamo le SDL nella cartella del Dev-C++: per far ciò, copiamo i contenuti della cartella "include" delle SDL, nella cartella "include" del Dev-C++; facciamo lo stesso con i contenuti della cartella lib.

Fatto ciò, apriamo Dev-C++ e creiamo un nuovo progetto selezionando File/Nuovo Progetto e scegliendo "Empty project". A questo punto clicchiamo su Progetto/Opzioni Progetto. Nel menù a tendina Parametri/Linker scriviamo "-lmingw32 -lSDLmain -lSDL": queste istruzioni dicono al Linker dove trovare le SDL.

Per inserire un file al progetto eseguire Progetto/Aggiungi al progetto e scegliere il file.

Spiegazioni Progetto


gio2d.h


void Inizializza_engine(int risoluzione_x, int risoluzione_y, int colori, int modalita);
void posiziona (SDL_Surface *immagine, int pos_x, int pos_y);
void posiziona(SDL_Surface *immagine, int pos_x, int pos_y, int frames[5], int n_fotogrammi);
void trasparenza(SDL_Surface *immagine, int rosso, int verde, int blu);
void aggiorna();
void cancella();
void finalizza();
void libera_memoria(SDL_Surface *immagine);

Questo file è un header e contiene solo i prototipi di funzione utilizzati nel file gio2d.cpp. Avremmo potuto anche evitare la scrittura di questo file, visto il numero delle funzioni, ma è buona norma scrivere l'header per semplificare la lettura del file. Notate come le parole racchiuse tra /* e */ non siano considerate dal compilatore. Queste parole sono commenti e cioè frasi necessarie per comprendere che cosa fa il codice: infatti se riprendeste il progetto dopo molto tempo potreste dimenticarvi il compito del codice scritto; i commenti servono a evitare ciò.

Osservate come le funzioni vengano precedute dalla parola void: tutte le funzioni infatti dopo aver svolto il loro compito restituiscono un valore: in questo caso però la parola void specifica che non restituiscono niente.

Segue poi il nome vero e proprio della funzione: ricordate sempre di dare un nome che centri qualcosa con il compito della funzione; in questo modo sarà più facile utilizzarle.

Poi, tutte le funzioni devono contenere delle parentesi tonde ( ): all'interno di esse possono comparire dei parametri che possono essere necessari alla funzione per svolgere il proprio compito.

gio2d.cpp


#include <iostream>
#include <stdlib.h>
#include <SDL\SDL.h>
#include "gio2d.h"

Queste righe contengono i commenti e gli header file necessari alla corretta compilazione del progetto. Notate come i nomi inseriti tra i simboli < > si riferiscano alla cartella "include", del Dev-C++, mentre quelli tra virgolette " " si riferiscano a file nella cartella del progetto.



SDL_Surface *screen;

Questa riga informa il compilatore che verrà utilizzata una variabile globale di tipo SDL_Surface: screen è infatti la variabile che verrà utilizzata come superficie dove inizializzare il motore grafico; corrisponde praticamente allo schermo del monitor.



/////////////////*** Inizializza il motore grafico ***//////////////////////////
void Inizializza_engine(int risoluzione_x, int risoluzione_y, int colori, int modalita){
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)<0){
printf("Errore nell'inizializzazione dell'engine: %s\n", SDL_GetError());
exit(1);
}
if(modalita==0)
screen=SDL_SetVideoMode(risoluzione_x, risoluzione_y, colori, SDL_HWSURFACE | SDL_DOUBLEBUF);
if(modalita==1)
screen=SDL_SetVideoMode(risoluzione_x, risoluzione_y, colori, SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_FULLSCREEN);
}
///////////////////////*** fine inizializzazione ***////////////////////////////

Eccoci alla prima funzione. I parametri sono quattro e cioè:

*

risoluzione_x ovvero una variabile intera che deve contenere i punti di dimensione orizzontale;
*

risoluzione_y ovvero una variabile intera che deve contenere i punti di dimensione verticale;
*

colori ovvero una variabile intera che deve contenere il numero di bit che possono essere 8, 16, 24, 32 oppure 0 se vogliamo che vengano mantenuti i bit usati attualmente;
*

modalita ovvero una variabile intera che stabilisce se avviare il programma come finestra (0) o a schermo intero (1).

Innanzitutto avviamo un ciclo condizionale che inizializza i sottosistemi audio/video. Se non ci riesce visualizza un messaggio di errore che ci avverte della situazione e chiude il programma;

Avviamo poi un altro ciclo if per verificare la modalità:

*

se modalita è uguale a 0 attribuiamo alla variabile screen lo schermo come finestra;
*

se modalita è uguale a 1 attribuiamo alla variabile screen lo schermo intero;

Notate come vengano utilizzati i parametri per stabilire la risoluzione e la profondità. SDL_HWSURFACE ecc sono dei flag che stabiliscono gli attributi del motore grafico:

*

SDL_HWSURFACE stabilisce che le superfici vengano caricate nella memoria della scheda video;
*

SDL_DOUBLEBUF stabilisce che venga utilizzato il buffer doppio in modo che le immagini siano fluide;
*

SDL_FULLSCREEN stabilisce che venga avviato il tutto a schermo intero.

La funzione SDL_SetVideo avvia il motore grafico secondo i parametri stabiliti.



/////////////////*** posiziona l'immagine sulllo schermo ***//////////////////////
void posiziona(SDL_Surface *immagine, int pos_x, int pos_y)
{
SDL_Rect destinazione;
destinazione.x=pos_x;
destinazione.y=pos_y;
SDL_BlitSurface(immagine, NULL, screen, &destinazione);}
////////////////////////*** fine posizionamento ***/////////////////////////////

Questa funzione è necessaria per posizionare sullo schermo altre immagini come per esempio alberi, mostri ecc.

I parametri accettati sono:

*

immagine ovvero una superficie SDL che contiene l'immagine da caricare;
*

pos_x ovvero un intero che stabilisce la posizione orizzontale rispetto allo schermo;
*

pos_y ovvero un intero che stabilisce la posizione verticale rispetto allo schermo.

Dichiariamo poi una variabile locale ossia una variabile che può essere utilizzata solo all'interno della funzione in cui è dichiarata. Alle proprietà della variabile destinazione attribuiremo i valori dei parametri passati alla funzione. Questo perché la funzione SDL_BlitSurface necessita di questo di tipo di variabile come parametro. Avremmo potuto inserire come parametro della nostra funzione un SDL_Rect al posto di pos_x e pos_y ma sarebbe stato molto più scomodo nello sviluppo più avanzato del progetto.

La funzione SDL_BlitSurface svolge il compito principale: posiziona la nostra immagine sullo schermo. Ecco i parametri che accetta:

*

immagine ovvero l'immagine che dobbiamo visualizzare;
*

NULL: al posto di NULL avremmo potuto utilizzare un SDL_Rect per far visualizzare solo una parte dell'immagine ma in questo caso noi vogliamo visualizzarla tutta per cui inseriamo NULL;
*

screen è la superficie su cui visualizzare l'immagine: in questo caso il nostro schermo;
*

destinazione come avrete notato non è altro che la variabile precedentemente dichiarata: stabilisce le coordinate dove visualizzare l'immagine; notate la & necessaria per richiamare questa variabile come argomento per questa funzione. Un argomento non è altro che il nome che si utilizza al posto del termine parametro quando si chiama una funzione.



///////////////*** posiziona un'immagine con più fotogrammi ***/////////////////
void posiziona(SDL_Surface *immagine, int pos_x, int pos_y, int frames[5], int n_fotogramma){
if(n_fotogrammi>frames[4])n_fotogrammi=0;
SDL_Rect destinazione;
destinazione.x=pos_x;
destinazione.y=pos_y;
SDL_Rect fotogramma;
fotogramma.x= frames[0] + frames[2]*n_fotogrammi;
fotogramma.y= frames[1];
fotogramma.w= frames[2];
fotogramma.h= frames[3];
SDL_BlitSurface(immagine, &fotogramma, screen, &destinazione);}
/////////////////*** fine posizionamento immagine animata ***///////////////////

Questa funzione è necessaria per visualizza un'immagine che abbia più di un fotogramma e che quindi deve sembrare un'animazione. Notate che il nome è uguale alla funzione precedente: l'unica differenza sono i parametri. Oltre a quelli spiegati prima compaiono anche:

*

frames[5] è un array intero che deve contenere le seguenti informazioni:
o

frames[0]: la x dell'immagine che ci dice da dove comincia l'animazione;
o

frames[1]: la y dell'immagine che ci dice da dove comincia l'animazione;
o

frames[2]: la larghezza di ciascun fotogramma;
o

frames[3]: l'altezza di ciascun fotogramma;
o

frames[4]: il numero dei fotogrammi che compongono l'animazione.
*

n_fotogrammi è un intero che deve contenere il numero del fotogramma da visualizzare.

Avviamo innanzitutto un ciclo if per verificare se n_fotogrammi ha superato il numero di fotogrammi totali: in caso positivo si ricomincia da 0.

Dichiariamo poi una variabile SDL_Rect che conterrà, come spiegato nella funzione precedente, le coordinate in cui visualizzare l'immagine.

Dichiariamo poi un'altra variabile SDL_Rect che stabilirà quale rettangolo dell'intera immagine visualizzare e quindi quale fotogramma. Per far ciò, impostiamo la x del rettangolo, in modo che sia uguale al punto iniziale dell'immagine, più i punti necessari per arrivare al fotogramma desiderato. Poi impostiamo la sua y uguale alla y di partenza dell'intera animazione: infatti la y non cambia per tutta la durata dell'animazione. Impostiamo poi la larghezza del rettangolo uguale alla larghezza stabilita mediante l'array. Facciamo lo stesso con l'altezza.

Ora utilizziamo la funzione SDL_BlitSurface per visualizzare l'immagine:

*

immagine è l'immagine che vogliamo visualizzare;
*

fotogramma è il rettangolo dell'intera animazione che vogliamo sia visualizzato;
*

screen è la superficie su cui visualizzare l'immagine;
*

destinazione contiene le coordinate in cui vogliamo venga rappresentata l'immagine.

ATTENZIONE: l'intera funzione non fa sì che venga visualizzata l'animazione: visualizza solo il fotogramma scelto mediante l'argomento n_fotogramma. Per visualizzare l'animazione in ogni suo fotogramma, dobbiamo far sì che, quando chiamiamo la nostra funzione nel file principale, il valore di n_fotogramma cambi.



////////////*** impostazione colore trasparente come sfondo ***/////////////////

void trasparenza(SDL_Surface *immagine, int rosso, int verde, int blu){
Uint32 colore_trasparente = SDL_MapRGB(immagine->format, rosso, verde, blu);
// imposta l'accelerazione RLE e imposta il colore trasparente
SDL_SetColorKey(immagine, SDL_SRCCOLORKEY|SDL_RLEACCEL, colore_trasparente);
}
/////////////////*** fine impostazione colore trasparente ***///////////////////

Questa funzione imposta un colore trasparente per un'immagine: questa tecnica è fondamentale per visualizzare per esempio dei personaggi senza lo sfondo dell'immagine.

I parametri accettati sono 4 e nel dettaglio:

*

immagine: una superficie contenente il file da visualizzare;
*

rosso: un intero che contiene la percentuale il gradiente rosso contenuta nel colore voluto;
*

verde: un intero che contiene la percentuale il gradiente verde contenuta nel colore voluto;
*

blu: un intero che contiene la percentuale di gradiente blu contenuta nel colore voluto.

A questo punto dichiariamo un intero a 32 bit senza segno che conterrà la mappatura necessaria per impostare il colore voluto come trasparente. Grazie alla funzione SDL_MapRGB infatti otterremo la mappatura dell'imagine in base alla risoluzione dell'immagine.

Grazie alla funzione SDL_SetColorKey imposteremo il colore voluto come trasparente. I parametri accettati sono:

*

immagine e cioè l'immagine a cui vogliamo sia applicato il colore trasparente;
*

SDL_SRCCOLORKEY e cioè un flag che imposta il colore voluto come trasparente;
*

SDL_RLEACCEL e cioè un flag che attiva l'accelerazione se disponibile;
*

colore_trasparente e cioè un intero senza segno a 32 bit che contiene il colore voluto come trasparente.



//////////////////////////*** aggiorna lo schermo ***///////////////////////////
void aggiorna(){SDL_Flip(screen);}
/////////////////////////*** fine aggiornamento ***/////////////////////////////

Questa funzione non è altro che una funzione SDL rinominata in modo che sia più facile da utilizzare e da ricordare.

Il compito di questa semplice riga di codice è molto importante perché senza di essa non sarebbe possibile vedere alcunché. SDL_Flip non fa altro che disegnare sullo schermo l'intera superficie screen che conterrà anche tutte la altre superfici che dichiareremo.



///////////////////////*** cancella lo schermo ***//////////////////////////////

/* è necessario chiamare questa funzione prima dell'aggiornamento
per evitare di ridisegnarci sopra */

void cancella(){SDL_FillRect(screen, 0, 0);}
//////////////////////*** fine cancellazione ***////////////////////////////////

Come prima questa funzione non è altro che un funzione SDL sotto altro nome.

è necessario chiamare questa funzione per far sì che, quando chiamiamo la funzione aggiorna, non si ridisegni sopra alla precedente immagine con conseguenti errori di visualizzazione.



/////////////////////*** chiude i sottosistemi ***//////////////////////////////
void finalizza(){SDL_Quit();}
//////////////////////////////*** fine chiusura ***/////////////////////////////////

Anche questa funzione cambia solo il nome di una funzione SDL.

Dobbiamo chiamare questa funzione prima che il nostro programma termini perché così chiuderemo correttamente tutti i sottosistemi inizializzati.



////////////////////*** libera la memoria dell'immagine ***/////////////////////
void libera_memoria(SDL_Surface *immagine){SDL_FreeSurface(immagine);}
//////////////////////*** fine liberazione memoria ***//////////////////////////

Ancora una volta rinominiamo una funzione SDL.

Dobbiamo chiamare questa funzione ogni qualvolta decidiamo di cambiare l'immagine contenuta in una SDL_Surface. Prima di caricarne un'altra, infatti, bisogna chiamare questa funzione così da non appesantire la memoria.



main.cpp


Questo file sarà il cuore vero e proprio dei programmi che vorremo sviluppare con il gio2d engine. Qui chiamaremo le funzioni grafiche e ne creeremo di nuove per dar vita ad un gioco. Ma, lo scopo di questo tutorial, è di insegnare a utilizzare questo motore grafico, per cui, per adesso ci limiteremo a scrivere il codice per aprire una finestra con un'immagine di sfondo.

#include <iostream>
#include <stdlib.h>
#include <SDL\SDL.h>
#include <windows.h>
#include "gio2d.h"

Come al solito le prime righe sono solo dei commenti. Includiamo poi le librerie necessarie alla corretta compilazione del nostro file. Vedete come abbiamo incluso il file scritto precedentemente, gio2d.h; includendo questo file, automaticamente potremmo disporre del codice inserito in gio2d.cpp.



using namespace std;

Questa riga di codice evita di dover ripetere la parola chiave std prima di molte istruzioni.



// variabili globali

//impostazioni del gioco
int risoluzione_x=640;
int risoluzione_y=480;
int bit_colori=32;
int modalita=0; // 0= modalità finestra // 1= modalità schermo intero

Queste righe stabiliscono le impostazioni del nostro motore grafico: se ricordate queste sono le variabili necessarie per inizializzare il sottosistema grafico.



SDL_Surface *sfondo;

Questa variabile di tipo SDL_Surface conterrà la nostra immagine di sfondo. Notate l'* che è indispensabile per dichiarare questo tipo di variabile. L'* sta a indicare l'utilizzo di un puntatore.



////////////////////////////////main///////////////////////////////////


int main(int argc, char *argv[]){

Inizializza_engine(risoluzione_x, risoluzione_y, bit_colori, modalita);

sfondo=SDL_LoadBMP("sfondo.bmp");
while (GetAsyncKeyState(VK_ESCAPE)==0){
cancella();

posiziona(sfondo, 0, 0);
aggiorna();
}
finalizza();
return 0;
}

Ecco la funzione main, indispensabile in qualsiasi programma. Notate gli argomenti passati a main, indispensabili per l'utilizzo delle SDL.

Innanzitutto avviamo i sottosistemi con la funzione Inizializza_engine, passando come argomenti le variabili che abbiamo dichiarato prima. Adesso è il momento di caricare, sulla superficie dichiarata prima, la nostra immagine.

Poi avviamo un ciclo while per far sì che il nostro programma resti in esecuzione fino a quando non venga premuto il tasto esc.

A questo punto chiamiamo la funzione cancella(), che farà in modo che prima che disegnata l'immagine lo schermo sia vuoto, evitando così problemi di visualizzazione.

Disegniamo ora l'immagine di sfondo alle coordinate 0,0 (cioè all'angolo in alto a sinistra dello schermo), mediante l'utilizzo della funzione posiziona.

Ora è il momento di aggiornare lo schermo mediante l'utilizzo della funzione aggiorna(). Se non utilizzassimo questa funzione si aprirebbe una schermata completamente nera.

A questo punto usciamo dal ciclo while.

ATTENZIONE: durante l'esecuzione del programma usciremo dal while solo quando sarà premuto il tasto esc

Adesso dobbiamo chiudere correttamente tutti i sottosistemi avviati mediante la chiamata della funzione finalizza().

La funzione main per essere conclusa correttamente vuole un valore di ritorno che deve essere di tipo int. Per questo scriviamo return 0. Lo 0 indica, convenzionalmente, che la funzione è stata eseguita correttamente.

Ora abbiamo finalmente il nostro programma che mostra la nostra bella immagine di sfondo!!!



Gio Project
Pubblicata da Gio Project Lunedì, 25 Agosto 2008


Commenti


Esegui il login per aggiungere un commento