Allora, la prima cosa da fare in tutti i progetti è appunto, mettere in piedi il progetto
. Questo non è un immenso lavoro in xna. Vi serve Microsoft Visual studio 2010 e XNA game studio 4.0.
Una volta aperto visual studio, andiamo su nuovo, progetto
selezionata la voce progetto vediamo che è presente tutta una tipologia di progetti propria di XNA chiamata XNA Game Studio 4.0, come si può vedere nell’elenco a sinistra.
Qui ci sono un po’ di cose interessanti. Si possono immediatamente notare i due tipi di progetto “Windows Game” e “XBox 360 Game”. Questi due tipi di progetto, abbastanza autoesplicativi, sono i progetti principali che dovrete utilizzare per la creazione di un gioco, in quanto creano gli eseguibili veri e propri del vostro gioco. I Progetti “Library” immediatamente sotto i progetti “Game”, servono anch’essi per contenere codice utile per i vostri giochi, ma esse non creano un eseguibile, bensì, come dice appunto il nome, creeranno una libreria che potrete usare nei vostri giochi.
I progetti rimanenti, “Content Pipeline Extention Library” ed “Empty Content Project” servono per gestire gli Asset del vostro gioco. Infatti tutto ciò che è una risorsa utilizzabile all’interno del gioco, sia esso un suono, un modello, una texture o anche solo un xml, viene visto da XNA come un “Content”. Queste risorse vengono incluse nel progetto e convertite in un formato XNA durante la compilazione. Non mi dilungo oltremodo ora su questo argomento che riprenderò in seguito. Vi basti sapere che i progetti di cui sopra vi saranno utili per intervenire sul risultato della conversione dei vostri asset, per poterli poi usare all’interno del vostro gioco.
Ora vi basterà selezionare “Windows Game(4.0)”, dare un nome al gioco, nel mio caso SpaceWar, e selezionare una cartella dove creare il progetto. Fatto questo premete pure su OK.
Questo sarà il risultato:
Quello che avete ottenuto è un progetto pronto per poter essere eseguito, sostanzialmente non farà null’altro che crearvi una finestra vuota ma è esattamente quello che ci serve come punto di partenza.
All’interno del Solution Explorer potete vedere che sono stati creati due progetti, il primo è il vostro gioco con tutti i sorgenti pregenerati da Visual Studio, il secondo è il progetto che si occupa dei vostri asset e da cui il vostro progetto game dipende. Infatti potete vedere sotto la voce “Content References” che il progetto content è referenziato da quello game.
Il codice di partenza
Ora aprendo Program.cs vedrete sostanzialmente una cosa importante:
01 using System;02 03 namespace SpaceWar04 {05 #if WINDOWS || XBOX06 static class Program07 {08 /// <summary>09 /// The main entry point for the application.10 /// </summary>11 static void Main(string[] args)12 {13 using (Game1 game = new Game1())14 {15 game.Run();16 }17 }18 }19 #endif20 }
La funzione Main è il punto di partenza del vostro programma, qui viene creato un oggetto di tipo Game1, cioè il vostro gioco. Subito dopo essere creato viene eseguito con il metodo .Run();
Ora immagino che Game1 non sia il nome che avevate ipotizzato per il vostro gioco, per modificarlo è sufficiente cliccare con il tasto destro del mouse sul file Game1.cs all’interno del Solution Explorer e selezionare Rinomina, una volta che avrete inserito il nuovo nome della classe [NOME CLASSE].cs VS (visual studio) vi chiederà di confermare l’operazione con una dialog, selezionate SI e vedrete che la classe all’interno di Program.cs non sarà più Game1 ma il nome che avete dato alla vostra classe, nel mio caso SpaceWar.
Ora passiamo ad analizzare questa classe generata da Visual studio
1 /// <summary>2 /// This is the main type for your game3 /// </summary>4 public class SpaceWar : Microsoft.Xna.Framework.Game5 {
Come si può vedere, la nostra classe deriva dalla classe Game di XNA. Questa classe rappresenta un gioco nel framework di XNA, è necessario ereditare da essa per poter creare un nostro gioco e con poche funzioni da implementare ci mette fin da subito in condizione di poter gestire le nostre logiche e contenuti.
1 GraphicsDeviceManager graphics;2 SpriteBatch spriteBatch;3 4 public SpaceWar()5 {6 graphics = new GraphicsDeviceManager(this);7 Content.RootDirectory = "Content";8 }
Il GraphicsDeviceManager identifica il device che useremo per renderizzare le nostre risorse. Ne viene creata una istanza nel costruttore e viene assegnata a graphics. In più possiamo vedere che viene assegnato “Content” come RootDirectory di Content, che è il content manager che useremo per caricare le risorse di gioco. “Content” è la directory dove il nostro Content project mette le risorse alla fine del processo di build, ed è dove troviamo le nostre risorse una volta create.
01 /// <summary>02 /// Allows the game to perform any initialization it needs to before starting to run.03 /// This is where it can query for any required services and load any non-graphic04 /// related content. Calling base.Initialize will enumerate through any components05 /// and initialize them as well.06 /// </summary>07 protected override void Initialize()08 {09 // TODO: Add your initialization logic here10 11 base.Initialize();12 }
La funzione Initialize ci offre un buon punto per inizializzare tutti gli oggetti e i componenti del nostro gioco. Viene chiamata una sola volta all’inizio dell’esecuzione, prima ancora di mostrare la finestra di gioco. Tutto ciò che deve essere sempre in vita durante tutto il gioco lo possiamo inizializzare o creare qui.
01 /// <summary>02 /// LoadContent will be called once per game and is the place to load03 /// all of your content.04 /// </summary>05 protected override void LoadContent()06 {07 // Create a new SpriteBatch, which can be used to draw textures.08 spriteBatch = new SpriteBatch(GraphicsDevice);09 10 // TODO: use this.Content to load your game content here11 }
LoadContent ci fornisce un punto base dove caricare le risorse necessarie al nostro gioco, come prima istanza possiamo mettere tutte le risorse di cui abbiamo bisogno in questo punto, in modo da aver assicurato il loro caricamento prima dell’utilizzo nel gioco. Questo metodo viene anche usato nel caso sia necessario ricaricare una parte, o tutte le risorse del nostro gioco, per esempio dopo la gestione di un errore grave, quindi è consigliabile metterevi la gestione delle risorse, in modo da non incorrere in brutte sorprese.
1 /// <summary>2 /// UnloadContent will be called once per game and is the place to unload3 /// all content.4 /// </summary>5 protected override void UnloadContent()6 {7 // TODO: Unload any non ContentManager content here8 }
UnloadContent è la funzione opposta a LoadContent. Come abbiamo appena visto, LoadContent viene usato per caricare le risorse. Questa funzione serve per scaricare le risorse precedentemente caricate, o alla fine dell’esecuzione del gioco, o in seguito ad un errore grave da gestire che comporti il ricaricamento delle risorse. In questo caso infatti viene chiamato UnloadContent seguito da LoadContent, in modo da ripristinare le risorse del vostro gioco. Se avete messo dei caricamenti di risorse all’interno della funzione di LoadContent, ricordatevi di scaricarli in questo metodo.
01 /// <summary>02 /// Allows the game to run logic such as updating the world,03 /// checking for collisions, gathering input, and playing audio.04 /// </summary>05 /// <param name="gameTime">Provides a snapshot of timing values.</param>06 protected override void Update(GameTime gameTime)07 {08 // Allows the game to exit09 if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)10 this.Exit();11 12 // TODO: Add your update logic here13 14 base.Update(gameTime);15 }
La funzione di Update vi servirà per aggiornare il vostro gioco in ogni suo aspetto prima di renderizzarlo a schermo. Questa viene chiamata all’interno di un ciclo continuo del vostro gioco, e qui potete aggiornare ogni singolo oggetto o logica di gioco, aggiornare l’audio e leggere l’input dell’utente, come spiegato dal commento che VS vi ha messo sopra la funzione. In ingresso vi viene passata la variabile gameTime, questa variabile di tipo GameTime contiene diverse informazioni tra cui una essenziale per la corretta gestione dello trascorrere del tempo. Infatti, dato che la funzione Update viene chiamata a frequenza diversa a seconda della macchina su cui il vostro gioco viene eseguito, la quantità di upate per secondo che avete sulla vostra macchina non sarà uguale a quella che ci sarà sulla macchina di qualcun’altro.
Molti vecchi giochi hanno sofferto del fatto che i programmatori non hanno tenuto in conto che la frequenza di aggiornamento dei loro giochi non sarebbe stata sempre la stessa. Ai tempi i processori avevano più o meno tutti una velocità simile, quindi non c’era il problema di mantenere sotto controllo con che velocità veniva aggiornato un gioco. Questo è emerso in seguito, quando la velocità dei processori è andata aumentando e diversificando, i giochi programmati per le vecchie macchine, sulle nuove erano ingestibili, perchè il tempo di gioco risultava accelerato in una maniera tale da rendere impossibile l’interazione dell’utente.
Per evitare questo, all’interno della variabile gameTime trovate ElapsedGameTime, questa variabile rappresenta il tempo trascorso dall’ultima volta che è stata chiamata la funzione di Update e la potrete usare per mantenere sotto controllo il trascorrere del tempo in gioco. Per esempio, nello spostare un oggetto nello spazio, basterà aggiungere alla sua posizione la velocità con cui si muove, moltiplicando quest’ultima per un valore float in cui un secondo è rappresentato da 1.0, ricavabile dai millisencondi in ElapsedGameTime. In questo modo l’oggetto si sposterà tenendo conto del reale trascorrere del tempo percepito dal giocatore e non rispetto a quante volte viene chiamata la funzione di Update sulla macchina.
1 if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)2 this.Exit();
Legge l’input dell’utente, se esso preme sul pad il bottone back il programma viene terminato.
01 /// <summary>02 /// This is called when the game should draw itself.03 /// </summary>04 /// <param name="gameTime">Provides a snapshot of timing values.</param>05 protected override void Draw(GameTime gameTime)06 {07 GraphicsDevice.Clear(Color.CornflowerBlue);08 09 // TODO: Add your drawing code here10 11 base.Draw(gameTime);12 }
Ultima ma non meno importante è la funzione di Draw. Qui disegneremo tutti gli oggetti del nostro mondo e creeremo gli effetti visivi necessari al nostro gioco. Questa funzione è volutamente separata dalla funzione di Update in modo da avere due momenti distinti tra l’aggiornamento del gioco e la sua fase di Rendering, anch’essa viene chiamata in continuazione all’interno del ciclo di gioco. Anche qui ci viene passata una variabile gameTime identica a quella di Update, questa volta per poter gestire correttamente le animazioni della nostra grafica.
1 GraphicsDevice.Clear(Color.CornflowerBlue);
Dice al device di pulire il nostro render Targhet, quindi nel nostro caso la finestra di gioco, con un colore blu. Provate a sostituire al blu un alto colore, per esempio BurlyWood, e vedrete che il colore all’interno della finestra di gioco cambierà . Dopo questa operazione di Clear possiamo inserire il disegno delle nostre risorse, prima della funzione base.Draw(gameTime) , questo perchè la funzione Draw della classe Game è quella che “Convalida” tutto quello che abbiamo scritto nella nostra funzione di Draw ed è l’ultima cosa che dobbiamo chiamare al suo interno.
Conclusione
Abbiamo visto come cominciare un progetto XNA, senza modificare una riga di codice abbiamo una finestra a video che viene aggiornata ( anche se senza nessun contenuto
) possiamo leggere l’input dell’utente e abbiamo la gestione del rendering pronta per poter essere usata.
Alla prossima per le prime modifiche al progetto






