Posts tagged programmazione giochi

Disegnare il testo su schermo – Grafica – XNA Tutorials

In questo articolo analizzeremo DrawString, il metodo della classe SpriteBatch che si occupa di disegnare a schermo il testo. Vedremo velocemente come caricare e un font in memoria e successivamente smanetteremo un pò, guardando i vari risultati ottenuti.

Innanzitutto, lavorando con XNA dobbiamo sapere che il font viene rappresentato, come risorsa, tramite un file di tipo SpriteFont. Questo file conterrà la descrizione del font che utilizzeremo: parametri come la grandezza, il nome del font e così via saranno specificati qui.

Per creare un file SpriteFont, clicchiamo con il pulsante destro del mouse su Content, nell’Esplora Soluzioni, quindi su Aggiungi -> Nuovo Elemento. (in figura il procedimento)

Senza titolo-2

Ci si aprirà una finestra dove dovremo selezionare il tipo di risorsa da creare e il nome da assegnare. Ovviamente sceglieremo SpriteFont e daremo come nome, per esempio, “font1″.

Senza titolo-4

(click per ingrandire)

Dando l’ok sarà questo il codice che ci ritroveremo davanti per questa risorsa (ho cancellato i commenti per risparmiare spazio):

<?xml version="1.0" encoding="utf-8"?>
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
  <Asset Type="Graphics:FontDescription">
    <FontName>Kootenay</FontName>

    <Size>14</Size>

    <Spacing>0</Spacing>

    <UseKerning>true</UseKerning>

    <Style>Regular</Style>

    <CharacterRegions>
      <CharacterRegion>
        <Start>&#32;</Start>
        <End>&#126;</End>
      </CharacterRegion>
    </CharacterRegions>
  </Asset>
</XnaContent>

I parametri sono veramente semplici da capire. FontName definisce il nome del font utilizzato: lo cambierò in “Arial” (senza virgolette ovviamente). Size indica la grandezza del font da utilizzare in fase di scrittura. Spacing indica invece la larghezza tra un carattere e l’altro, mentre UseKerning è un opzione che gestisce il layout del font. Style consente inoltre di decidere che tipo di stile applicare al testo scritto: grassetto, corsivo e così via.

La CharacterRegion indica l’insieme dei caratteri che è possibile riprodurre. Per ora lo lasceremo così, successivamente modificheremo questi valori per permetterci di riprodurre altri caratteri.

Ecco quindi come appare il file dopo la mia piccola modifica:

<?xml version="1.0" encoding="utf-8"?>
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
  <Asset Type="Graphics:FontDescription">
    <FontName>Arial</FontName>

    <Size>14</Size>

    <Spacing>0</Spacing>

    <UseKerning>true</UseKerning>

    <Style>Regular</Style>

    <CharacterRegions>
      <CharacterRegion>
        <Start>&#32;</Start>
        <End>&#126;</End>
      </CharacterRegion>
    </CharacterRegions>
  </Asset>
</XnaContent>

Ora che la nostra risorsa è pronta, dobbiamo includerla con il codice. Creiamo una variabile di tipo SpriteFont e, successivamente, carichiamo la nostra risorsa un pò come avevamo fatto con le immagini, nel metodo LoadContent.

Quindi, prima dichiariamo

    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        SpriteFont font1;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

e dopo carichiamo.

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            font1 = Content.Load<SpriteFont>("font1");
        }

A questo punto non dobbiamo fare altro che disegnare un pò di cose a schermo. Iniziamo con una scritta semplice, la classica “Ciao Mondo!” (non poteva mancare). Modificando il codice del metodo Draw con le istruzioni riportate qui,

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            spriteBatch.Begin();
            spriteBatch.DrawString(font1, "Ciao Mondo!!!", new Vector2(10, 10), Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }

il risultato sarà semplicemente questo illustrato.

Senza titolo-6

(click per ingrandire)

Proviamo qualcos’altro.

Tornando ad analizzare il metodo SpriteBatch.DrawString, vediamo un pò quali sono i parametri che diamo.

  1. Il primo parametro di tipo SpriteFont è il font con cui disegneremo il testo. Verdana, Arial, Times New Roman di qualsiasi grandezza, questo è il punto di riferimento per eventuali cambiamenti.
  2. Il secondo parametro è la stringa di testo che vogliamo portare su schermo.
  3. Il terzo parametro è un vettore di tipo Vector2 che si occupa di definire su schermo la posizione del testo. Nel mio caso volevo mettere il testo in posizione x = 10 ed y = 10 ed ho definito così il vettore di conseguenza.
  4. Il quarto parametro è il colore del testo, che possiamo cambiare a nostro piacimento. Qui di seguito qualche prova con i valori Green, Red e Lime.

Senza titol654

Senza titolo-3

Senza titolo-2

Dopo queste prove con il colore vediamo l’overload del metodo da noi usato, che al posto dei classici quattro parametri ne presenta di più, precisamente 9. Grazie a questo metodo potremo ruotare il nostro testo:

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            spriteBatch.Begin();

            spriteBatch.DrawString(
                                    font1,
                                    "testo di prova 2",
                                    new Vector2(200, 200),
                                    Color.White,
                                    10,
                                    new Vector2(
                                        font1.MeasureString("testo di prova 2").X/2,
                                        font1.MeasureString("testo di prova 2").Y/2
                                        ),
                                    1.0f,
                                    SpriteEffects.None,
                                    1
                                    );

            spriteBatch.End();

            base.Draw(gameTime);
        }

I primi quattro parametri rimangono invariati: non abbiamo bisogno di descriverli un’altra volta. Ciò che cambia invece arriva dopo. Abbiamo quel “10″ che rappresenta l’angolo di rotazione che vogliamo usare. Ovviamente serve un centro di rotazione, ed a questo serve il successivo parametro, di tipo Vector2. E’ prevista anche la possibilità di gestire lo scaling del testo (il ridimensionamento) tramite un parametro di tipo Float. Infine abbiamo il tipo di effetto da dare al testo (provate voi stessi a smanettarci per vedere i risultati) ed infine la profondità del layout come ultimo parametro.

Ecco qui il risultato del codice, ed anche con il testo per ora abbiamo finito.

Senza titolo55

(click per ingrandire)

P.S: è importante sapere come regolarsi con le CharacterRegions del file XML della risorsa. Come ho detto precedentemente, questo parametro del font permette di decidere quali caratteri verranno ridisegnati. I valori che notiamo nel codice

<?xml version="1.0" encoding="utf-8"?>
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
  <Asset Type="Graphics:FontDescription">
    <FontName>Arial</FontName>

    <Size>14</Size>

    <Spacing>0</Spacing>

    <UseKerning>true</UseKerning>

    <Style>Regular</Style>

    <CharacterRegions>
      <CharacterRegion>
        <Start>&#32;</Start>
        <End>&#126;</End>
      </CharacterRegion>
    </CharacterRegions>
  </Asset>
</XnaContent>

tra i tag Start ed End sono i codici ASCII di partenza e di arrivo dell’insieme dei caratteri riproducibili. In poche parole, il sistema sarà in grado di disegnare su schermo i caratteri che hanno il codice ASCII che va da 32 a 126. Volendo portare a schermo dei caratteri come “ò” oppure “à” dovremo necessariamente modificare l’intervallo, in quando non include questi caratteri. Al posto di 126, nel tag End, usate 255 e non dovreste avere ulteriori problemi.

Cheers.

  • Share/Bookmark

Disegnare una sprite animata – Grafica – XNA Tutorials

In questo articolo impareremo a disegnare su schermo una sprite animata tramite poche e semplici istruzioni. Innanzitutto, ecco l’immagine che ho usato io per questa prova:

Senza titolo-1

Come potete notare non è niente di che: un insieme di quadrati con un semplice effetto di illuminazione, fatto in photoshop in due secondi. Ora, se volessimo partire da questa immagine per creare un animazione, come fare? Basta ragionare in termini di frame. Mi spiego meglio: provate a disegnare uno dopo l’altro, nella stessa posizione, i quadrati contenuti in quest’immagine e non uno a fianco all’altro.

Quale effetto otterreste? Eccolo.

[gickr.com]_651a3e0d-5b3f-a354-9dbe-384d07960c7c

Spero di essere stato chiaro, più di tanto non potevo :P

Prenderemo la nostra immagine di esempio e disegneremo su schermo una parte per volta e non tutta insieme, in modo tale da dare l’idea dell’animazione. Come si fa? Ecco il codice, che piano piano spiegherò nel dettaglio (per quanto sia veramente facile).

Nota: nella stessa cartella dell’eseguibile ho posizionato il file “immagine.png”, in quanto ho caricato la texture direttamente da file.

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;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace GiocoDiProva
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Texture2D immagine;
        int number_of_frames = 10;
        int current_frame = 0;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {

            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            immagine = Texture2D.FromFile(this.GraphicsDevice, "immagine.png");
        }

        protected override void UnloadContent()
        {

        }

        protected override void Update(GameTime gameTime)
        {
            if (current_frame < number_of_frames)
            {
                current_frame++;
            }
            else
            {
                current_frame = 0;
            }

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            spriteBatch.Begin();
            spriteBatch.Draw(immagine, new Rectangle(200, 200, 40, 40), new Rectangle(current_frame * 40, 0, 40,40), Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

Partiamo dalle dichiarazioni delle variabili:

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Texture2D immagine;
        int number_of_frames = 10;
        int current_frame = 0;

La Texture2D immagine è la nostra texture che verrà “sezionata” per poi essere disegnata. “number_of_frames” è invece il numero dei frames totali di cui si compone la nostra animazione. “current_frame” è invece un intero che assumerà il numero del frame attuale per sapere in ogni istante quale parte dell’immagine deve essere riprodotta.

Il metodo Update spiega meglio l’utilizzo di queste due variabili (il metodo LoadContent l’ho saltato perchè mi pare ovvio ormai a cosa serve :) ):

protected override void Update(GameTime gameTime)
        {
            if (current_frame < number_of_frames)
            {
                current_frame++;
            }
            else
            {
                current_frame = 0;
            }

            base.Update(gameTime);
        }

In questo metodo controlliamo se il frame attuale che stiamo considerando è minore del conteggio totale dei frames. Se ciò avviene, allora andremo avanti di frame passando al successivo, tramite un semplice incremento. Altrimenti, se il controllo restituisce falso, vuol dire che abbiamo raggiunto la fine dell’animazione e dobbiamo riniziare da capo. A questo proposito aggiungiamo il “current_frame = 0″.

Ora che abbiamo deciso quale frame disegnare, dobbiamo utilizzare quest’informazione nel metodo Draw. Ecco il codice:

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            spriteBatch.Begin();
            spriteBatch.Draw(immagine, new Rectangle(200, 200, 40, 40), new Rectangle(current_frame * 40, 0, 40,40), Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }

Stavolta basta una sola istruzione. Precisamente, il metodo Draw che prende quattro parametri in input:

  1. la texture da disegnare.
  2. il rettangolo di “destinazione”, ovvero il nostro frame con le coordinate di disegno e la grandezza del frame desiderato. In quest’esempio, ho disegnato la sprite alle coordinate 200,200, con una grandezza del frame di 40×40 (che è appunto la dimensione di ogni quadrato incluso il contorno).
  3. il rettangolo “sorgente”, ovvero quel rettangolo che racchiude tutta l’immagine (per intenderci, la prima da me esposta all’inizio dell’articolo.
  4. il colore da usare per la modulazione del canale (come al solito ho usato il bianco).

Come per ogni test, premete F5 e guardate il risultato ;)

  • Share/Bookmark

Ridimensionare una Sprite – Grafica – XNA Tutorials

Ridimensionare una sprite, al pari della rotazione, è un’operazione molto semplice, nonostante sia molto comune in tantissimi giochi. Comunque sia, chiacchiere a parte, in questo articolo affronteremo due metodi di ridimensionamento: prima quello “fisso”, ovvero il disegno della texture ridimensionata, e successivamente quello “variabile” tramite input da tastiera.

  • Ridimensionamento “fisso”

Il nocciolo della questione, come potete immaginare, si trova nel metodo Draw, che come abbiamo detto prima è il punto in cui i contenuti vengono disegnati su schermo. A fare la parte importante, nel nostro caso, è il rettangolo che noi diamo come parametro nel metodo Draw dello SpriteBatch da noi utilizzato. Nello specifico, di questo rettangolo modificheremo larghezza ed altezza.

Ecco il metodo Draw:

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();

            spriteBatch.Draw(immagine, new Rectangle(10, 10, immagine.Width * 2, immagine.Height * 2), Color.White);

            spriteBatch.End();

            base.Draw(gameTime);
        }

Stavolta ho semplicemente dato come dimensione del rettangolo una larghezza pari al doppio della rispettiva nell’immagine e un’altezza calcolata in egual modo. Il risultato? Eccolo.

asd

(Click per ingrandire)

Adesso passiamo all’ingrandimento dato dall’input dell’utente. Anche questo è molto semplice da gestire: tuttavia, richiederà qualche variabile in più in quanto abbiamo dei problemi a livello di tipo di variabili.

Senza titolo-2

Mi spiego meglio: come potete vedere dall’intellisense, la proprietà Width e Height di una texture sono di tipo Int. Questo vuol dire che, per ottenere un’ingrandimento di, toh, una volta e mezzo (x1.5) avremo dei problemi  rimanendo ad usare gli interi. Per cui, ecco cosa faremo.

  1. Creiamo una variabile di tipo float, chiamata moltiplicatore. Qui memorizzeremo il valore da usare per la moltiplicazione nel ridimensionamento.
  2. Modifichiamo il metodo Update in modo da gestire l’input da tastiera.
  3. Modifichiamo il metodo Draw in modo da disegnare bene la nostra immagine.

Ecco la nostra variabile:

    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Texture2D immagine;

        float moltiplicatore = 1.0f;

In fase di disegno moltiplicheremo le dimensioni dell’immagine per questo valore. Ponendo come valore iniziale 1.0, inizialmente la nostra immagine verrà disegnata nella sua grandezza naturale.

Ora gestiamo l’input da parte dell’utente: stavolta i tasti che useremo saranno il “+” per ingrandire ed il “-” per rimpicciolire. Il metodo Update verrà riscritto così:

        protected override void Update(GameTime gameTime)
        {
            if (Keyboard.GetState().IsKeyDown(Keys.Add))
            {
                if (moltiplicatore < 2.0f)
                {
                    moltiplicatore += 0.05f;
                }
            }

            if (Keyboard.GetState().IsKeyDown(Keys.Subtract))
            {
                if (moltiplicatore > 0.5f)
                {
                    moltiplicatore -= 0.05f;
                }
            }

            base.Update(gameTime);
        }

Per evitare problemi nel mostrare l’immagine (dimensioni negative e così via) ho limitato il ridimensionamento in modo tale che vada da un minimo di 0.5 (la metà della grandezza originale) a 2.0 (il doppio ;) ). I valori “Keys.Add” e “Keys.Subtract” sono i corrispettivi del “+” e del “-”.

(nel caso togliate i limiti del ridimensionamento, andando in negativo si ottiene l’immagine capovolta, quando la variabile moltiplicatore è diversa da zero)

Infine ecco il metodo Draw riscritto:

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();

            spriteBatch.Draw(immagine, new Rectangle(10, 10, (int)(immagine.Width * moltiplicatore), (int)(immagine.Height * moltiplicatore)), Color.White);

            spriteBatch.End();

            base.Draw(gameTime);
        }

Ogni dimensione viene moltiplicata per il moltiplicatore (gioco di parole non voluto) e successivamente visto come int tramite l’aggiunta di “(int)” dietro l’espressione da usare. Potete provare voi stessi il tutto senza ulteriori spiegazioni :D

  • Share/Bookmark

Mercury Particle Engine – XNA

Arieccomi qua, a scrivere dopo poche ore dal post precedente. Volevo segnalarvi velocemente un sistema particellare per XNA decisamente utile e comodo, il Mercury Particle Engine. Reperibile su http://mpe.codeplex.com/, è sicuramente degno di nota in quanto presenta un sistema comodo da implementare e un editor semplicissimo da usare ma allo stesso tempo potentissimo.

Ecco alcuni screenshot di cose realizzate in meno di due minuti:

Senza titolo-2

Senza titolo-3

Senza titolo-4

Dateci un’occhiata ;)

  • Share/Bookmark

Introduzione – Grafica – XNA Tutorials

Dunque, in questa seconda parte dei tutorials, come potrete ben immaginare, ci occuperemo delle prime basilari operazioni di grafica. Impareremo ad importare un’immagine per poi disegnarla oppure caricarla direttamente da un file. Proseguiremo modificando un pò le nostre immagini disegnate, con operazioni quali la rotazione e il ridimensionamento.

Provvederemo anche al testo, studiando la funzione DrawString, essenziale per lo scopo. Ed ancora, vedremo le Sprite Animate e il loro disegno su schermo, con tutte le modalità relative.

Insomma, in questo primo capitolo affonderemo le mani nella pasta per bene. Non parlerò oltre, buona lettura!

  • Share/Bookmark

PHStudios, tutorials per XNA veramente utili.

Riporto al volo un link con dei tutorials decisamente utili su XNA. Gli argomenti trattati sono tra i più disparati:

  • Advanced Motion
  • Intro Screen
  • Keyboard Movement
  • Gamepad Movement
  • Input Class
  • Menu System
  • Advanced Screen System
  • Advanced Menu System
  • Randomize Sprites
  • Gameplay Object
  • Mouse
  • Clickable Game Object
  • Particle System
  • Basic Physics
  • Content Pipeline
  • Game Object Data
  • Play Audio
  • XACT3
  • Audio System
  • Basic Saving and Loading
  • Basic Camera and Transform Matrix
  • Pop-ups
  • Video

Trovate i samples su http://www.phstudios.com/?q=node/108

  • Share/Bookmark

Mettiamo tutto insieme (Conclusioni) – Creazione di un gioco con SDL.NET

In realtà sotto questo punto di vista c’è tanto da scrivere ma anche poco da scrivere: tanto perchè ci sarebbe innanzitutto da compilare il nostro codice. Una volta ottenuti gli eseguibili funzionanti dobbiamo avere una precisa struttura della cartella del nostro programma. Ed a questo, come avete visto, ci abbiamo pensato in precedenza.

Dobbiamo testare a fondo il gioco, in modo tale da notare rallentamenti (non in questo caso ovviamente). Nel caso ci sia qualcosa che non va, si riprende il codice e lo si modifica, si cercano degli errori. Certo, questo è facile da dire ma spesso è assolutamente stancante da fare nella pratica. Nonostante abbia fatto questo gioco in due giorni scarsi c’è da dire che non era tutto perfetto da subito: man mano che andavo avanti ho fatto prove, compilato, ricompilato e modificato per poi compilare nuovamente.

Niente perchè, appunto, è facile da scrivere ma difficile da fare.

Una grande fortuna, devo ammetterlo, è stato quello di poter lavorare con un linguaggio managed come il C#, eliminando in questo modo tutta una serie di problemi che si potevano presentare con un linguaggio si più efficace, ma altrettanto complesso come il C++.

Adesso che abbiamo la nostra struttura pronta, non dobbiamo fare altro che creare un programma di installazione per il nostro gioco: potrete scoprire come si fa leggendo l’articolo dedicato a questo scopo su questo stesso blog.

Grazie per l’ascolto!

  • Share/Bookmark

Gestiamo l’Input. – Creazione di un gioco con SDL.NET

Il gioco che stiamo creando, ovviamente, non va mica avanti da solo. Qualcuno deve far muovere le cose, fare in modo che determinati eventi e determinati spostamenti avvengano. Quel qualcuno è il nostro giocatore. Come già spiegato nella “guida base” l’input è gestito facilmente dalle librerie SDL e applicare questi concetti alla pratica sarà ancora più facile.

In tutta la struttura del gioco c’è solo un metodo che si occupa di prendere tutto l’input ed elaborarlo: il metodo Events_KeyboardDown. Ecco il codice, riproposto qui di seguito, che verrà successivamente commentato in modo da essere compreso in tutto e per tutto.

[code]]czoyNDcwOlwiDQpwcml2YXRlIHZvaWQgRXZlbnRzX0tleWJvYXJkRG93bihvYmplY3Qgc2VuZGVyLCBLZXlib2FyZEV2ZW50QXJncyB7WyYqJl19ZSkNCiAgICAgICAgew0KICAgICAgICAgICAgc3dpdGNoIChzdGF0bykNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBjYXtbJiomXX1zZSBHYW1lU3RhdGUuSW50cm86DQogICAgICAgICAgICAgICAgICAgIGlmIChlLktleSA9PSBLZXkuUmV0dXJuKSBzdGF0byA9IEdhe1smKiZdfW1lU3RhdGUuR2FtZTsNCiAgICAgICAgICAgICAgICAgICAgaWYgKGUuS2V5ID09IEtleS5Fc2NhcGUpIHN0YXRvID0gR2FtZVN0YXR7WyYqJl19ZS5FeGl0Ow0KICAgICAgICAgICAgICAgICAgICBicmVhazsNCg0KICAgICAgICAgICAgICAgIGNhc2UgR2FtZVN0YXRlLkdhbWU6DXtbJiomXX0KICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKHN0YXRvX3ApDQogICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAge1smKiZdfSAgICAgICAgICBjYXNlIFN0YXRvUGFydGl0YS5QcmltYToNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKGUuS2V7WyYqJl19eSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgS2V5LntbJiomXX1SZXR1cm46DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0b19wID0gU3RhdG9QYXJ0aXRhLkluQ29yc287e1smKiZdfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVzZXRHYW1lKCk7DQoNCiAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgICAgICBicmVhazsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIEtleS5Fc2NhcGU6DQogIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdG8gPSBHYW1lU3RhdGUuRXhpdDsNCiAgICAgICAgICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgICAgICAgICBjYXNlIFN0YXRvUGFydGl0YS5JbkNvcnNvOg0KICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKGUuS2V5KQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHsNCiAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgS2V5LkxlZnRBcnJvdzoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICBpZiAocG9zaXppb25lX2dpb2NhdG9yZSA+IDApIHBvc2l6aW9uZV9naW9jYXRvcmUtLTsNCiAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICBicmVhazsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIEtleS5SaWdoe1smKiZdfXRBcnJvdzoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChwb3NpemlvbmVfZ2lvY2F0b3JlIDwgcG9zaXp7WyYqJl19aW9uaV9naW9jYXRvcmUuQ291bnQgLSAxKSBwb3NpemlvbmVfZ2lvY2F0b3JlKys7DQogICAgICAgICAgICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSBLZXkuRXNjYXBlOg0KICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlc2V0R2FtZSgpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgIHN0YXRvX3AgPSBTdGF0b1BhcnRpdGEuUHJpbWE7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3tbJiomXX07DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KDQogICAge1smKiZdfSAgICAgICAgICAgICAgICAgICAgY2FzZSBTdGF0b1BhcnRpdGEuRmluZToNCg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN7WyYqJl19d2l0Y2ggKGUuS2V5KQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtbJiomXX0gIGNhc2UgS2V5LlJldHVybjoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRvX3AgPSBTdGF0b1BhcnRpe1smKiZdfXRhLlByaW1hOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgICAgIGNhc2UgS2V5LkVzY2FwZToNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRvID0gR3tbJiomXX1hbWVTdGF0ZS5FeGl0Ow0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICAgICAge1smKiZdfSAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgfQ0KICB7WyYqJl19ICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICBjYXNlIEdhbWVTdGF0ZS5FeGl0Og0KDQogICAgICAgIHtbJiomXX0gICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgIH0NCiAgICAgICAgfQ0KXCI7e1smKiZdfQ==[[/code]

Innanzitutto diamo uno sguardo ai parametri in input: c'è il sender, di tipo generico object, e la variabile “e” di tipo KeyboardEventArgs. Proprio quest'ultima variabile sarà il perno di tutto il metodo.

Prima di dare uno sguardo al codice, inoltre, ricordatevi sempre che stiamo ragionando in base agli stati: diverso è lo stato in cui il gioco si trova, diverse saranno le necessità a cui dovremo far fronte. In parole povere: ci troviamo nella schermata d'introduzione? Non ci sarà bisogno di gestire lo spostamento del personaggio. E così via discorrendo.

Di conseguenza opereremo in questo modo: creando prima uno switch che comprenda i casi di tutti gli stati, e per ognuno di essi effettuare operazioni diverse. Partiamo dal primo caso:

[code]]czoxOTM6XCINCmNhc2UgR2FtZVN0YXRlLkludHJvOg0KICAgICAgICAgICAgICAgICAgICBpZiAoZS5LZXkgPT0gS2V5LlJldHVybil7WyYqJl19IHN0YXRvID0gR2FtZVN0YXRlLkdhbWU7DQogICAgICAgICAgICAgICAgICAgIGlmIChlLktleSA9PSBLZXkuRXNjYXBlKSBzdGF0b3tbJiomXX0gPSBHYW1lU3RhdGUuRXhpdDsNCiAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQpcIjt7WyYqJl19[[/code]

Questo è sicuramente il caso più semplice. Fin quando il gioco si troverà nello stato Intro, verrà mostrato a schermo il background con le istruzioni del gioco, oltre ad un invito a premere invio per continuare. Dunque, sono due le cose che possiamo fare: o passiamo alla schermata di gioco oppure ce ne usciamo.

Qui arriva in nostro aiuto il parametro “e”. La proprietà e.Key, infatti, permette di sapere se in quel momento un determinato pulsante sulla tastiera è stato premuto. Il controllo, nella sua maniera più semplice, avviene con questa sintassi:

if (e.Key == Key.codice_chiave) // qui vanno le operazioni da eseguire.

Come potete vedere dall'intellisense, Key (e non e.Key, che è una proprietà) è un enumerazione comprendente tutti i caratteri della tastiera. Quindi ci basterà confrontare e.Key con un valore di Key per sapere se quel determinato pulsante è stato premuto.

Tornando al nostro codice, quindi, nella prima riga verifichiamo che il tasto invio venga premuto. Nel caso viene premuto (e l'uguaglianza ritorna true) allora lo stato viene modificato in GameState.Game. Se invece la condizione non è verificata tutto rimane come prima.

Nella seconda istruzione le cose sono simili: verifichiamo che il pulsante ESC venga premuto. Se si allora passeremo allo stato GameState.Exit, che provvederà a farci uscire dal programma. Altrimenti tutto rimarrà invariato e la schermata continuerà ad essere mostrata. Andiamo avanti col nostro codice.

[code]]czoyMDI2OlwiDQogY2FzZSBHYW1lU3RhdGUuR2FtZToNCiAgICAgICAgICAgICAgICAgICAgc3dpdGNoIChzdGF0b19wKQ0KICAgICB7WyYqJl19ICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSBTdGF0b1BhcnRpdGEuUHJpbWE6DQogICAgICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgIHN3aXRjaCAoZS5LZXkpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgY2FzZSBLZXkuUmV0dXJuOg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19IHN0YXRvX3AgPSBTdGF0b1BhcnRpdGEuSW5Db3JzbzsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlc2V0R3tbJiomXX1hbWUoKTsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICAgICAge1smKiZdfSAgICAgICAgICAgIGNhc2UgS2V5LkVzY2FwZToNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRvID0gR2F7WyYqJl19bWVTdGF0ZS5FeGl0Ow0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCg0KICAgICAgICAgICAgICAgICAgICAgICAge1smKiZdfWNhc2UgU3RhdG9QYXJ0aXRhLkluQ29yc286DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3dpdGNoIChlLktleSkNCiAgICB7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIEtleS5MZWZ0QXJyb3tbJiomXX13Og0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBvc2l6aW9uZV9naW9jYXRvcmUgPiAwKSBwb3Npemlve1smKiZdfW5lX2dpb2NhdG9yZS0tOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgICAgICAgICAgIGNhc2UgS2V5LlJpZ2h0QXJyb3c6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtbJiomXX0gaWYgKHBvc2l6aW9uZV9naW9jYXRvcmUgPCBwb3NpemlvbmlfZ2lvY2F0b3JlLkNvdW50IC0gMSkgcG9zaXppb25lX2dpb2NhdG9ye1smKiZdfWUrKzsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICBjYXNlIEtleS5Fc2NhcGU6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZXNldEdhbWUoKTsNCntbJiomXX0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0b19wID0gU3RhdG9QYXJ0aXRhLlByaW1hOw0KICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICB7WyYqJl19ICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgU3RhdG9QYXJ0aXRhLkZpbmU6DXtbJiomXX0KDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3dpdGNoIChlLktleSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7e1smKiZdfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIEtleS5SZXR1cm46DQogICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgICAgc3RhdG9fcCA9IFN0YXRvUGFydGl0YS5QcmltYTsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtbJiomXX0gICBicmVhazsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIEtleS5Fc2NhcGU6DQogICAgICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgc3RhdG8gPSBHYW1lU3RhdGUuRXhpdDsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYntbJiomXX1yZWFrOw0KICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KXCI7e1smKiZdfQ==[[/code]

Dunque, in questa parte di codice stiamo gestendo il caso dello stato GameState.Game. Ci troviamo nella schermata di gioco vera e propria e, quindi, è da qui che dobbiamo partire per gestire i vari stati in cui si trova la partita (la variabile stato_p).

Per questo apriamo un nuovo switch, stavolta prendendo in esame il valore della variabile, appunto, stato_p.

Nel primo caso il discorso è semplice: ci troviamo nella schermata in cui il gioco non ancora inizia e possiamo fare solo due cose: o premere invio per iniziare la partita, oppure premere esc per uscire dal gioco. In codice, ciò si traduce in:

[code]]czo1NDI6XCINCmNhc2UgU3RhdG9QYXJ0aXRhLlByaW1hOg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN3aXRjaCAoZS5LZXl7WyYqJl19KQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSBLZXkuUntbJiomXX1ldHVybjoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRvX3AgPSBTdGF0b1BhcnRpdGEuSW5Db3JzbzsNe1smKiZdfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZXNldEdhbWUoKTsNCg0KICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgS2V5LkVzY2FwZToNCiAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0byA9IEdhbWVTdGF0ZS5FeGl0Ow0KICAgICAgICAgICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgYnJlYWs7DQpcIjt7WyYqJl19[[/code]

Come potete vedere, al posto di fare più if ho fatto un altro blocco switch con i casi corrispondenti ai pulsanti che volevo gestire. Se premiamo invio lo stato della partita passa da "Prima" a "InCorso" e le statistiche di gioco vengono resettate tramite ResetGame(). Nel caso in cui venga premuto Esc, invece, c'è poco da dire: la variabile "stato" passa a GameState.Exit e usciremo dal gioco dopo gli opportuni passaggi.

Per il caso della partita in corso, invece, le cose da gestire sono giusto un paio in più.

[code]]czo3ODg6XCINCmNhc2UgU3RhdG9QYXJ0aXRhLkluQ29yc286DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3dpdGNoIChlLkt7WyYqJl19ZXkpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIEtleXtbJiomXX0uTGVmdEFycm93Og0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBvc2l6aW9uZV9naW9jYXRvcmUgPiAwe1smKiZdfSkgcG9zaXppb25lX2dpb2NhdG9yZS0tOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICB7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgS2V5LlJpZ2h0QXJyb3c6DQogICAgICAgICAgICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICAgaWYgKHBvc2l6aW9uZV9naW9jYXRvcmUgPCBwb3NpemlvbmlfZ2lvY2F0b3JlLkNvdW50IC0gMSkgcG9zaXppb25le1smKiZdfV9naW9jYXRvcmUrKzsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICAgICAgICAgICBjYXNlIEtleS5Fc2NhcGU6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZXNldHtbJiomXX1HYW1lKCk7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0b19wID0gU3RhdG9QYXJ0aXRhLlByaW1hOw0Ke1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiB7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQpcIjt7WyYqJl19[[/code]

Si fa il classico switch per vedere qualche tasto è stato premuto e ora i tasti da gestire sono tre: la freccia sinistra (Key.LeftArrow nella nostra enumerazione Key), la freccia destra (corrispondente a RightArrow) e il tasto Esc per uscire dalla partita in corso. Non è stata prevista una pausa durante il gioco.

E per ultimo gestiamo il caso della partita terminata:

[code]]czo0ODk6XCINCmNhc2UgU3RhdG9QYXJ0aXRhLkZpbmU6DQoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKGUuS2V7WyYqJl19eSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgS2V5LntbJiomXX1SZXR1cm46DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0b19wID0gU3RhdG9QYXJ0aXRhLlByaW1hOw0Ke1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICBjYXNlIEtleS5Fc2NhcGU6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0byA9IEdhbWVTdGF0ZS5FeHtbJiomXX1pdDsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAge1smKiZdfSB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQpcIjt7WyYqJl19[[/code]

Non credo serva spiegare davvero quello che ho fatto, è lo stesso identico procedimento che ho adoperato per i due casi precedenti.

Bene, anche per l'input ci siamo e, a meno che non ci siamo scordati qualcosa in precedenza, il nostro gioco è praticamente completo a livello di codice. Adesso ci prepareremo per la compilazione, concludendo così l'ultimo articolo di questa serie di tutorials.

  • Share/Bookmark

Disegnamo gli oggetti. – Creazione di un gioco con SDL.NET

In fase di disegno degli oggetti, come potete immaginare, le cose si complicano un po’. Dobbiamo contare varie cose:

  • Dobbiamo generare un oggetto al secondo;
  • Spostare la moglie una volta al secondo;
  • Capire se abbiamo preso l’oggetto in caduta oppure no, quindi segnare i punti o togliere le vite.

Iniziamo quindi con il codice, stavolta completo, di UpdatePartita().

[code]]czozMDczOlwiDQpwcml2YXRlIHZvaWQgVXBkYXRlUGFydGl0YSgpDQogICAgICAgIHsNCiAgICAgICAgICAgIHN3aXRjaCAoc3RhdG97WyYqJl19X3ApDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgY2FzZSBTdGF0b1BhcnRpdGEuUHJpbWE6DQogICAgICAgICAgICAgIHtbJiomXX0gICAgICBWaWRlby5TY3JlZW4uQmxpdChiYWNrZyk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJle1smKiZdfW5kZXIoXCJQdW50ZWdnaW86IDBcIiwgU3lzdGVtLkRyYXdpbmcuQ29sb3IuQmxhY2spLCBuZXcgU3lzdGVtLkRyYXdpbmcuUG9pbnQoNntbJiomXX0wLCAyKSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcihcIlByZW1pIEludmlvIHBlciBpbntbJiomXX1pemlhcmUgbGEgcGFydGl0YS4uLlwiLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0ZW0uRHJhd2luZy5Qb2ludHtbJiomXX0oNjAsIDE3KSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcihcIlZpdGU6IDBcIiwgU3lzdGV7WyYqJl19bS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50KDQwMCwgMikpOw0KICAgICAgICAgICAgICAgIHtbJiomXX0gICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICBjYXNlIFN0YXRvUGFydGl0YS5JbkNvcnNvOg0KICAgICAgICAgICAgICAgICAge1smKiZdfSAgVmlkZW8uU2NyZWVuLkJsaXQoYmFja2cpOw0KICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChmMS5SZW5kZXJ7WyYqJl19KFwiUHVudGVnZ2lvOiBcIiArIHB1bnRlZ2dpby5Ub1N0cmluZygpLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0e1smKiZdfWVtLkRyYXdpbmcuUG9pbnQoNjAsIDIpKTsNCiAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZjEuUmVuZGVyKFwie1smKiZdfVZpdGU6IFwiICsgdml0ZS5Ub1N0cmluZygpLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0ZW0uRHJhd2luZy5Qe1smKiZdfW9pbnQoNDAwLCAyKSk7DQogICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKHBvc2l6aW9uZV97WyYqJl19Z2lvY2F0b3JlKQ0KICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDA6DQogICAgICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGdpb2MxLCBwb3NpemlvbmlfZ2lvY2F0b3JlW3Bvc2l6aW9uZV9ne1smKiZdfWlvY2F0b3JlXSk7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICAgICAgICAgIGN7WyYqJl19YXNlIDE6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZ2lvYzIsIHBvc2l6aW9uaV9naW9jYXtbJiomXX10b3JlW3Bvc2l6aW9uZV9naW9jYXRvcmVdKTsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCg0KICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgIGNhc2UgMjoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChnaW9jMyx7WyYqJl19IHBvc2l6aW9uaV9naW9jYXRvcmVbcG9zaXppb25lX2dpb2NhdG9yZV0pOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZXtbJiomXX1hazsNCiAgICAgICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KHdpZmUsIHBve1smKiZdfXNpemlvbmlfbW9nbGllW3Bvc2l6aW9uZV9tb2dsaWVdKTsNCg0KICAgICAgICAgICAgICAgICAgICBVcGRhdGVPZ2dldHRpKCk7DQp7WyYqJl19DQogICAgICAgICAgICAgICAgICAgIGZvcmVhY2ggKE9nZ2V0dG9DYWRlbnRlIG8xIGluIG9nZ2V0dGkpDQogICAgICAgICAgICAgIHtbJiomXX0gICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKG8xLmZpbmVzdHJhKQ0KICAgICAgICAgICAgICAgICAgICAge1smKiZdfSAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDA6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZ7WyYqJl19aWRlby5TY3JlZW4uQmxpdChvMS5DYW52YXMsIHBvc2l6aW9uaV9maW5lc3RyYTFbbzEucG9zaXppb25lXSk7DQogICAgICAgICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDE6DQogICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChvMS5DYW52YXMsIHBvc2l6aW9uaV9maW5lc3RyYTJbbzEucG97WyYqJl19c2l6aW9uZV0pOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgICAgIHtbJiomXX0gICAgY2FzZSAyOg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChvMS5DYW52YXMsIHBve1smKiZdfXNpemlvbmlfZmluZXN0cmEzW28xLnBvc2l6aW9uZV0pOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCiB7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgICAgICAgICBicmVha3tbJiomXX07DQoNCiAgICAgICAgICAgICAgICBjYXNlIFN0YXRvUGFydGl0YS5GaW5lOg0KICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3Jle1smKiZdfWVuLkJsaXQoYmFja2cpOw0KICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChmMS5SZW5kZXIoXCJQdW50ZWdnaW86e1smKiZdfSBcIiArIHB1bnRlZ2dpby5Ub1N0cmluZygpLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0ZW0uRHJhd2luZy5Qe1smKiZdfW9pbnQoNjAsIDIpKTsNCiAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZjEuUmVuZGVyKFwiUGFydGl0YSBUZXJte1smKiZdfWluYXRhLCBwcmVtZXJlIEludmlvLi4uXCIsIFN5c3RlbS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBve1smKiZdfWludCg2MCwgMTcpKTsNCiAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZjEuUmVuZGVyKFwiVml0ZTogMFwiLCBTeXtbJiomXX1zdGVtLkRyYXdpbmcuQ29sb3IuQmxhY2spLCBuZXcgU3lzdGVtLkRyYXdpbmcuUG9pbnQoNDAwLCAyKSk7DQogICAgICAgICAgICAge1smKiZdfSAgICAgICBicmVhazsNCiAgICAgICAgICAgIH0NCiAgICAgICAgfQ0KXCI7e1smKiZdfQ==[[/code]

Stavolta l'aggiunta è più succosa: c'è il metodo UpdateOggetti() che analizzeremo a breve, ed il ciclo foreach(OggettoCadente o1 in oggetti). Partiamo da questo ciclo, chiariamolo bene e successivamente spiegheremo nel dettaglio il metodo sopraccitato.

[code]]czo3MDg6XCINCmZvcmVhY2ggKE9nZ2V0dG9DYWRlbnRlIG8xIGluIG9nZ2V0dGkpDQogICAgICAgICAgICAgICAgICAgIHsNCiAgICB7WyYqJl19ICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKG8xLmZpbmVzdHJhKQ0KICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICBjYXNlIDA6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5Ce1smKiZdfWxpdChvMS5DYW52YXMsIHBvc2l6aW9uaV9maW5lc3RyYTFbbzEucG9zaXppb25lXSk7DQogICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDE6DQogICAgICAgICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChvMS5DYW52YXMsIHBvc2l6aW9uaV9maW5lc3RyYTJbbzEucG9zaXppb25lXSk7DQoge1smKiZdfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDI6DQp7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChvMS5DYW52YXMsIHBvc2l6aW9uaV9maW5lc3tbJiomXX10cmEzW28xLnBvc2l6aW9uZV0pOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAge1smKiZdfSAgICAgICAgICB9DQogICAgICAgICAgICAgICB9DQpcIjt7WyYqJl19[[/code]

Questo blocco di codice scorre tutti gli elementi della lista di oggetti che stiamo usando: in poche parole, contiene tutte le informazioni degli oggetti che stanno cadendo dalle finestre. Per ogni oggetto, quindi, eseguiamo due operazioni specifiche:

  • Controlliamo da quale finestra sta cadendo, tramite il blocco switch(o1.finestra);
  • Disegnamo su schermo l'oggetto tramite la funzione Blit, usando come surface quella dell'oggetto attualmente preso in considerazione e come coordinate quelle della finestra dalla quale sta cadendo.

Avendo un percorso suddiviso in tre “parti”, ogni oggetto verrà disegnato nella parte indicata dalla variabile “o1.posizione”. Spero di essere stato chiaro, non potevo semplificare di più :) Adesso ci rimane solo da analizzare la funzione UpdateOggetti(). Prestate la massima attenzione soprattutto in questa parte.

Innanzitutto, ecco il codice completo del metodo:

[code]]czoxNTIwOlwiDQp2b2lkIFVwZGF0ZU9nZ2V0dGkoKQ0KICAgICAgICB7DQogICAgICAgICAgICBmcmFtZXMrKzsNCg0KICAgICAgICB7WyYqJl19ICAgIGlmIChmcmFtZXMgPT0gZnJhbWVsaW1pdCkNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBzMS5QbGF5KCk7DQoNCntbJiomXX0gICAgICAgICAgICAgICAgaW50IGZpbmVzdHJhID0gcjEuTmV4dCgwLCAzKTsNCiAgICAgICAgICAgICAgICBpbnQgaW5kaWNlID0ge1smKiZdfS0xOw0KDQogICAgICAgICAgICAgICAgcG9zaXppb25lX21vZ2xpZSA9IGZpbmVzdHJhOw0KDQogICAgICAgICAgICAgICAgZm9yZWF7WyYqJl19Y2ggKE9nZ2V0dG9DYWRlbnRlIG8xIGluIG9nZ2V0dGkpDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICBzd3tbJiomXX1pdGNoIChvMS5wb3NpemlvbmUpDQogICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgMDoNe1smKiZdfQogICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDE6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbzEucG9zaXppb25lKyt7WyYqJl19Ow0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAyOg0KICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICAgICBpZiAobzEuZmluZXN0cmEgPT0gcG9zaXppb25lX2dpb2NhdG9yZSkNCiAgICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdW50ZWdnaW8gKz0gMTA7DQogICAgICB7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdml0ZSAtPSAxOw0KICAgICAgICAgICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgczIuUGxheSgpOw0KDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh2aXRlID09IDApIHN7WyYqJl19dGF0b19wID0gU3RhdG9QYXJ0aXRhLkZpbmU7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICAgICBpbmRpY2UgPSBvZ2dldHRpLkluZGV4T2YobzEpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFre1smKiZdfTsNCiAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgICAgIGlmKGluZGljZSAhPSB7WyYqJl19LTEpIG9nZ2V0dGkuUmVtb3ZlQXQoaW5kaWNlKTsNCiAgICAgICAgICAgICAgICBvZ2dldHRpLkFkZChuZXcgT2dnZXR0b0NhZGVudHtbJiomXX1lKHBvc2l6aW9uZV9tb2dsaWUsIHIxLk5leHQoMCwgNikpKTsNCg0KICAgICAgICAgICAgICAgIGZyYW1lcyA9IDA7DQoNCiAgICAge1smKiZdfSAgICAgICAgICAgaWYgKChwdW50ZWdnaW8gJSAzMDAgPT0gMCkmJihwdW50ZWdnaW8gIT0gMCkpDQogICAgICAgICAgICAgICAgew17WyYqJl19CiAgICAgICAgICAgICAgICAgICAgZnJhbWVsaW1pdCAtPSA1Ow0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgIHtbJiomXX0gICAgIH0NClwiO3tbJiomXX0=[[/code]

Non vi preoccupate se non capite, può essere comprensibile: spiegherò tutto riga per riga.

Innanzitutto vi rispiego brevemente il ragionamento che ho fatto e che ho applicato: ogni secondo devono succedere diverse cose. Dobbiamo far proseguire gli oggetti nella loro caduta, dobbiamo spostare la moglie in una finestra a caso delle tre e dobbiamo creare un nuovo oggetto.

Quindi la prima riflessione da fare sta qua: dato che la cosa dovrà avvenire ogni secondo e che il nostro programma va a 60 frames al secondo, se usiamo la variabile "frames" come contatore ad ogni esecuzione del metodo UpdateOggetti(), quando "frames" avrà un valore pari a 60 sapremo che sarà passato esattamente un secondo. Questo è il senso dell'istruzione

frames++;

deve essere una sorta di "timer" molto grezzo e adattato a questo singolo scopo. La variabile "framelimit" invece serve a specificare la lunghezza dell'intervallo. Inizialmente avremo bisogno di determinate azioni ogni secondo? Allora il "framelimit" sarà pari a 60. Per velocizzare questi eventi ed aumentare la difficoltà del prodotto sarà sufficiente diminuire di un tot la variabile "framelimit".

Niente di più. Tenete bene a mente questo concetto. Proseguiamo con il codice:

if (frames == framelimit)


serve a specificare che tutto il contenuto di questo blocco if sarà svolto solo al raggiungimento di 60 frames. Ovvero, una volta al secondo. Ma cosa abbiamo deciso di svolgere? Ecco riga per riga tutto spiegato.

s1.Play();

è il suono del bip classico di questi giochini. Non c'è molto da spiegare, il suo uso è puramente estetico e viene riprodotto ogni volta che le cose nel gioco cambiano...

int finestra = r1.Next(0, 3);

int indice = -1;

posizione_moglie = finestra;


alla variabile finestra associamo invece il valore generato dal Random r1, tra 0 e 3. L'indice è una variabile che scopriremo tra poco, mentre la terza istruzione, semplicemente assegna a posizione_moglie il valore generato dal random.

Rileggere il proprio codice fa sempre bene: in questo caso infatti potevo tranquillamente assegnare il valore del random a posizione_moglie, eliminando la necessità di "finestra".

Adesso che abbiamo calcolato la nuova posizione della moglie, preoccupiamoci di gestire tutti gli oggetti:

[code]]czo4ODg6XCINCmZvcmVhY2ggKE9nZ2V0dG9DYWRlbnRlIG8xIGluIG9nZ2V0dGkpDQogICAgICAgICAgICAgICAgew0KICAgICAgICB7WyYqJl19ICAgICAgICAgICAgc3dpdGNoIChvMS5wb3NpemlvbmUpDQogICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICBjYXNlIDA6DQogICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDE6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAge1smKiZdfW8xLnBvc2l6aW9uZSsrOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19IGNhc2UgMjoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAobzEuZmluZXN0cmEgPT0gcG9zaXppb25lX2dpb2NhdG9yZXtbJiomXX0pDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdW50ZWdnaW8ge1smKiZdfSs9IDEwOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlDQogICB7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdml0ZSAtPSAxOw0KICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICAgICAgICAgczIuUGxheSgpOw0KDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlme1smKiZdfSAodml0ZSA9PSAwKSBzdGF0b19wID0gU3RhdG9QYXJ0aXRhLkZpbmU7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICB7WyYqJl19ICAgICAgICAgICAgICAgICAgICAgICAgICBpbmRpY2UgPSBvZ2dldHRpLkluZGV4T2YobzEpOw0KICAgICAgICAgICAgICAgICAgIHtbJiomXX0gICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgIH0NClwiO3tbJiomXX0=[[/code]

Come punto di riferimento per capire cosa succede dobbiamo utilizzare la posizione attuale di ogni oggetto. Per questo, con il ciclo foreach, prendiamo in considerazione ogni oggetto attualmente presente in lista.

Nel caso la posizione dell'oggetto attualmente preso in considerazione sia 0 oppure 1, c'è ben poco da fare: lo si sposta in giù di uno. A questo serve l'istruzione o1.posizione++.

Nel caso invece la posizione sia 2, quindi ad un passo dal giocatore, abbiamo questo blocco if.

[code]]czoxOTc6XCINCmlmIChvMS5maW5lc3RyYSA9PSBwb3NpemlvbmVfZ2lvY2F0b3JlKQ0Kew0KICAgIHB1bnRlZ2dpbyArPSAxMDsNCn17WyYqJl19DQplbHNlDQp7DQogICAgdml0ZSAtPSAxOw0KICAgIHMyLlBsYXkoKTsNCg0KICAgIGlmICh2aXRlID09IDApIHN0YXRvX3AgPSBTdHtbJiomXX1hdG9QYXJ0aXRhLkZpbmU7DQp9DQppbmRpY2UgPSBvZ2dldHRpLkluZGV4T2YobzEpOw0KXCI7e1smKiZdfQ==[[/code]

Nel caso il giocatore si trovi sotto l'oggetto (la finestra appartenente all'oggetto può essere 0,1 o 2. idem per la posizione del giocatore) allora viene aumentato il punteggio di dieci punti. L'oggetto è stato preso con successo.

Nel caso contrario invece le cose cambiano. Togliamo una vita con vite -= 1; e riproduciamo s2, il suono che si utilizza nel caso si perda una vita. Infine facciamo un ultimo controllo: se le vite sono pari a zero allora il gioco finisce. Basta quindi dare un nuovo valore a stato_p: StatoPartita.Fine.

Infine ecco che introduciamo qualche informazione in più sulla variabile intera "indice". Normalmente non possiamo cancellare un oggetto dentro un ciclo che scorre l'oggetto stesso, non trovate? Parliamo della nostra lista di oggetti.

Ogni secondo infatti, un nuovo oggetto deve essere creato. Non possiamo accumulare oggetti all'infinito però! Per cui dobbiamo cancellare anche l'oggetto che è già caduto. A questo serve l'indice, che assume il valore di ritorno del metodo oggetti.IndexOf(o1), che indica la posizione dell'oggetto o1 nella lista.

Le cose tuttavia non finiscono qua:

[code]]czoyMDU6XCINCmlmKGluZGljZSAhPSAtMSkgb2dnZXR0aS5SZW1vdmVBdChpbmRpY2UpOw0Kb2dnZXR0aS5BZGQobmV3IE9nZ2V0dG97WyYqJl19Q2FkZW50ZShwb3NpemlvbmVfbW9nbGllLCByMS5OZXh0KDAsIDYpKSk7DQoNCmZyYW1lcyA9IDA7DQoNCmlmICgocHVudGVnZ2lvIHtbJiomXX0lIDMwMCA9PSAwKSYmKHB1bnRlZ2dpbyAhPSAwKSkNCnsNCiAgICBmcmFtZWxpbWl0IC09IDU7DQp9DQpcIjt7WyYqJl19[[/code]

Come prima cosa controlliamo che l'indice sia diverso da -1.

Domanda: perchè questo controllo?
Risposta: la variabile indice ha il compito che abbiamo spiegato sopra. Tuttavia, nei primi due secondi di gioco, gli oggetti sono in caduta e ce ne sono precisamente 2. Nessuno di questi, inoltre, è a contatto col giocatore. In questo modo, finchè non c'è un indice "valido" (ovvero diverso da zero) non verrà cancellato nessun'oggetto.

In questo modo appena avremo tre oggetti in campo (e la posizione del primo oggetto sarà 2, guardacaso proprio ad un passo dal giocatore) allora avremo un nuovo indice ed eseguiremo le operazioni nel blocco.

"oggetti.RemoveAt(indice)" è l'operazione che fa per noi. Dalla lista oggetti elimineremo quindi l'oggetto all'indice specificato nell'argomento.

Dopo aver rimosso l'oggetto, ne creiamo uno nuovo:

oggetti.Add(new OggettoCadente(posizione_moglie, r1.Next(0, 6)));

con questa sintassi abbiamo creato un nuovo oggetto cadente, passando come parametri del costruttore, nell'ordine, la posizione attuale della moglie e un nuovo valore generato a caso tra 1 e 6. Scrivo 1 anzichè zero perchè quando specifichiamo il margine inferiore è considerato escluso. L'istruzione new OggettoCadente(...) è a sua volta argomento di oggetti.Add(). In questo modo l'oggetto viene aggiunto alla lista.

Tramite "frames = 0;" il contatore del nostro grezzissimo timer viene azzerato e le operazioni ripartono da capo.

Infine, ecco le istruzioni che permettono di velocizzare il gioco man mano che si va avanti:

[code]]czo3NzpcIg0KaWYgKChwdW50ZWdnaW8gJSAzMDAgPT0gMCkmJihwdW50ZWdnaW8gIT0gMCkpDQp7DQogICAgZnJhbWVsaW1pdCAtPSB7WyYqJl19NTsNCn0NClwiO3tbJiomXX0=[[/code]

con queste poche righe di codice viene innanzitutto controllato che il punteggio attuale sia un multiplo di 300 (avevamo deciso che ogni 300 punti la velocità venisse aumentata: basterà controllare che il punteggio attuale sia un multiplo dello stesso numero specificato) e che ovviamente sia diverso da zero, in modo tale da non velocizzare tutto all'inizio. Se le condizioni sopraccitate vengono rispettate, allora il limite dettato da "framelimit" viene diminuito di cinque frames.

Per spiegarlo in parole povere, fin quando non faremo 300 punti avremo un framelimit di 60. Da 300 punti a 600 avremo un framelimit di 55, e così via, di trecento in trecento diminuendo di 5.

Ce l'abbiamo fatta! Dopo questa istruzione il nostro metodo è completato e non abbiamo bisogno di altre istruzioni aggiuntive... ora che la fase di disegno è completata al 100%, manca ancora da gestire l'input. Infine, metteremo tutto insieme.

Avanti!

  • Share/Bookmark

Disegnamo il Marito. – Creazione di un gioco con SDL.NET

Dato che abbiamo voluto utilizzare tre immagini differenti per il marito, il discorso sarà leggermente diverso ma comunque niente di complesso. Il nostro codice mostra nuove aggiunte:

[code]]czoyMjQzOlwiDQpwcml2YXRlIHZvaWQgVXBkYXRlUGFydGl0YSgpDQogICAgICAgIHsNCiAgICAgICAgICAgIHN3aXRjaCAoc3RhdG97WyYqJl19X3ApDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgY2FzZSBTdGF0b1BhcnRpdGEuUHJpbWE6DQogICAgICAgICAgICAgIHtbJiomXX0gICAgICBWaWRlby5TY3JlZW4uQmxpdChiYWNrZyk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJle1smKiZdfW5kZXIoXCJQdW50ZWdnaW86IDBcIiwgU3lzdGVtLkRyYXdpbmcuQ29sb3IuQmxhY2spLCBuZXcgU3lzdGVtLkRyYXdpbmcuUG9pbnQoNntbJiomXX0wLCAyKSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcihcIlByZW1pIEludmlvIHBlciBpbntbJiomXX1pemlhcmUgbGEgcGFydGl0YS4uLlwiLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0ZW0uRHJhd2luZy5Qb2ludHtbJiomXX0oNjAsIDE3KSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcihcIlZpdGU6IDBcIiwgU3lzdGV7WyYqJl19bS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50KDQwMCwgMikpOw0KICAgICAgICAgICAgICAgIHtbJiomXX0gICAgYnJlYWs7DQoNCiAgICAgICAgICAgICAgICBjYXNlIFN0YXRvUGFydGl0YS5JbkNvcnNvOg0KICAgICAgICAgICAgICAgICAge1smKiZdfSAgVmlkZW8uU2NyZWVuLkJsaXQoYmFja2cpOw0KICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQmxpdChmMS5SZW5kZXJ7WyYqJl19KFwiUHVudGVnZ2lvOiBcIiArIHB1bnRlZ2dpby5Ub1N0cmluZygpLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0e1smKiZdfWVtLkRyYXdpbmcuUG9pbnQoNjAsIDIpKTsNCiAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZjEuUmVuZGVyKFwie1smKiZdfVZpdGU6IFwiICsgdml0ZS5Ub1N0cmluZygpLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN0ZW0uRHJhd2luZy5Qe1smKiZdfW9pbnQoNDAwLCAyKSk7DQoNCgkgICAgIHN3aXRjaCAocG9zaXppb25lX2dpb2NhdG9yZSkNCiAgICAgICAgICAgICAgICAgICAgew17WyYqJl19CiAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgMDoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBWaWRlby5TY3JlZW4uQntbJiomXX1saXQoZ2lvYzEsIHBvc2l6aW9uaV9naW9jYXRvcmVbcG9zaXppb25lX2dpb2NhdG9yZV0pOw0KICAgICAgICAgICAgICAgICAgICAge1smKiZdfSAgICAgICBicmVhazsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAxOg0KICAgICAgICAgICAgICAgICAgICAgICAgICB7WyYqJl19ICBWaWRlby5TY3JlZW4uQmxpdChnaW9jMiwgcG9zaXppb25pX2dpb2NhdG9yZVtwb3NpemlvbmVfZ2lvY2F0b3JlXSk7DQogICAgIHtbJiomXX0gICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDI6DQogICAgICAgICAge1smKiZdfSAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGdpb2MzLCBwb3NpemlvbmlfZ2lvY2F0b3JlW3Bvc2l6aW9uZV9naW97WyYqJl19Y2F0b3JlXSk7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICAgICAgICAgIH0NCg0KCSAgIHtbJiomXX0gICBWaWRlby5TY3JlZW4uQmxpdCh3aWZlLCBwb3NpemlvbmlfbW9nbGllW3Bvc2l6aW9uZV9tb2dsaWVdKTsNCiAgICAgICAgICAge1smKiZdfSAgICAgICAgIGJyZWFrOw0KDQogICAgICAgICAgICAgICAgY2FzZSBTdGF0b1BhcnRpdGEuRmluZToNCiAgICAgICAgICAgICAgICB7WyYqJl19ICAgIFZpZGVvLlNjcmVlbi5CbGl0KGJhY2tnKTsNCiAgICAgICAgICAgICAgICAgICAgVmlkZW8uU2NyZWVuLkJsaXQoZjEuUmVuZHtbJiomXX1lcihcIlB1bnRlZ2dpbzogXCIgKyBwdW50ZWdnaW8uVG9TdHJpbmcoKSwgU3lzdGVtLkRyYXdpbmcuQ29sb3IuQmxhY2spLCBuZXcgU3l7WyYqJl19c3RlbS5EcmF3aW5nLlBvaW50KDYwLCAyKSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcntbJiomXX0oXCJQYXJ0aXRhIFRlcm1pbmF0YSwgcHJlbWVyZSBJbnZpby4uLlwiLCBTeXN0ZW0uRHJhd2luZy5Db2xvci5CbGFjayksIG5ldyBTeXN7WyYqJl19dGVtLkRyYXdpbmcuUG9pbnQoNjAsIDE3KSk7DQogICAgICAgICAgICAgICAgICAgIFZpZGVvLlNjcmVlbi5CbGl0KGYxLlJlbmRlcntbJiomXX0oXCJWaXRlOiAwXCIsIFN5c3RlbS5EcmF3aW5nLkNvbG9yLkJsYWNrKSwgbmV3IFN5c3RlbS5EcmF3aW5nLlBvaW50KDQwMCwgMikpOw17WyYqJl19CiAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICB9DQp9DQpcIjt7WyYqJl19[[/code]

Abbiamo inserito un altro switch, che stavolta serve a controllare sotto quale finestra si trova il giocatore: il metodo utilizzato ed il ragionamento alla base, però, sono gli stessi che abbiamo adottato per la moglie. Così, dopo aver controllato la posizione del giocatore tramite lo switch, ecco che utilizziamo l'istruzione

Video.Screen.Blit(gioc1, posizioni_giocatore[posizione_giocatore]);

oppure

Video.Screen.Blit(gioc2, posizioni_giocatore[posizione_giocatore]);

oppure

Video.Screen.Blit(gioc3, posizioni_giocatore[posizione_giocatore]);


In poche parole, cambia solo il nome della variabile. La cosa si complica un po' di più, invece, per ciò che concerne gli oggetti. Andiamo avanti per scoprire come.

  • Share/Bookmark