Tutorial XNA creazione gioco : Tutorial XNA creazione di un gioco completo parte 4

Continua la serie del tutorial dedicato alla creazione di un video gioco completo. Questa volta, vedremo colliosini, in particare oggetti (asteroidi) che la nostra navicella spaziale dovrà evitare.

il
Sviluppatore Microsoft .Net, Collaboratore di IProgrammatori

Introduzione

Vedremo in questa quarta parte del tutorial della creazione di un video gioco completo, come aggiungere al nostro video gioco, ostacoli da evitare, come un asteroide da evitare durante il suo movimento.
L’intento di questo articolo è quello di capire lo scenario in cui il giocatore eviti situazioni negative.

Stesura codice per la classe Asteroide

Aprite il progetto in questione, aggiungete un immagine riguardante un oggetto di tipo asteroide, come quello riportato in figura 1. L’immagine deve avere dimensioni pari a 47 x 61.




Figura 1

Per aggiungere un nuovo elemento, nella sezione “Content” nella finestra esplora soluzione, fate click con il tasto destro sulla voce “Content” e dal menu di scelta rapida selezionate la voce “Aggiungi” e la voce “elemento esistente”.
Dopo averlo aggiunto, sempre nella finestra esplora soluzione, fate click sul nome del progetto, ed aggiungete una nuova classe. Dal menu di scelta rapida, selezionate la voce di menu “Aggiungi” e successivamente la voce “Classe”.  Nella finestra che viene visualizzata, impostiamo il nome del file, con il seguente valore “Asteroide.cs” .
Questa classe avrà il compito di gestire questo elemento.
Scriviamo il codice che permette di gestire le informazione della risorsa, come posizione, energia a disposizione, i danni inflitti all’astronave, ed altre informazioni utili.
Lo spazio dei nomi da utilizzare sono per la grafica e per la gestione delle immagini, qui di seguito la dichiarazione dei spazio dei nomi

//spazio dei nomi
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

 Tre metodi, che impostano i valori, che vengono aggiornati di continuo e che permettono di visualizzare a video la situazione.
Qui di seguito si riporta il codice di tale operazioni.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//spazio dei nomi
using Microsoft.Xna.Framework;
namespace XNATutorial
{
    class Asteroide
    {
        //gestione per l'animazione dell'asteroide
        public Animazione AnimazioneAsteroide;
        //posizione dell'immagine
        public Vector2 Posizione;
        //lo stato attivo
        public bool StatoAttivo;
        //Energia
        public int Energia;
        //Totale dei danni inflitti all'astronave
        public int DanniAstronave;
        //totale punteggio
        public int Punteggio;
        //larghezzza
        public int Width
        {
            get { return AnimazioneAsteroide.FrameWidth; }
        }
        //altezza
        public int Height
        {
            get { return AnimazioneAsteroide.FrameHeight; }
        }
        //La velocità di spostamento dell'immagine
        float Velocita;
        public void Initialize()
        {
        }
        public void Update()
        {
        }
        public void Draw()
        {
        }
    }
}


Modifichiamo il metodo Initialize per impostare i valori per i vari membri.
Qui di seguito si riporta il codice per il metodo Initialize

   public void Initialize(Animazione animazione, Vector2 posizione)
        {
            //imposto la risorsa ossia l'immagine asteroide
            AnimazioneAsteroide = animazione;
            //imposto la posizione
            Posizione = posizione;
            //attivo lo stato
            StatoAttivo = true;
            //imposto l'energia
            Energia = 10;
            //imposto i danni all'astronave
            DanniAstronave = 10;
            //imposto la velocità di spostamento
            Velocita = 6f;
            //imposto il punteggio
            Punteggio = 100;
        }


Modifichiamo il metodo update, impostando lo spostamento costante della risorsa, qui di seguito si riporta il codice per il metodo update.

public void Update(GameTime gameTime)
        {
            //imposto la posizione in riferimento alla velocità riducendo il valore
            Posizione.X -= Velocita;
            //imposto alla risorsa la posizione
            AnimazioneAsteroide.Posizione = Posizione;
            //aggiorno l'oggetto di tipo animazione
            AnimazioneAsteroide.Update(gameTime);
            //nel caso che non ho energia oppure non rientra più nello "spazio" visito lo disativo
            if (Posizione.X < -Width || Energia <= 0)
            {
                //Elimino la risorsa
                StatoAttivo = false;
            }
        }



Dobbiamo modificare il metodo draw per la visualizzazione della risorsa. Qui di seguito si riporta il metodo Draw.

public void Draw(SpriteBatch spriteBatch)
        {
            //aggiorno la parte grafica
            AnimazioneAsteroide.Draw(spriteBatch);
        }


Si riporta il codice completo della classe asteroide

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//spazio dei nomi
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace XNATutorial
{
    class Asteroide
    {
        //gestione per l'animazione dell'asteroide
        public Animazione AnimazioneAsteroide;
        //posizione dell'immagine
        public Vector2 Posizione;
        //lo stato attivo
        public bool StatoAttivo;
        //Energia
        public int Energia;
        //Totale dei danni inflitti all'astronave
        public int DanniAstronave;
        //totale punteggio
        public int Punteggio;
        //larghezzza
        public int Width
        {
            get { return AnimazioneAsteroide.FrameWidth; }
        }
        //altezza
        public int Height
        {
            get { return AnimazioneAsteroide.FrameHeight; }
        }
        //La velocità di spostamento dell'immagine
        float Velocita;
        public void Initialize(Animazione animazione, Vector2 posizione)
        {
            //imposto la risorsa ossia l'immagine asteroide
            AnimazioneAsteroide = animazione;
            //imposto la posizione
            Posizione = posizione;
            //attivo lo stato
            StatoAttivo = true;
            //imposto l'energia
            Energia = 10;
            //imposto i danni all'astronave
            DanniAstronave = 10;
            //imposto la velocità di spostamento
            Velocita = 6f;
            //imposto il punteggio
            Punteggio = 100;
        }
        public void Update(GameTime gameTime)
        {
            //imposto la posizione in riferimento alla velocità riducendo il valore
            Posizione.X -= Velocita;
            //imposto alla risorsa la posizione
            AnimazioneAsteroide.Posizione = Posizione;
            //aggiorno l'oggetto di tipo animazione
            AnimazioneAsteroide.Update(gameTime);
            //nel caso che non ho energia oppure non rientra più nello "spazio" visito lo disativo
            if (Posizione.X < -Width || Energia <= 0)
            {
                //Elimino la risorsa
                StatoAttivo = false;
            }
        }
        public void Draw(SpriteBatch spriteBatch)
        {
            //aggiorno la parte grafica
            AnimazioneAsteroide.Draw(spriteBatch);
        }
    }
}


Modifica classe Game1

Ora passiamo al file Game1.cs, in modo che possiamo gestire la nuova classe appena creata.
Si creano dei membri a livello di classe per la gestione della risorsa, e della classe. In particolare i tempi di visualizzazione, la gestione della risorsa e visualizzazione.
Qui di seguito si riportano le dichiarazioni dei membri per la classe Game1.

 //gestione della risorsa asteroide
        //oggetto di tipo Asteroide
        Texture2D AsteroideTexture;
        List<Asteroide> Asteroidi;
        //oggetti per gestire i tempi di visualizzare gli asteroidi
        TimeSpan TempoAsteroide;
        TimeSpan TempoAsteroidePrecedente;
        //numero causale per la visualizzazione degli asteroide
        Random random;

Nell’evento Initialize scriviamo il codice che permette di inizializzare con dei valori  i membri  appena creati.

 protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            // inizializzo la classe
            player = new Player();
            //imposola velocità
            VelocitaSpostamento = 8.0f;
            //oggetti per la gestione dello sfondo
            sfondo1 = new Sfondo();
            sfondo2 = new Sfondo();
            //inizializzo l'oggetto degli asteroide
            Asteroidi = new List<Asteroide>();
            //imposto a zero il tempo per asteroide precendente
            TempoAsteroidePrecedente = TimeSpan.Zero;
            //imposto i tempi degli asteroide
            TempoAsteroide = TimeSpan.FromSeconds(1.0f);
            //inizializzo l'oggetto di tipo random
            random = new Random();
            base.Initialize();
        }

Nell’evento “Content” impostiamo la risorsa precedentemente inserita, quella riguardante l’immagine asteroide.
Qui di seguito si riporta il codice dell’evento “loadContent”

 protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            //oggetto per la gestione dell'effetto immagini in movimento
            Animazione playerAnimazione = new Animazione();
            Texture2D playerTexture = Content.Load<Texture2D>("AstronaveMovimento");
            playerAnimazione.Initialize(playerTexture, Vector2.Zero, 115, 69, 8, 30, Color.White, 1f, true);
            // carico la risorsa (immagine)
            Vector2 playerPosizione = new Vector2(GraphicsDevice.Viewport.TitleSafeArea.X, GraphicsDevice.Viewport.TitleSafeArea.Y + GraphicsDevice.Viewport.TitleSafeArea.Height / 2);
           //ricambio l'oggetto player
            player.Initialize(playerAnimazione, playerPosizione);
            // player.Initialize(Content.Load<Texture2D>("Astronave"), playerPosizione);
            //carico le immagini
            sfondo1.Initialize(Content, "Sfondo1", GraphicsDevice.Viewport.Width, -1);
            sfondo2.Initialize(Content, "Sfondo2", GraphicsDevice.Viewport.Width, -2);
            SfondoPrincipale = Content.Load<Texture2D>("SfondoPriincipale");
            //carico l'immagine asteroide
            AsteroideTexture = Content.Load<Texture2D>("asteroide");
            // TODO: use this.Content to load your game content here
        }



Si crea una funzione, che permette di aggiungere di volta in volta una nuova immagine di tipo asteroide.
Qui di seguito si riporta tale dichirazione.

private void AggiungiAsteroide()
        {
            // Create the animation object
            Animazione AsteroideAnimazione = new Animazione();
            //Inizializzo l'oggetto per la gestione dell'animazione dell'asteroide - possiamo creare anche un immagini con più immagine animate, cambiando il valore 1 con il totale degli elementi
            AsteroideAnimazione.Initialize(AsteroideTexture, Vector2.Zero, 47, 61, 1, 30, Color.White, 1f, true);
            //genero in maniera causale la posizione della risorsa nello schermo
            Vector2 posizione = new Vector2(GraphicsDevice.Viewport.Width + AsteroideTexture.Width / 2, random.Next(100, GraphicsDevice.Viewport.Height - 100));
            //Creo un nuovo asteroide
            Asteroide asteroide = new Asteroide();
            //inzializzo con l'oggetto animazione e posizione
            asteroide.Initialize(AsteroideAnimazione, posizione);
            //aggiungo il nuovo oggetto alla lista degli oggetti di tipo asteroide
            Asteroidi.Add(asteroide);
        }

Si crea una funzione, che permette di aggiornare le informazioni presenti a video, con l’aggiunta o rimozione degli asteroidi.

 /// <summary>
        /// funzione che permette di aggiungere o eliminare le risorse da visualizzare a video
        /// </summary>
        /// <param name="gameTime"></param>
        private void AggiornaAsteroidi(GameTime gameTime)
        {
            //aggiungo un nuovo elemento ogni secondo e mezzo.
            if (gameTime.TotalGameTime - TempoAsteroidePrecedente > TempoAsteroide)
            {
                TempoAsteroidePrecedente = gameTime.TotalGameTime;
                //aggiungo la risorsa a video
                AggiungiAsteroide();
            }
            //nel caso che lo stato attivo è false elimino l'asteroide in questione
            for (int i = Asteroidi.Count - 1; i >= 0; i--)
            {
                Asteroidi[i].Update(gameTime);
                if (Asteroidi[i].StatoAttivo == false)
                {
                    Asteroidi.RemoveAt(i);
                }
            }
        }


Nell’evento update, della classe Game1, inseriamo il codice per eseguire il metodo “AggiornaAsteroidi”, passando come parametro l’oggetto GameTime.


protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
            // TODO: Add your update logic here
            //tasto precedente prima di questa modifica          
            StatoPrecedenteTastiera = StatoCorrenteTastiera;
            //rilevo l'ultimo pulsante digitato
            StatoCorrenteTastiera = Keyboard.GetState();
            //aggiorno il gaming
            UpdatePlayer(gameTime);
            //aggiorno la posizione delle immagini
            sfondo1.Update();
            sfondo2.Update();
            //aggiungo o elimino gli asteroidi
            AggiornaAsteroidi(gameTime);
            base.Update(gameTime);
        }


Nell’evento Draw, dobbiamo invocare il metodo “Draw” del nostro oggetto Asteroide, in modo che aggiorna le singole risorse nel gaming.


/// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.White);
            // TODO: Add your drawing code here
            //ridisegno il tutto
            spriteBatch.Begin();
            spriteBatch.Draw(SfondoPrincipale, Vector2.Zero, Color.White);
            //visualizzo lo spostamento
            sfondo1.Draw(spriteBatch);
            sfondo2.Draw(spriteBatch);
            //aggiorno la visualizzazione della risorsa di tipo asteroide
            for (int i = 0; i < Asteroidi.Count; i++)
            {
                Asteroidi[i].Draw(spriteBatch);
            }
            //aggiorno la visualizzazione
            player.Draw(spriteBatch);
            //termine del ridisegno
            spriteBatch.End();
            base.Draw(gameTime);
        }



Il risultato sarà come riportato in figura 2.





Figura 2

Qui di seguito si riporta il codice completo della classe Game1.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace XNATutorial
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        //oggetto della classe Player
        Player player;
        //Per la gestione della tastiera
        KeyboardState StatoCorrenteTastiera;
        KeyboardState StatoPrecedenteTastiera;
        // la velocità di spostamento
        float VelocitaSpostamento;
        //immagine principale
        Texture2D SfondoPrincipale;
        //gestione delle immagini di sfondo quelle in movimento
        Sfondo sfondo1;
        Sfondo sfondo2;
        //gestione della risorsa asteroide
        //oggetto di tipo Asteroide
        Texture2D AsteroideTexture;
        List<Asteroide> Asteroidi;
        //oggetti per gestire i tempi di visualizzare gli asteroidi
        TimeSpan TempoAsteroide;
        TimeSpan TempoAsteroidePrecedente;
        //numero causale per la visualizzazione degli asteroide
        Random random;
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            // inizializzo la classe
            player = new Player();
            //imposola velocità
            VelocitaSpostamento = 8.0f;
            //oggetti per la gestione dello sfondo
            sfondo1 = new Sfondo();
            sfondo2 = new Sfondo();
            //inizializzo l'oggetto degli asteroide
            Asteroidi = new List<Asteroide>();
            //imposto a zero il tempo per asteroide precendente
            TempoAsteroidePrecedente = TimeSpan.Zero;
            //imposto i tempi degli asteroide
            TempoAsteroide = TimeSpan.FromSeconds(1.0f);
            //inizializzo l'oggetto di tipo random
            random = new Random();
            base.Initialize();
        }
        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            //oggetto per la gestione dell'effetto immagini in movimento
            Animazione playerAnimazione = new Animazione();
            Texture2D playerTexture = Content.Load<Texture2D>("AstronaveMovimento");
            playerAnimazione.Initialize(playerTexture, Vector2.Zero, 115, 69, 8, 30, Color.White, 1f, true);
            // carico la risorsa (immagine)
            Vector2 playerPosizione = new Vector2(GraphicsDevice.Viewport.TitleSafeArea.X, GraphicsDevice.Viewport.TitleSafeArea.Y + GraphicsDevice.Viewport.TitleSafeArea.Height / 2);
           //ricambio l'oggetto player
            player.Initialize(playerAnimazione, playerPosizione);
            // player.Initialize(Content.Load<Texture2D>("Astronave"), playerPosizione);
            //carico le immagini
            sfondo1.Initialize(Content, "Sfondo1", GraphicsDevice.Viewport.Width, -1);
            sfondo2.Initialize(Content, "Sfondo2", GraphicsDevice.Viewport.Width, -2);
            SfondoPrincipale = Content.Load<Texture2D>("SfondoPriincipale");
            //carico l'immagine asteroide
            AsteroideTexture = Content.Load<Texture2D>("asteroide");
            // TODO: use this.Content to load your game content here
        }
        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
            // TODO: Add your update logic here
            //tasto precedente prima di questa modifica          
            StatoPrecedenteTastiera = StatoCorrenteTastiera;
            //rilevo l'ultimo pulsante digitato
            StatoCorrenteTastiera = Keyboard.GetState();
            //aggiorno il gaming
            UpdatePlayer(gameTime);
            //aggiorno la posizione delle immagini
            sfondo1.Update();
            sfondo2.Update();
            //aggiungo o elimino gli asteroidi
            AggiornaAsteroidi(gameTime);
            base.Update(gameTime);
        }
        private void UpdatePlayer(GameTime gameTime)
        {
            //passo l'oggetto gametime
            player.Update(gameTime);
            //nel caso che digito il pulsante esc esco dal gioco
            if (StatoCorrenteTastiera.IsKeyDown(Keys.Escape))
                base.Exit();
            //in riferimento alle freccie della tastiera sposto l'astronave
            if (StatoCorrenteTastiera.IsKeyDown(Keys.Left) )
            {
                player.Posizione.X -= VelocitaSpostamento;
            }
            if (StatoCorrenteTastiera.IsKeyDown(Keys.Right) )
            {
                player.Posizione.X += VelocitaSpostamento;
            }
            if (StatoCorrenteTastiera.IsKeyDown(Keys.Up) )
            {
                player.Posizione.Y -= VelocitaSpostamento;
            }
            if (StatoCorrenteTastiera.IsKeyDown(Keys.Down) )
            {
                player.Posizione.Y += VelocitaSpostamento;
            }
            //imposto la posizione della navicella
            player.Posizione.X = MathHelper.Clamp(player.Posizione.X, 0, GraphicsDevice.Viewport.Width - player.Width );
            player.Posizione.Y = MathHelper.Clamp(player.Posizione.Y, 0, GraphicsDevice.Viewport.Height - player.Height + 50);
        }
        private void AggiungiAsteroide()
        {
            // Create the animation object
            Animazione AsteroideAnimazione = new Animazione();
            //Inizializzo l'oggetto per la gestione dell'animazione dell'asteroide - possiamo creare anche un immagini con più immagine animate, cambiando il valore 1 con il totale degli elementi
            AsteroideAnimazione.Initialize(AsteroideTexture, Vector2.Zero, 47, 61, 1, 30, Color.White, 1f, true);
            //genero in maniera causale la posizione della risorsa nello schermo
            Vector2 posizione = new Vector2(GraphicsDevice.Viewport.Width + AsteroideTexture.Width / 2, random.Next(100, GraphicsDevice.Viewport.Height - 100));
            //Creo un nuovo asteroide
            Asteroide asteroide = new Asteroide();
            //inzializzo con l'oggetto animazione e posizione
            asteroide.Initialize(AsteroideAnimazione, posizione);
            //aggiungo il nuovo oggetto alla lista degli oggetti di tipo asteroidei
            Asteroidi.Add(asteroide);
        }
        /// <summary>
        /// funzione che permette di aggiungere o eliminare le risorse da visualizzare a video
        /// </summary>
        /// <param name="gameTime"></param>
        private void AggiornaAsteroidi(GameTime gameTime)
        {
            //aggiungo un nuovo elemento ogni secondo e mezzo.
            if (gameTime.TotalGameTime - TempoAsteroidePrecedente > TempoAsteroide)
            {
                TempoAsteroidePrecedente = gameTime.TotalGameTime;
                //aggiungo la risorsa a video
                AggiungiAsteroide();
            }
            //nel caso che lo stato attivo è false elimino l'asteroide in questione
            for (int i = Asteroidi.Count - 1; i >= 0; i--)
            {
                Asteroidi[i].Update(gameTime);
                if (Asteroidi[i].StatoAttivo == false)
                {
                    Asteroidi.RemoveAt(i);
                }
            }
        }
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.White);
            // TODO: Add your drawing code here
            //ridisegno il tutto
            spriteBatch.Begin();
            spriteBatch.Draw(SfondoPrincipale, Vector2.Zero, Color.White);
            //visualizzo lo spostamento
            sfondo1.Draw(spriteBatch);
            sfondo2.Draw(spriteBatch);
            //aggiorno la visualizzazione della risorsa di tipo asteroide
            for (int i = 0; i < Asteroidi.Count; i++)
            {
                Asteroidi[i].Draw(spriteBatch);
            }
            //aggiorno la visualizzazione
            player.Draw(spriteBatch);
            //termine del ridisegno
            spriteBatch.End();
            base.Draw(gameTime);
        }
    }
}

Conclusioni

Questa quarta parte del tutorial, ha fornito le basi per utilizzare ostacoli il cui giocatore deve evitare. Anche in questa parte, come nella precedente, possiamo utilizzare un immagine con più frame, per gestire l’ostacolo con animazione particolare per esempio una coda di fuoco per il nostro asteroide.
La gestione delle collisioni è molto utilizzata nei video giochi, e questa parte  ha offerta alcune tecniche basilari.
Tramite la parola downoad possiamo scaricare il file d’esempio utilizzando in questa parte.

 Download