Come ultimo esempio di uso dello SpriteBatch utilizziamo una matrice di trasformazione, da applicare per modificare le coordinate della posizione di ciascun vertice risultante da ogni chiamata di Draw.
Una matrice di trasformazione è un oggetto molto potente, in quanto ci consente di specificare una serie di trasformazioni da applicare in sequenza una dopo l'altra. Il bello di una matrice è che in effetti consente una rappresentazione compatta (16 valori floating point) di tantissime informazioni.
In effetti basta considerare che se abbiamo una matrice di rotazione, una di scala e una di traslazione, semplicemente moltiplicandole tra loro otterremo una unica matrice in grado di effettuare una rotazione, una scala e una traslazione.
Ad esempio, per ruotare una serie di oggetti contamporaneamente intorno al centro dello schermo, dovremmo:
- traslare gli oggetti in modo che l'origine sia il centro dello schermo
- ruotare gli oggetti della rotazione desiderata
- traslare gli oggetti in modo da ripristinare l'origine all'angolo superiore sinistro dello schermo
Per avere questo effetto, creiamo una matrice che rappresenti questa trasformazione (adatta ad uno schermo a risoluzione 800×480
):
Matrix.CreateTranslation(-400, -240, 0) *
Matrix.CreateRotationZ((float)gameTime.TotalGameTime.TotalMinutes) *
Matrix.CreateTranslation(400, 240, 0)
e la passiamo come ultimo parametro dell'overload più lungo del metodo SpriteBatch.Begin
:
sprite_batch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearWrap, null, null, null,
Matrix.CreateTranslation(-400, -240, 0) *
Matrix.CreateRotationZ((float)gameTime.TotalGameTime.TotalMinutes) *
Matrix.CreateTranslation(400, 240, 0));
a questo punto possiamo disegnare una serie di asteroidi, ma questa volta li disegniamo singolarmente (e non secondo una griglia piena):
var width = 800 / 10;
var height = 480 / 7;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 7; j++)
{
if((i + j) % 2 == 0)
sprite_batch.Draw(asteroid, new Rectangle(i * width, j * height, width, height), Color.White);
}
}
Il risultato è:
Possiamo anche ruotare ciascun asteroide in direzione contraria a quella della rotazione globale, in modo che gli asteroidi restino tutti allineati ai lati della finestra; siccome la rotazione intorno al centro è pari al numero di minuti da cui l'applicazione è stata avviata, allora ruotiamo ciascun asteroide del numero di minuti dall'avvio della applicazione negato:
sprite_batch.Draw(asteroid, new Rectangle(i * width, j * height, width, height), null, Color.White,
-(float)gameTime.TotalGameTime.TotalMinutes, new Vector2(asteroid.Width, asteroid.Height) * 0.5f,
SpriteEffects.None, 0.0f);
Il risultato è: