E’ tempo di mostrare qualcosa sullo schermo, vero? Sono d’accordo con voi, iniziamo a parlare delle Surface, i primi oggetti che ci accompagneranno nella rappresentazione su schermo dei nostri file grafici. Volendo tradurre alla lettera dalla Wiki ufficiale, possiamo ottenere la spiegazione più esatta per questa classe:

“Le Surface in SDL.NET rappresentano, genericamente, delle informazioni grafiche. E’ possibile crearle da zero (e quindi disegnarci ciò che si vuole) oppure caricando dei files dal disco fisso o da un array di bytes. Ogni Surface ha una propria larghezza ed altezza e possiedono delle funzionalità di disegno sulla superficie stessa.”

Le cose non sono ancora chiare? Tranquilli, ecco un bel programma di prova, che verrà prontamente spiegato.

[code]]czoxMDg3OlwiDQp1c2luZyBTeXN0ZW07DQoNCnVzaW5nIFNkbERvdE5ldC5Db3JlOw0KdXNpbmcgU2RsRG90TmV0LkdyYXBoaWNzOw17WyYqJl19Cg0KbmFtZXNwYWNlIERlbW8NCnsNCiAgICBwdWJsaWMgY2xhc3MgR2FtZQ0KICAgIHsNCiAgICAgICAgU3VyZmFjZSBzY2hlcm1hdHtbJiomXX1hID0gVmlkZW8uU2V0VmlkZW9Nb2RlKDgwMCwgNjAwKTsNCiAgICAgICAgU3VyZmFjZSBpbW1hZ2luZSA9IG5ldyBTdXJmYWNlKFwiaXtbJiomXX1tbWFnaW5lLmpwZ1wiKTsNCg0KICAgICAgICBbU1RBVGhyZWFkXQ0KICAgICAgICBwdWJsaWMgc3RhdGljIHZvaWQgTWFpbigpDQogIHtbJiomXX0gICAgICB7DQogICAgICAgICAgICBHYW1lIGFwcCA9IG5ldyBHYW1lKCk7DQogICAgICAgICAgICBhcHAuR28oKTsNCiAgICAgICAge1smKiZdfX0NCg0KICAgICAgICBwdWJsaWMgR2FtZSgpDQogICAgICAgIHsNCiAgICAgICAgICAgIEV2ZW50cy5GcHMgPSA2MDsNCiAgICAgICB7WyYqJl19ICAgICBWaWRlby5XaW5kb3dDYXB0aW9uID0gXCJHZXN0aW9uZSBTdXJmYWNlIC0gU0RMLk5FVFwiOw0KICAgICAgICB9DQoNCiAgICAge1smKiZdfSAgIHB1YmxpYyB2b2lkIEdvKCkNCiAgICAgICAgew0KICAgICAgICAgICAgRXZlbnRzLlF1aXQgKz0gbmV3IEV2ZW50SGFuZGxlcjx7WyYqJl19UXVpdEV2ZW50QXJncz4odGhpcy5RdWl0KTsNCiAgICAgICAgICAgIEV2ZW50cy5UaWNrICs9IG5ldyBFdmVudEhhbmRsZXI8VGlja3tbJiomXX1FdmVudEFyZ3M+KEV2ZW50c19UaWNrKTsNCiAgICAgICAgICAgIEV2ZW50cy5SdW4oKTsNCiAgICAgICAgfQ0KDQogICAgICAgIHZve1smKiZdfWlkIEV2ZW50c19UaWNrKG9iamVjdCBzZW5kZXIsIFRpY2tFdmVudEFyZ3MgZSkNCiAgICAgICAgew0KICAgICAgICAgICAgc2NoZXJ7WyYqJl19bWF0YS5GaWxsKFN5c3RlbS5EcmF3aW5nLkNvbG9yLkJsYWNrKTsNCg0KICAgICAgICAgICAgc2NoZXJtYXRhLkJsaXQoaW1tYWdpbntbJiomXX1lKTsNCg0KICAgICAgICAgICAgc2NoZXJtYXRhLlVwZGF0ZSgpOw0KICAgICAgICB9DQoNCiAgICAgICAgcHJpdmF0ZSB2b2lkIFF1e1smKiZdfWl0KG9iamVjdCBzZW5kZXIsIFF1aXRFdmVudEFyZ3MgZSkNCiAgICAgICAgew0KICAgICAgICAgICAgRXZlbnRzLlF1aXRBcHBsaWN7WyYqJl19YXRpb24oKTsNCiAgICAgICAgfQ0KICAgIH0NCn0NClwiO3tbJiomXX0=[[/code]

Partiamo dalle prime righe:

using System;

using SdlDotNet.Core;

using SdlDotNet.Graphics;

Qua dovremmo esserci: includiamo i namespace che ci servono per i nostri scopi, attraverso la direttiva using.

Surface schermata = Video.SetVideoMode(800, 600);

Ecco la prima delle nostre Surface. Come detto precedentemente, queste possono essere create in vari modi: l'oggetto “schermata” che stiamo dichiarando è speciale: rappresenta infatti la finestra di gioco che noi utilizzeremo.

Il tutto viene reso possibile grazie al metodo SetVideoMode della classe Video, che restituisce appunto una Surface. In questo modo abbiamo deciso che la nostra finestra avrà una dimensione di 800 pixel per 600 pixel.

Ora abbiamo:

Surface immagine = new Surface("immagine.jpg");

Questa surface invece viene creata passando come argomento del metodo costruttore il nome del file da caricare. In questo caso caricheremo il file “immagine.jpg”, situato nella stessa cartella del nostro file exe compilato. Una volta che il nostro oggetto viene creato è pronto per essere mostrato. Continuiamo però con il codice:

public Game()

{

Events.Fps = 60;

Video.WindowCaption = "Gestione Surface - SDL.NET";

}

Qui abbiamo il metodo costruttore della nostra classe Game, che svolge essenzialmente due operazioni: imposta il limite dei frame per secondo a 60 utilizzando la proprietà Fps della classe Events e cambia il titolo alla finestra del programma, stavolta utilizzando WindowCaption di Video. Molto semplice come concetto. Adesso:

public void Go()

{

Events.Quit += new EventHandler<QuitEventArgs>(this.Quit);

Events.Tick += new EventHandler<TickEventArgs>(Events_Tick);

Events.Run();

}

Il metodo Go aggiunge gli Handler per gli eventi che vogliamo gestire: in questo caso ne abbiamo due: Quit, già visto in precedenza, e Tick. Tick permette di gestire ciò che succede ad ogni frame, risultando particolarmente utile nell'aggiornamento dei valori delle variabili in gioco. Stando al limite dato precedentemente, la nostra funzione Events_Tick viene eseguita 60 volte al secondo. Quello che facciamo nel metodo Events_Tick è spiegato nelle righe successive:

void Events_Tick(object sender, TickEventArgs e)

{

schermata.Fill(System.Drawing.Color.Black);

schermata.Blit(immagine);

schermata.Update();

}


Ed ecco l'utilizzo effettivo delle nostre Surface! Come si può vedere, in questo metodo eseguiamo tre operazioni:

  1. Tramite la funzione Fill riempiamo tutta la schermata di nero. Può apparire insensato, ma pensate ad un file immagine con delle trasparenze (per esempio un file PNG) che si sposta nel tempo. Senza riempire lo schermo di nero tutte le volte avremmo un effetto fastidioso dovuto alla “non pulizia” della surface, se così la vogliamo chiamare.
  2. Tramite il metodo Blit invece disegnamo la nostra immagine. Blit è un metodo che ha ben 8 overload: ciò vuol dire che ci sono ben 9 modalità diverse con cui disegnare sulle surface. Nel nostro caso abbiamo passato solo il nome della Surface da disegnare: questo vuol dire che le coordinate in cui verrà disegnata la nostra Surface saranno (0,0) rispetto alla “schermata”.
  3. Il metodo Update si occupa infine di “aggiornare” il nostro frame e fare in modo che tutte le eventuali modifiche del sistema siano mostrate a schermo. Non dimenticatelo alla fine della fase di disegno, è importante!

Ma torniamo al punto 2: perchè queste coordinate sono (0,0)? E' semplice da spiegare: la Surface “schermata” rappresenta la finestra stessa del programma. Di conseguenza, il punto situato più in alto a sinistra avrà delle coordinate pari a 0 sull'asse x ed altrettanto sulla y.

Per disegnare la nostra immagine in una posizione differente, la sintassi sarà la seguente:

schermata.Blit(immagine, new System.Drawing.Point(20, 20));

Stavolta disegneremo la nostra immagine alle coordinate (20,20), e non più (0,0), sui nostri assi cartesiani. A tal proposito devo ricordarvi una cosa molto importante: gli assi cartesiani sullo schermo del pc sono ribaltati sull'asse Y. In poche parole, ecco due immagini per chiarire le idee.

cartesiani-1

cartesiani-2

Ciò che abbiamo dopo il metodo Events_Tick è il codice del metodo Quit, che ci consente di uscire dal programma.

Tornando a parlare genericamente, l'oggetto Surface supporta una buona quantità di formati grafici:

  • BMP
  • PNM
  • XPM
  • LBM
  • PCX
  • GIF
  • JPEG
  • PNG
  • TGA

Trasparenza
Ovviamente non è solo questo quello che dobbiamo dire riguardo alle Surface e al loro utilizzo. Un'altra feature molto utile è quella della gestione della trasparenza, che avviene tramite due proprietà:

  • TransparentColor
  • Transparent

Facciamo un esempio. Abbiamo un'immagine in un file PNG che vogliamo utilizzare come cursore del mouse nel nostro gioco. Caricandola e mostrandola sullo schermo tramite la procedura sopra mostrata, tuttavia, non avremmo il risultato desiderato: oltre a mostrare il cursore, infatti, verrebbe mostrato anche il quadrato in cui è contenuta l'immagine, ovvero tutto il file immagine, per intero. Come facciamo ad evitare questo problema?

Per prima cosa, dobbiamo specificare il colore di trasparenza del nostro file. Ecco un veloce esempio, usando sempre la nostra surface “immagine”:

immagine.TransparentColor = System.Drawing.Color.White;

In questo modo diciamo al sistema che, quando mostrerà a schermo la surface “immagine” dovrà ignorare tutti i pixel di colore bianco. Dopo questo, usiamo anche la proprietà Transparent, semplicemente impostandola su True. Ricordate che questo step è necessario.

immagine.Transparent = true;

Adesso il nostro cursore sarà bello e pronto da mostrare senza problemi ;) . Ovviamente queste istruzioni è bene non metterle nel loop senza condizioni: io per esempio le metto nel metodo costruttore della mia classe. Ecco un esempio completo, per mostrare dove ho posizionato le istruzioni.

public Game()

{

immagine.TransparentColor = System.Drawing.Color.White;

immagine.Transparent = true;

Events.Fps = 60;

Video.WindowCaption = "Gestione Surface - SDL.NET";

}

Alpha Blending

Anche l'Alpha Blending è decisamente semplice da utilizzare. Come per la trasparenza, dovremo scrivere solamente due righe di codice, utilizzando due proprietà:

  • Alpha
  • AlphaBlending

Alpha è di tipo byte e va da 0 a 255. Impostandolo su 0 avremo la totale trasparenza della surface. A 255 invece la surface sarà totalmente visibile. Dopo aver impostato l'Alpha procediamo quindi ad attivare l'AlphaBlending impostando la proprietà su true, proprio come per la trasparenza. Se non effettuiamo quest'ultimo passo la proprietà Alpha verrà ignorata.

Ecco un esempio:

immagine.Alpha = Convert.ToByte(255);

immagine.Alpha = Convert.ToByte(0);

immagine.AlphaBlending = true;

Come suggerisce anche la Wiki ufficiale, un uso saggio delle surface può essere anche l'uso delle SurfaceCollections, ovvero strutture dinamiche che permettono di gestire altrettanto dinamicamente questo tipo di oggetto. Tuttavia ne parleremo più in la, magari quando riuscirò a scrivere una sorta di sezione “avanzata” di questa guida. Per ora, per quanto riguarda le surface, è tutto!

  • Share/Bookmark