XNA - Frattali

Tutto ciò è colpa di un mio professore dell'università...
E' lui che ha fatto accadere tutto ciò ?_?
Non ve la prendete con me ?_?

Seguendo il corso di Costruzione di Interfacce il nostro professore ci ha fatto vedere un'applicazione che fatto lui: si tratta di un programma che permette di calcolare e visualizzare il frattale di Mandelbrot.
Il frattale di MandelBrot è uno dei più famosi frattali (lo so dire ora, ma prima che ce lo facesse vedere lui manco sapevo chi fosse XD), e devo dire che con i frattali ci si può "divertire". Tutto ciò mi ha fatto tornare alla mente quando, da piccolo, mio padre mi fece vedere un programma per calcolare queste strane immagini, dove io potevo selezionare una parte piccola piccola, e l'immagine non "perdeva di dettaglio"... Ah beata innocenza XD

Sta di fatto che dopo aver visto questo programma fatto dal nostro professore mi sono deciso a farne uno mio. Dato che sto lavorando con XNA ho deciso di farne uno che utilizzasse XNA per calcolare e visualizzare i frattali.
Ci sto ancora lavorando, e devo implementare alcuni particolari, ma direi che il grosso è fatto.
Quindi se vi chiedete che fine ho fatto, o sono dentro un frattale(XD) o sto studiando per un esame (che voglia eh XD).

Intanto vi lascio con qualche immagine ottenuta da questo generatore di frattali che ho fatto ^_^
Spero vi piacciano :)
Quando sarà finito posterò qualcosa del codice che ho fatto!











A presto :)

Continua a leggere!

The Xna-Way: Tutorial 8: Screen Shots del nostro gioco!

Aggiornamento al 16/04/2011:
con l'aggiornamento alla versione 4 di Xna questo codice non è più funzionante, dato che alcune delle classi ed oggetti che vengono utilizzate sono state eliminate o è stato rivisto il loro funzionamento.


Quale è una cosa di cui oggi come oggi non si può fare a meno in un videogame?
Bè una delle tante è la possiibilità di salvare la schermata di gioco, di farne una foto e di conservarla, in modo da mostrare ai nostri cari amici (nerd anche loro come noi) le nostre peripezie XD
Questa cosa oltre ad essere una chicca che oramai cerchiamo in quasi ogni gioco mi tornerà utile anche per catturare schermate di gioco per i piccoli tutorial che farò.

Mi sono quindi messo di buzzo buono, e studiando degli esempi che ho trovato su Xna-club, esempi che tra l'altro trattavano effetti di post processing come bloom, e ho tirato fuori il mio piccolo componente.

L'idea su cui mi sono basato è questa: XNA ci fornisce un ciclo di esecuzione nel quale vengono invocati in continuazione i metodi Update e Draw dei components che fanno parte del nostro gioco.
Ora uno screen shot va salvato quando la scena è stata completamtente renderizzata. Quindi, ragionevolmente, dovremmo salvarla dopo che sono stati eseguiti tutti i metodi Draw dei nostri componenti.
Ma come possiamo essere sicuri o controllare questa cosa?

La cosa più semplice ed immediata da fare è capire che la scena che noi vogliamo salvare, è "completa" quando siamo nel metodo update. Cioè saremo "indietro" di un frame quando andremo a salvare l'immagine, gli oggetti non saranno stati spostati, ma nel contempo il rendering della scena non sarà ancora incominciato, e quindi avremo (da qualche parte in memoria) il render completo della scena al frame precedente!
Quello che dobbiamo fare è semplicemtente recuperarlo e salvarlo.

Ecco la classe, il component, che fa questo:
Show/Hide


Al costruttore gli viene passato il percorso della directory in cui salvare le immagini, oppure solo il riferimento alla Game, ed in questo secondo caso la directory di default per il salvataggio delle immagini è la cartella immagini dell'utente.
Come si vede si recupera anche il riferimento al servizio per l'input.

In Initialize creo un ResolveTexture2D delle stesse dimensioni e formato del backBuffer del gioco.

Nel metodo update controlliamo se il tasto Stamp (o printScree, dipende da come lo si vuole chiamare) è stato premuto.
In caso positivo tramite ResolveBackBuffer recupero il backBuffer del frame precedente (e quindi il render completo della scena precente) e lo metto in resolveTarget.
Dato che resolve target è un tipo particolare di Texture2D posso forzare con un cast il suo tipo ad essere Texture2D appunto, ed usare quindi il metodo save per salvare su disco l'immagine.

Tutto qua. Così possiamo salvare semplicemente gli screen del nostro gioco.

Nel costruttore di Game1 si deve aggiungere le seguenti righe:

screenShot = new ScreenShots(this);
Components.Add(screenShot);

ed siamo veramente alla fine!

Non allego questa volta il file con il codice, dato che la classe è piccola e completamente inserita nel post.

Spero vi sia utile.
Alla prossima!
Continua a leggere!

The Xna-Way: Tutorial 7: Collisioni con BoundingSphere

Dopo molto, troppo, tempo eccomi di nuovo qua a parlare di qualcosa inerente ad XNA.
Questa volta voglio un po' parla delle collisioni, cosa che fa impazzire ogni programmatore di Video Giochi.

Anche io sono impazzito (più di quanto non fossi prima) quando mi sono addentrato in questo campo, e devo dire che ne sono rimasto un po' spaventato.

Come si possono calcolare oggi come oggi le collisioni tra 2 oggetti?
Bè un esempio di ciò l'ho usato per calcolare quali oggetti erano intersecati dal bounding Frustum della telecamera e quindi filtrare quelli da renderizzare da quelli che non lo erano.
Questo è un esempio di collisioni fatto con i VOLUMI: ho usato un sfera (per la precisione una BoundingSphere) per approssimare il volume degli oggetti presenti nella scena (ci dovrà essere una sfera per ogni oggetto, o nel caso di oggetti replicati una sola sfera che verrà riposizionata e aggiornata per ogni oggetto di cui voglio calcolare la collisione).
Come qualcuno potrà pensare, calcolare le collisione di 2 oggetti approssimando il loro volume con delle sfere può portare a delle approssimazioni tali da avere un grosso grado di imprecisione del calcolare l'effettiva collisione tra due oggetti.
Se il nostro oggetto è una sfera allora la sua BS (abbreviazione di BoundigSphere) coinciderà esattamente con l'oggetto, ma già nel caso di un cubo esso verrà contenuto per intero all'interno della sfera, così con oggetti più complessi!
Ecco un esempio di quando ho detto:

Come possiamo vedere la navicella e il meteorite sono completamente contenuti all'interno delle loro sfere, anche se buona parte delle sfere non sono occupate dal volume del modello.

Come si fa per calcolare le collissioni? La collisione viene calcolata controllando se le due sfere si toccano o si intersecano l'una con l'altra. Ho una collisione anche se una sfera è contenuta interamente in un'altra.

XNA ci offre la classe BoundingSphere la quale è caratterizzata da una posizione e da un raggio. Le BS sono comode perchè anche se muoviamo o ruotiamo l'oggetto esso sarà sempre contenuto all'interno della stessa sfera, cosa che non accade con i BoundingBox (altro tipo di volumi per approssimazione), i quali sono dei parallelepipedi. Nel caso di una rotazione dobbiamo ricalcolare il rettangolo che contiene il nostro modello.
L'unico caso in cui dobbiamo modificare la sfera è quando scaliamo il nostro oggetto, dato che dovremo anche aumentare/diminuire il raggio della sfera.

In tutto questo tempo ho cercato a giro metodi "semplici" per il calcolo della collisione tra due oggetti nel modo più preciso possibile, cioè del tipo poligono contro poligono, vertice su poligono, etc, ma come potete immagianare con scarsi risultati.
Ho provato a farne uno di mio, ma non funzionava bene ed il calcolo era talmente peso che già per modelli a basso numero di poligoni il frame rate si abbassava enormemente.
Ho decico quindi per ora di dedicarmi a qualcosa di meno complicato e di continuare a scrivere qualcosa. Poi quando avrò maggiori conoscenze continuerò in seguito ad approfondire questo argomento.

Quello che mi sono proposto di fare è quanto segue: alla creazione del modello ho deciso di estrarre dalle informazioni la boundingSphere caricata, e di tenerla aggiornata secondo le trasformazioni dell'oggetto. Esporrò poi un metodo per permettere di controllare se due dati modelli stanno collidendo con le boundingSphere. Forse non è proprio il caso di utilizzare una classe, ma utilizzare un'interfaccia che esponga queste proprietà comuni agli oggetti di cui vogliamo controllare le collisioni.

Quello che ho fatto è stato definire la seguente interfaccia:

public interface CollisionI
{
BoundingSphere _BoundingSphere { get; }
bool CheckSPhereCollision(CollisionI obj);
}

Questa interfaccia contiene una proprietà e un metodo: la proprietà ritorna la boundingSphere relativa all'oggetto, mentre il metodo controlla se per due oggetti che implementano l'interfaccia ci sia o meno collisione.
Da notare che in questo modo non vincolo il tipo degli oggetti tra cui posso calcolare la collisione, basta che implementino l'interfaccia e posso calcoare tutto.
Certo è un poco spoglia, ma nessuno ci vieta in futuro, quando ne avremo bisogno, di aggiungere nuove funzionalità a questa interfaccia (aggiornando poi naturamente le classi che le implementano).
Ho deciso di utilizzare un'interfaccia anche per dare libera scelta di implementare in modo diverso le funzionalità da una classe all'altra.

Vediamo le modifiche fatte nella classe Ship:
Codice da aggiungere a livello di classe->

//dati per le collisioni
BoundingSphere sphere;
float radius;
public Color colore = Color.Yellow;
public BoundingSphere _BoundingSphere
{
get { return sphere; }
}

public bool CheckSPhereCollision(CollisionI obj)
{
return sphere.Intersects(obj._BoundingSphere);
}


Codice da aggiungere nel costruttore->

sphere = new BoundingSphere();

foreach (ModelMesh mesh in this.myModel.Meshes)
{
foreach (ModelMeshPart mp in mesh.MeshParts)
mp.Effect = currentEffect;
sphere = BoundingSphere.CreateMerged(sphere, mesh.BoundingSphere);
}
radius = sphere.Radius;


Codice da aggiungere nel metodo update (in fondo prima di base.Update(gameTime);)->

sphere.Center = position;
sphere.Radius = radius * scale;


OK!
In questo modo abbiamo aggiunto nella classe i campi necessari a gestire le boundingSphere, abbiamo implementato i metodi richiesti.
Nel costruttore creaiamo la boundingSphere come la sfera più grande che può contente tutte le boundingSphere di ogni singola parte del nostro modello (per ora non mi interessa di calcolare possibili sottocollisioni con le singole parti del modello, mi accontento di un calcolo più grossolano, se volete specializzare fate voi, io mi per ora mi accontento di capire il concetto ^^).
Mi salvo anche il raggio iniziale delle sfera.
Nel metodo update riposiziono la sfera in base alla posizione del modello e riaggiorno il raggio (nel caso sia stato scalato il modello).

Come faccio a disegnare la sfera e a farla visualizzare?
Ho usato una classe statica trovata qua:
Rendering Bounding Spheres
Passando al metodo render la sfera (più altri parametri) essa sarà renderizzata per noi. Certo è un po' pesante quando si deve disegnare molte sfere, quindi non esageriamo! (o se proprio volete renderizzare tutte le sfere vi consiglio di abbassare la risoluzione delle stesse: nel metodo render della classe statica andate a cambiare il valore 30 presente nella chiamata a InitializeGraphics da a 0, le sfere saranno leggermente più spigolose ma ne guadagnarete in prestazioni!).
Quindi nel metodo draw della nostra classe Ship, dopo che abbiamo disegnato il modello aggiungete la seguente riga:
BoundingSphereRenderer.Render(sphere, this.Game.GraphicsDevice, myCamera.View, myCamera.Projection, Color.Yellow);

Ora se lanciate il gioco dovreste vedere una sfera approssimata da due cerchi, di colore giallo.

Ora c'è da fare delle modifiche alla classe MeteorManager:
Codice da aggiungere a livello di classe->

BoundingSphere sphere;
float radius;

public BoundingSphere _BoundingSphere
{
get { throw new NotImplementedException(); }
}

public bool CheckSPhereCollision(CollisionI obj)
{
int val = 1;
for (int i = (cameraR - val); i <= this.cameraR + val; i++)
{
for (int j = (this.cameraC - val); j <= this.cameraC + val; j++)
{
for (int k = (this.cameraP - val); k <= this.cameraP + val; k++)
{
int r = i % cells;
int c = j % cells;
int p = k % cells;
if (r < 0)
r = cells + r;
if (c < 0)
c = cells + c;
if (p < 0)
p = cells + p;
foreach (Trasformation tras in gameWorld[r % cells, c % cells, p % cells].myList)
{
sphere.Center = tras.position;
sphere.Radius = radius * tras.scale;
if (sphere.Intersects(obj._BoundingSphere))
return true;
}
}
}
}
return false;
}

Cosa fa il metodo CheckSPhereCollision?
Prende l'indice della cella in cui siamo, scorre le celle adiacenti a noi, e controlla (riposizionando la sfera e riscalandola) se c'è una collisione con uno degli elementi. Se ne trova uno con cui ho collisione ritorna true, altrimenti se dopo aver controllato tutti gli elementi possibili non ho trovato nessuna collisione ritorno false.

Il costruttore va modificato con le stesse righe aggiunte nel costruttore di Ship.
Il metodo update non va cambiato, dato che per ora i nostri meteoriti devono star fermi.
Andiamo al metodo draw: questa riga va aggiunta nel ramo più interno, all'interno del IF che fa la verifica se la sfera è o meno intersecata dal frustum della telecamera->
BoundingSphereRenderer.Render(sphere, this.Game.GraphicsDevice, myCamera.View, myCamera.Projection, Color.Purple);

Da notare che ora la sphere a cui faccio riferimento non è più quella creata localmente nel Draw, ma quella a livello di classe. In questo modo non andremo a creare un nuovo oggetto ogni volta che viene eseguito il metodo Draw, con un miglioramento dell'utilizzo della memoria direi (su pc non si noterà, ma credo che sulla 360 forse si...).

Per rendere le cose un po' più "carine" ho deciso di fare ciò:
- nel metodo draw del MeteorManager ho diminuito la variabile val a 5
- nel LoadContent di Game1 ho portato il numero di meteoriti per cella da 5 a 3 (per alleggerire il calcolo)
-nella classe Ship ho aggiunto:
public Color colore = Color.Yellow;
-> nel metodo Update di Game1 ho aggiunto:

if (mman.CheckSPhereCollision(myShip))
myShip.colore = Color.Red;
else
myShip.colore = Color.Yellow;

In questo modo quando si rileva una collisione il colore della sfera della nostra nave diverrà rosso, mentre tornerà giallo quando non collidiamo con nulla!

Dovrebbe essere tutto! :D
Se qualcosa non va c'è sempre il file allegato!
E se trovate qualche errore fatemelo notare così correggo ^__^

XNA-tut7.rar

Argomenti trattati:
> Collisioni con BoundingSphere

Se leggete o scaricate i sorgenti lasciate un commento plz, almeno mi rendo conto di come vanno le cose ^^

Alla prossima!

Continua a leggere!

Donazioni

My Menu'