VS85 Team's profileXNALearnersBlogListsGuestbook Tools Help

Blog


    6/23/2008

    Programmazione funzionale in .Net 3.5: tutorial di base su F# (parte II)

    Nota: per poter eseguire questo codice e' necessario Visual Studio 2008 con installato il Microsoft F# Research Compiler (noi abbiamo usato la versione 1.9.1.3).

    Prima di cominciare con il tutorial vero e proprio, e' importante specificare perche' questo linguaggio puo' essere utile. Elenco i vantaggi principali senza entrare nel dettaglio:

    1. questo paradigma e' perfetto per il multithreading, che entro pochi anni diventera' semplicemente irrinunciabile (quattro core? otto core? anche di piu'?)
    2. F# e' strettamente legato a C# e al mondo .Net in generale; si possono scrivere librerie in F# che poi possono essere lette e usate da C# nativamente, nonche' viceversa
    3. F# puo' usare qualsiasi libreria .Net (XNA :) :) )
    4. F# genera codice veloce
    5. F# scrive codice logico/matematico semplicemente fantastico

    In questo tutorial vedremo come si manipola la struttura dati che la fa da padrone nel mondo ricorsivo di F#: la lista!

    Dichiariamo tre liste:

    let l1 = [1;2;3;4]
    let l2 = 1::2::3::4::[]
    let l3 = l1@l2

    l1 contiene i primi 4 interi, l2 anche ma e' costruita effettuando una serie di operazioni di tipo "push" a partire dalla lista vuota []. Infine l3 e' la concatenazione delle liste l1 e l2.

    Come primo esempio di una funzione che manipola liste vediamo come sommare tutti gli elementi di una lista con una funzione ricorsiva:

    let rec sum l =
        match l with    
            [] -> 0 |
            x::xs -> x + (sum xs)
    

    in cui vediamo come il pattern matching possa essere usato non solo per ispezionare il valore delle variabili, ma anche la struttura dei loro componenti (ad esempio qui distinguiamo tra la lista vuota [], la somma dei cui elementi e' 0, e la lista con almeno un elemento x che va sommato alla somma del resto della lista).

    Quindi vediamo in opera alcune cosiddette "funzioni di ordine superiore", ossia funzioni che prendano in input anche altre funzioni per applicarle secondo pattern gia' noti. Una delle piu' semplici e' la funzione di riduzione, ossia:

    let rec reduce f l =
        match l with
            [] -> failwith "la lista non puo' essere vuota" |
            [x] -> x |
            x::xs -> 
                let acc = reduce f xs
                in f acc x
    

    in cui la funzione f viene applicata alla riduzione della coda e all'elemento corrente. Se f fosse la funzione che ritorna il massimo tra due valori:

    (fun a -> fun b -> if a > b then a else b)

    allora reduce ritorna il massimo elemento della lista. Se invece come f usiamo la funzione somma:

    (fun a -> fun b -> a + b)

    allora reduce ritorna la somma di tutti gli elementi della lista. Esistono moltissimi altri patterns tipici di iterazione di liste e di collezioni, esattamente gli stessi che si trovano in LINQ (ed ecco perche' abbiamo presentato LINQ come una estensione funzionale di C#, piuttosto che presentarlo come fanno molti come libreria per accesso ai databases).

    Qui sotto il webcast tenuto da me (Giuseppe Maggiore) e da Giulia Costantini in cui produciamo questo codice (e molto altro) spiegando in dettaglio cosa significa e mostrando il risultato della sua esecuzione (download ad alta risoluzione qui):

    [il webcast in streaming richiede silverlight e un attimo di pazienza per il caricamento; in compenso la visione puo' essere effettuata in full-screen a buona risoluzione!]

     

    6/22/2008

    Programmazione funzionale in .Net 3.5: tutorial di base su F# (parte I)

    Nota: per poter eseguire questo codice e' necessario Visual Studio 2008 con installato il Microsoft F# Research Compiler (noi abbiamo usato la versione 1.9.1.3).

    Prima di cominciare con il tutorial vero e proprio, e' importante specificare perche' questo linguaggio puo' essere utile. Elenco i vantaggi principali senza entrare nel dettaglio:

    1. questo paradigma e' perfetto per il multithreading, che entro pochi anni diventera' semplicemente irrinunciabile (quattro core? otto core? anche di piu'?)
    2. F# e' strettamente legato a C# e al mondo .Net in generale; si possono scrivere librerie in F# che poi possono essere lette e usate da C# nativamente, nonche' viceversa
    3. F# puo' usare qualsiasi libreria .Net (XNA :) :) )
    4. F# genera codice veloce
    5. F# scrive codice logico/matematico semplicemente fantastico

    In questo tutorial vedremo come dichiarare funzioni e costanti in F#. Cominciamo con il dichiarare alcune variabili:

    let a = "una stringa"
    let b = 10
    let c = 10.0f
    let d = 'a'
    

    con cui dichiariamo una serie di variabili immutabili (quasi delle costanti), lasciando che sia il compilatore a dedurne il tipo. Usiamo la console F# Interactive (integrata in Visual Studio) per farci compilare il codice al volo ed eseguirne pezzi. Vediamo cosa dice la console FSI quando le diamo in pasto il codice appena visto:

    val a : string
    val b : int
    val c : float32
    val d : char
    

    a e' una stringa, b e' un intero, c un float e d un char.

    Dichiariamo adesso una funzione che somma tre interi tra loro:

    let somma x y z = x + y + z
    

    Rispetto al modo "classico" di scrivere una funzione di somma notiamo soprattutto che non esiste la keyword "return" (e' tutto implicito) e non serve specificare il tipo dei valori da sommare tra loro o il tipo restituito. Rispetto all'equivalente C#, non c'e' paragone quanto a semplicita' e velocita' di scrittura:

    int somma(int x, int y, int z)
    {
        return x + y + z;
    }
    

    vediamo ora un semplice esempio di ricorsione (si noti come non scriviamo solo let, bensi let rec per indicare al compilatore che il nome della funzione verra' usato all'interno della funzione stessa):

    let rec fact n =
        match n with
            0 -> 1 |
            1 -> 1 |
            i -> 
                if i < 0 then failwith "fattoriale: solo numeri positivi"
                else i * (fact (i-1))
    

    in questo snippet vediamo come stiamo facendo cosiddetto "pattern matching" sulla variabile presa in input dalla funzione fattoriale (match n with). Il pattern matching permette di definire una serie di coppie <valore variabile, valore da ritornare> tale che viene ritornato il primo "valore da ritornare" il cui "valore associato" e' pari al valore della variabile. Sembra magico, ma non lo e' :D (ulteriori spiegazioni si trovano nel webcast!).

    Qui sotto trovate il webcast (mio -Giuseppe Maggiore- e di Giulia Costantini) in cui viene estensivamente spiegato e mostrato in azione il codice di questo post (download ad alta risoluzione qui):

    [il webcast in streaming richiede silverlight e un attimo di pazienza per il caricamento; in compenso la visione puo' essere effettuata in full-screen a buona risoluzione!]

     

    6/21/2008

    Programmazione funzionale in .Net 3.5: tutorial di base su LINQ

    In questo webcast vediamo come manipolare collezioni di oggetti in C# tramite LINQ. LINQ e' un modo nuovo per accedere ai dati di liste, array, dizionari e enumerabili in generale con efficienza sia del codice risultante che in termini di tempo di programmazione!

    Usiamo Visual Studio 2008 e C# 3.0, appoggiandoci al framework .Net 3.5.

    Come prima cosa inizializziamo una lista di interi usando la sintassi di inizializzazione delle collezioni:

    List<int> l = new List<int>()
    {
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10
    };
    

    Quindi vediamo come ottenere un risultato molto simile ma senza

    1. dover scrivere due volte List<int>
    2. dover scrivere a mano i valori della lista
    var l = Enumerable
        .Range(0, 10)
        .ToList();
    

    Questa in effetti e' la nostra prima query LINQ, in cui tramite Enumerable.Range(0,10) otteniamo un IEnumerable<int> che quando iterato restituisce gli interi nell'intervallo [0..10]. Da notare che in LINQ la maggior parte delle queries non e' materializzata finche' non la leggiamo, stampiamo o trasformiamo in lista. Quindi Enumerable.Range(0,10) non alloca spazio per undici interi, ma memorizza solo minimo, massimo e valore corrente durante le iterazioni. Invocando ToList() su un enumerabile lo iteriamo e copiamo i suoi elementi su una lista, di fatto

    1. concretizzando la query in memoria
    2. ritornando una lista

    il tipo della variabile l viene dedotto essere List<int> dal compilatore che analizza il valore assegnato alla variabile al momento della compilazione. Si noti che se fosse stato

    var l = Enumerable
        .Range(0, 10);
    

    la variabile l avrebbe avuto tipo IEnumerable<int> e non avrebbe occupato la stessa quantita' di memoria della versione concretizzata su lista.

    A questo punto mostriamo vari modi di stampare la lista l su console. Il primo, piu' banale, e' naturalmente quello di scrivere

    foreach (var i in l)
    {
        System.Console.WriteLine(i);
    }

    dove notiamo l'uso della keyword var per dichiarare la variabile i senza dover specificare che essa e' un intero: d'altra parte il compilatore puo' chiaramente capirlo da solo!

    Un metodo estremamente piu' elegante sarebbe quello di invocare uno dei nuovi metodi disponibili su una lista, come ad esempio il metodo ForEach:

    l.ForEach(
        delegate(int i)
        {
            System.Console.WriteLine(i);
        }
    );

    questo metodo prende come unico parametro una funzione che prende l'elemento corrente della lista, ci fa qualcosa e infine ritorna void. La sintassi della dichiarazione del delegate pero' e' un po' ridondante. In primo luogo il sistema sa che in quel punto del codice passeremo una funzione che prende come parametro un intero, quindi invece di scrivere delegate(int i) basterebbe dire solo i. Poi le parentesi graffe sono inutili, dato che si parla di una sola riga di codice! Infatti, grazie alle nuove espressioni lambda presenti in C# 3.0, possiamo scrivere una versione molto piu' raffinata dello stesso codice:

    l.ForEach(i => System.Console.WriteLine(i));

    dove si intuisce che il delegate passato alla ForEach prende una variabile i, e la trasforma eseguendo una stampa su console. L'idea fondamentale e' che passiamo alla ForEach il lavoro da fare per ogni elemento, e la ForEach invochera' questa nostra funzione su un valore dopo l'altro della nostra lista.

    Passiamo a cose un po' piu' corpose. Ipotizziamo di voler trasformare la nostra lista l in una nuova lista l1 contenente tutti i valori di l trasformati rispetto a una funzione matematica qualsiasi:

    var l1 = l
        .Select(i => - 3 * i * i * i + 6 * i * i + 3)
        .ToList();
    

    vediamo qui in azione la funzione Select, che prende un enumerabile (in questo caso l) e ad ogni elemento di l applica una funzione che lo trasforma, restituendo l'enumerabile con gli elementi trasformati. Si noti che anche qui finche' non invochiamo ToList la collezione risultante resta non eseguita: non occupa memoria ma ogni volta che accederemo ai suoi elementi verra' effettuato un calcolo!

    Un'altra importantissima aggiunta di LINQ al C# 3.0 sono i cosiddetti tipi anonimi. Scrivendo

    var i = new { Item = 0, ItemString = "pippo" };

    dichiariamo la variabile i come avente tipo anonimo. Se infatti poggiamo il mouse sopra la scritta var, Visual Studio fa comparire il tipo della variabile, ossia un tipo 'a (si legge alfa) avente due proprieta' pubbliche, int Item e string ItemString:

    image

    Mettere insieme Select e tipi anonimi produce collezioni complesse e permette di fare cose che prima d'ora richiedevano pagine di codice e di dichiararsi strutture dati ausiliarie solo per contenere piccolissimi tipi di dato temporanei.

    Vediamo ora una ulteriore feature interessantissima che possiamo combinare con LINQ, ossia gli enumerabili infiniti. Creiamo un oggetto di tipo IEnumerable<int> che contenga tutti gli interi:

    IEnumerable<int> NumeriNaturali()
    {
        int n = 0;
        while (true)
        {
            yield return n;
            n++;
        }
    }
    

    naturalmente questo codice non e' il prodotto di una allucinazione folle, ma effettivamente funziona. Certo, se provassimo a materializzare questo enumerable scrivendo:

    var N = NumeriNaturali()
        .ToList();
    

    il programma andrebbe in loop infinito cercando di salvare sulla lista N tutti i numeri prodotti dall'enumerabile ritornato dalla funzione. Ma se ad esempio scrivessimo la seguente query LINQ:

    var l4 = NumeriNaturali()
        .Skip(10)
        .Take(200)
        .ToList();
    

    in questo modo in l4 sono contenuti solo un numero finito di elementi e possiamo tranquillamente convertire l'enumerabile in una lista. Grazie al fatto che in LINQ le queries non vengono eseguite fino al momento in cui cio' e' strettamente indispensabile, e' possibile manipolare oggetti infiniti (generatore di numeri casuali, di perlin noise, di checkpoint di un percorso, di nemici da uno spawning point...) senza ricorrere per questo a memoria infinita (che come ben si sa e' molto costosa :) ).

    Qui sotto ecco un embedding del tutorial in cui mostriamo LINQ in azione in diretta. Il webcast (qui ad alta risoluzione) e' tenuto da me (Giuseppe Maggiore) e da Giulia Costantini in "doppio":

    [il webcast in streaming richiede silverlight e un attimo di pazienza per il caricamento; in compenso la visione puo' essere effettuata in full-screen a buona risoluzione!]

     

    5/28/2008

    XNA@DSI Corso e Contest

     

    image Nel secondo semestre dell'a.a. 2007-2008 il Dipartimento di Informatica dell'Universita' Ca'Foscari di image Venezia ha ospitato una iniziativa decisamente nuova e particolare: una serie di seminari (strutturatii come un vero e proprio corso) dedicati alla tecnologia XNA, tenuti da uno studente della specialistica del corso di Informatica e Microsoft Student Partner (MSP).

    I seminari, in totale 7, e della durata di 2 ore ciascuno consistevano in una parte teorica (accompagnata da slides) e una parte applicata (in cui scrivendo codice in tempo reale venivano applicati i concetti della parte teorica). Al termine del corso gli studenti sono stati messi in grado di scrivere videogiochi di media complessita'.

    Gli argomenti trattati sono: image

    • Geometria in XNA
    • Algoritmi di illuminazione
    • Texturing
    • La classe Microsoft.XNA.Framework.Model
    • Esempio di semplice gioco

       

    image Terminate le lezioni, grazie alla sponsorizzazione di Microsoft, e' stato organizzato un concorso per stimolare gli studenti a mettere in pratica cio' che avevano imparato. I giochi inviati sono stati di qualita' stupefacente, ed il vincitore (scegliere il quale e' stata durissimo J) si e' portato a casa una XBox360 nuova fiammante. Il secondo e il terzo hanno realizzato lo stesso punteggio e sono stati premiati entrambi come secondi classificati, ricevendo ciascuno due giochi della serie Games For Windows. L'anno prossimo verra' ripetuto il corso rivolgendosi nuovamente agli studenti della laurea triennale in Informatica, sempre presso il Dipartimento di Informatica dell'Universita' Ca' Foscari di Venezia.

     

     

     

    Qui sotto potete vedere in streaming la presentazione del corso su XNA e le interviste ai vincitori (dal quarto al primo); lo streaming richiede Silverlight. In alternativa potete scaricare il video completo (attenzione pero', sono circa 40MB!).

    4/13/2008

    Audio

    Eseguire audio in un progetto XNA

    By Giulia Costantini

    [English Version] 

     

    Introduzione

    In questo tutorial impareremo ad caricare ed eseguire suoni nelle nostre applicazioni XNA. Lo strumento che useremo e' il programma XACT (Cross-platform Audio Creation Tool), uno strumento multipiattaforma (Windows + Xbox360) per la creazione dell'audio. Questo programma viene automaticamente installato insieme ad XNA, quindi se avete gia' installato XNA sul vostro PC avete tutto quello che serve per seguire questo tutorial. Trovate XACT tramite "tutti i programmi"à "Microsoft XNA Game Studio 2.0" à "tools" à "Microsoft Cross-Platform Audio Creation Tool". Per ascoltare suoni all'interno di XACT (invece di dovere aprire un player apposito) potete usare il seguente programma (anch'esso installato insieme ad XNA): "tutti i programmi"à "Microsoft XNA Game Studio 2.0" à "tools" à"XACT Auditioning Utility".

     

    Concetti di base di XACT

    Cominciamo facendo conoscenza con la terminologia usata da XACT e XNA per quanto riguarda i suoni.

    Un wave e' un buffer di dati audio, ossia un file che contiene un suono. I formati audio supportati da XACT sono wav, aiff e xma.

    Un sound invece e' insieme di tracce eseguite contemporaneamente; per ciascuna traccia si possono impostare diverse proprieta' tra cui il wave che quella traccia esegue, il volume, ecc...

    Il termine cue si riferisce al suono logico, ossia cio' che viene effettivamente usato nel codice per riprodurre un suono. Ogni cue puo' contenere uno o piu' suoni; nel caso in cui essa contenga piu' suoni, quando viene eseguita viene scelto uno dei suoni che essa contiene. Tramite XACT possiamo decidere come viene effettuata la scelta del suono da eseguire: ad esempio possiamo impostare una scelta casuale, oppure che i suoni vengano riprodotti nell'ordine in cui li abbiamo inseriti, e cosi' via.

    Una wave bank e' una collezione di waves, ovvero una collezione di file audio. Il formato di file di una wave bank e' .xwb. Anche per ogni wave bank possiamo impostare varie proprieta', come ad esempio il metodo di compressione per i file audio, se la riproduzione viene effettuata in streaming o se il file viene tenuto tutto in memoria, ecc...

    Una sound bank invece e' una collezione di sounds e cues. Il formato di file di una sound bank e' .xsb.

    Come vedremo tra poco, un progetto XACT puo' essere visto come l'insieme di tre files:

    • un file .xgs che contiene impostazioni globali;
    • un file .xwb, cioe' la wave bank che contiene tutti i nostri file wave;
    • un file .xsb, cioe' la sound bank che contiene tutti i sounds e le cues.

     

    Creare un progetto XACT

    Prima di tutto creiamo un progetto XNA 2.0 tramite Visual Studio ("file" à "new" à "project" à "windows game 2.0") e aggiungiamo al progetto Content (tasto destro su Content à "add" à "new folder") una cartella chiamata "Sound".

     

    A questo punto siamo pronti per creare il nostro progetto XACT. Dopo avere aperto il programma XACT, clicchiamo su "file" à "new project" e inseriamo come path il percorso del nostro progetto XNA appena creato, precisamente "path_progetto_xna\Content\sound". Chiamiamo il nostro progetto XACT myFirstXACTProject.

     

    Ora dobbiamo creare una nuova wave bank e una nuova sound bank. Per fare cio' basta cliccare col tasto destro su "Wave Banks" e su "Sound Banks" e selezionare rispettivamente "New Wave Bank" e "New Sound Bank".

    .

     

    A questo punto nella parte destra dello schermo sono comparse le nostre banks (che di default si chiamano "Wave Bank" e "Sound Bank" ma che possiamo tranquillamente chiamare come vogliamo, semplicemente cliccando F2 sul nome e inserendo il nuovo nome seguito dalla pressione di Invio).

    Aggiungere suoni (file audio) alla wave bank non potrebbe essere piu' facile: dobbiamo selezionare i file audio da inserire nella wave bank e trascinarli sopra alla wave bank stessa. Vi ricordo che i formati supportati da XACT sono wav, aiff e xma.

    Abbiamo importato nella nostra wave bank due files, chiamati "industry.wav" e "menu.wav". Il risultato e' il seguente:

    Abbiamo visto che una sound bank contiene una collezione di sounds e di cues; in effetti se osserviamo la finestra della nostra sound bank possiamo osservare che e' divisa in due zone, una riservata ai sound e una riservata alle cues:

    Per creare un nuovo suono e' sufficiente trascinare un file della wave bank nella zona dedicata ai sounds della sound bank. Trascinando il wave "industry" otteniamo:

    Cosi' facendo abbiamo creato il suono "industry" (ma possiamo cambiargli nome) che consiste di una sola traccia, ovvero il wave "industry.wav". Possiamo aggiungere altre tracce al suono (trascinando un altro wave sul sound industry), ma ricordiamo che le tracce di un suono vengono riprodotte in contemporanea.

    Creiamo un altro suono, collegato al file wav "menu", trascinando il wave "menu" dalla wave bank alla sound bank:

    A questo punto abbiamo due suoni, ciascuno collegato ad un solo wave. Per creare una cue e' sufficiente trascinare un sound nella zona delle cues. Trascinando il sound "industry" nelle cues otteniamo:

    Abbiamo creato una cue chiamata "industry" (anche qui ovviamente possiamo cambiargli nome) collegata al suono "industry".

    Ricordiamoci alla fine di salvare il progetto XACT!

     

    Importare il progetto XACT in XNA

    Torniamo finalmente a Visual Studio, al nostro progetto XNA. Dobbiamo includere nel progetto Content il file del progetto XACT su cui abbiamo appena lavorato. Per fare cio', clicchiamo sul progetto Content, poi clicchiamo su "Show all files" per poter vedere il file di progetto XACT

    ,

    poi clicchiamo col tasto destro sul file .xap ("myFirstXACTProject.xap") appena comparso

    e infine selezioniamo "Include in Project". Dall'output della compilazione del nostro progetto

    vediamo che la Content Pipeline di XNA (che vedrete in un tutorial successivo) ci ha prodotto 3 files, che sono quelli di cui vi avevo parlato all'inizio del tutorial: un file di configurazione generale riguardante l'audio (.xgs), un file per la wave bank (.xwb) e uno per la sound bank (.xsb).

     

    Eseguire suoni nell'applicazione

    E' giunta finalmente l'ora di scrivere del codice J Cominciamo creando tre oggetti:

    AudioEngine engine;

    WaveBank waveBank;

    SoundBank soundBank;

     

    E inizializziamoli nella funzione "LoadContent":

    engine = new AudioEngine("Content\\sound\\myFirstXACTProject.xgs");

    waveBank = new WaveBank(engine, "Content\\sound\\Wave Bank.xwb");

    soundBank = new SoundBank(engine, "Content\\sound\\Sound Bank.xsb");

     

    Abbiamo dunque caricato i 3 files che caratterizzano il nostro progetto XACT, ossia il file di impostazioni .xgs (che va memorizzato nell'AudioEngine), la wave bank .xwb (che viene memorizzata nell'oggetto di tipo WaveBank) e la sound bank .xsb (che viene memorizzata nell'oggetto di tipo SoundBank). Alla waveBank e alla soundBank va anche passato l'engine di riferimento.

    A questo punto abbiamo due possibilita' per riprodurre un suono. La prima possibilita' e':

    soundBank.PlayCue("industry");

     

    ovvero utilizzare la sound bank per riprodurre direttamente una cue, identificata per nome (nel nostro caso abbiamo scelto di riprodurre la cue "industry"). Se inseriamo questo codice nella funzione LoadContent (subito dopo la creazione degli oggetti engine, waveBank e soundBank) otterremo che all'avvio dell'applicazione XNA verra' riprodotto il suono collegato alla cue "industry".

    La seconda possibilita' e' definire un altro oggetto

    Cue cue;

     

    di tipo Cue, e nella LoadContent scrivere:

     

    cue = soundBank.GetCue("industry");

    cue.Play();

     

    Cosa abbiamo fatto? Ci siamo fatti dare la cue di nome "industry" dalla sound bank, l'abbiamo salvata in un oggetto di tipo Cue e l'abbiamo riprodotta. L'effetto e' esattamente lo stesso visto prima, ossia all'avvio dell'applicazione viene riprodotto il suono collegato alla cue "industry").

    Se pero' volessimo che il suono venisse riprodotto solo quando viene premuto un certo tasto? Bastera' scrivere il seguente codice nella funzione "Update":

    if (Keyboard.GetState().IsKeyDown(Keys.Space))

    {

    cue = soundBank.GetCue("industry");

        cue.Play();

    }

    In questo modo quando premeremo il tasto spazio, verra' riprodotto il suono desiderato.

    C'e' pero' un problema: la funzione Update, come sappiamo, viene chiamata molte volte (anche centinaia) al secondo. Quindi durante la nostra pressione del tasto spazio la funzione Update viene chiamata piu' volte e ogni volta essa comincera' la riproduzione della cue "industry". Il risultato e' che il suono viene riprodotto piu' volte, ciascuna a distanza di millesimi di secondo dalla precedente. Inoltre se premiamo spazio una seconda volta (dopo qualche secondo), mentre il suono sta ancora venendo riprodotto, partira' una seconda istanza del suono che si sovrapporra' alla prima. Come possiamo evitare queste spiacevoli situazioni? Grazie alla proprieta' isPlaying che ci fornisce l'oggetto di tipo Cue! Tale proprieta' ci dice se la cue sta venendo riprodotta; in questo modo possiamo evitare di cominciare un'altra riproduzione del suono se ce n'e' gia' una attiva. In codice:

    if (cue != null && cue.IsPlaying == false)

    cue = null;

     

    if (Keyboard.GetState().IsKeyDown(Keys.Space) && cue == null)

    {

    cue = soundBank.GetCue("industry");

    cue.Play();

    }

    Nel codice qui sopra abbiamo imposto che la cue sia null quando non sta venendo eseguita. Infatti se la cue non e' null e sta eseguendo un suono e questo suono finisce, la sua proprieta' isPlaying diventera' falsa e quindi cue verra' impostata a null. La pressione del tasto spazio viene considerata solamente se cue e' null, ovvero se la cue non sta eseguendo un suono.

    Abbiamo dunque risolto il problema citato prima: la pressione prolungata del tasto spazio non provoca l'esecuzione dello stesso suono un sacco di volte uno sopra l'altro; inoltre pressioni di spazio effettuate durante l'esecuzione del suono non vengono considerate. Ricapitolando: la cue "industry" verra' eseguita solo se viene premuto il tasto spazio e se non e' gia' in esecuzione (che e' esattamente quello che volevamo ottenere)!

    Nota da tenere ben presente: la cue che otteniamo tramite il metodo GetCue puo' venire riprodotta una sola volta, dopodiche' la cue e' stata "consumata" e non e' piu' valida. E' per questo motivo che prima di ogni "cue.Play()" dobbiamo ri-estrarre la cue dalla sound bank con il metodo GetCue.

     

    Aspetti leggermente piu' avanzati di XACT

    L'utilizzo del programma XACT rende il tecnico dell'audio completamente indipendente dai programmatori. Come? Abbiamo visto che nel codice ci riferiamo alle cue per nome (ad esempio noi eseguivamo la cue "industry"). Dal momento in cui il tecnico del suono si limita a modificare solamente i wave associati alla cue, e non il nome della cue stessa, puo' lavorare per conto suo e il nostro codice continuera' a funzionare. Quindi il tecnico puo' apportare migliorie e modifiche alle cue tramite XACT, e con una semplice ricompilazione del nostro progetto XNA (durante cui vengono ricreati, se necessario, i 3 file xgs, xwb e xsb) avremo i suoni aggiornati senza dover cambiare una riga di codice.

    Ma cosa puo' fare esattamente un tecnico del suono con XACT?

    Abbiamo detto che ad un sound si possono associare piu' tracce (trascinando il nome di ciascun wave sul nome del sound): queste tracce verranno pero' riprodotte contemporaneamente. Si puo' anche cancellare una traccia da un sound cliccando sulla traccia e poi premendo "canc". Nell'immagine sotto vedete un suono ("industry") a cui sono associate due tracce (il wave "industry" e il wave "menu"):

    Nella sound bank possono stare piu' suoni, ciascuno associato a particolari tracce:

    A ciascuna cue possono venire associati piu' suoni, semplicemente trascinandoli sul nome della cue. Nell'immagine qui sotto vedete che alla cue chiamata "cue1" ho associato tutti e due i suoni presenti nella sound bank, ossia "industry" e "menu".

    Se in una cue si sono piu' suoni, possiamo impostare il metodo con cui viene scelto quale dei suoni riprodurre quando viene eseguita la cue.

    Nell'immagine vedete il menu a tendina con cui si imposta tale proprieta' e vedete che le possibilita' non sono poche.

     

    Trovate qui il codice sorgente e qui il webcast ad alta risoluzione in cui Alessandro Piva spiega XACT e la scrittura del codice in diretta. Una versione embedded a risoluzione piu' bassa e' qui sotto:

     

    3/20/2008

    Testo e primitive 2D in XNA

    English Version

     

    by Giuseppe Maggiore

     

    Introduzione

    XNA supporta in modo molto ordinato il disegno di oggetti bidimensionali su schermo. Tipicamente queste operazioni di disegno verranno eseguite durante la chiamata a Game.Draw, tramite un oggetto helper, lo sprite batch:

    SpriteBatch spriteBatch; 
    

    Lo sprite batch, come suggerisce il nome, e’ un sistema di raggruppamento ottimizzato di chiamate di disegno di sprites. Gli sprites sono piccoli rettangoli che vogliamo prendere da una texture e mostrare su schermo. Tale sistema richiede che le chiamate di disegno avvengano in mezzo tra apertura (Begin()) e chiusura (End()), in modo che lo sprite batch le possa ottimizzare o ordinare come preferisce:

    spriteBatch.Begin();
    
    /*...*/
    
    spriteBatch.End();
    

    La funzione Begin prende opzionalmente tre parametri:

    · SpriteBlendMode che specifica se vogliamo usare alpha blending, alpha blending additivo o nessun blending

    · SpriteSortMode che indica in che ordine lo sprite batch deve disegnare: per z crescente, per z decrescente, immediato, deferito alla chiamata di End()

    · SaveStateMode per far si che la chiamata a End() ripristini tutte le flags della scheda video ai valori che avevano prima della chiamata a Begin(): utile per rendere l’uso dello SpriteBatch del tutto trasparente agli altri moduli di disegno

    SpriteBatch.Draw

    Quando arriva il momento di disegnare ecco che entra in gioco la prima delle due primitive di disegno: SpriteBatch.Draw. Tale funzione prende come parametri di base una texture, un rettangolo in pixel su schermo in cui la texture va messa, ed un colore per cui moltiplicare quello della texture. Quindi per inserire un’immagine come sfondo dovremo scrivere:

    spriteBatch.Draw(Content.Load<Texture2D>("background"),
        new Rectangle(0, 0, 800, 600), Color.White);
    

    Questa chiamata a draw sta indicando di disegnare la texture “background” con il suo colore originale (moltiplicare per Color.White lascia inalterato il colore) nell’area di schermo che inizia al pixel (0,0) ed ha dimensioni (800,600) pixel:

    clip_image002

    Ipotizziamo ora di voler disegnare un oggetto di qualche tipo che si muova ruotando sullo schermo. Diciamo anche che tale oggetto ha posizione “Position” ed e’ ruotato di “Rotation” radianti rispetto al suo centro. Allora dovremo invocare nuovamente draw, ma prima sara’ necessario introdurre un nuovo parametro di draw che definisce il centro di rotazione. Di default infatti uno sprite viene fatto ruotare intorno al suo angolo in alto a sinistra, dove effettivamente l’oggetto viene posizionato:

    clip_image004 clip_image006clip_image008

    Questo pero’ non e’ affatto cio’ che desideriamo: vorremmo che lo sprite ruotasse attorno al suo centro, non attorno ad un suo angolo. Esiste una versione della funzione Draw che accetta anche il parametro origin. Tale parametro serve per indicare quale e’ l’origine della texture, rispetto a cui Position e’ definita. Nel disegno qui accanto vediamo che l’origine e’ stata messa al centro della texture (25,25) posto che la texture fosse grande (50,50). Qualsiasi rotazione adesso sara’ relativa al centro e non agli angoli, proprio come desideriamo. La chiamata risultante per ottenere questo effetto sara’:

    spriteBatch.Draw(Content.Load<Texture2D>("rotating_particle"),
        new Rectangle((int)Position.X, (int)Position.Y, (int)Size.X, (int)Size.Y),
        null, Color.White, Rotation, Size / 2, SpriteEffects.None, 0.0f);
    

    in cui possiamo notare come la texture sia posizionata in Position, abbia dimensione Size, e sia centrata sul suo punto medio Size/2, affinche’ le rotazioni usino quello come riferimento e non l’angolo (0,0).

    SpriteBatch.DrawString

    Per disegnare testo la situazione non e’ molto piu’ complicata. Prima pero’ ci serve un file di contenuto di tipo spritebatch, per descrivere il tipo di font da usare, i suoi parametri (dimensione, bold, etc.) e il set di caratteri da caricare (di default si usa Basic Latin, che va dallo spazio ASCII sino alla tilde ~). Creiamo il file di contenuto “Arial.SpriteFont”:

    clip_image009clip_image010clip_image012clip_image014

    clip_image016

    In questo semplice file xml sono specificati il tipo del font di sistema da usare per il disegno (arial), la sua dimensione in punti (14), spaziatura, uso di kerning, stile (bold, regular, italic) e i range di caratteri da usare (qui vediamo che e’ stato richiesto di caricare i caratteri dal 32 al 126, ossia il set Basic Latin menzionato prima).

    Ipotizziamo di voler disegnare la doverosa stringa “Hello World!” su schermo. La chiamata opportuna risulta essere:

    spriteBatch.DrawString(Content.Load<SpriteFont>("arial"), "Hello World!",
        new Vector2(10, 70), Color.LightBlue);
    

    Vediamo che la funzione DrawString richiede lo SpriteFont da usare per costruire la scritta, il testo da scrivere, la posizione su schermo (dell’angolo in alto a sinistra della scritta) ed infine il colore del testo. Effettivamente questa invocazione produce la scritta desiderata in azzurro a partire dal pixel (10,70):

    clip_image018

    Se pero’ volessimo far ruotare la scritta intorno al suo centro? Avremmo bisogno di specificare l’Origin della nostra DrawString a meta’ della dimensione della stringa disegnata, ma per fare cio’ e’ necessario sapere quanto grande risultera’ la stringa su schermo. SpriteFont fortunatamente e’ in grado di fornirci questa misura:

    SpriteFont Arial = Content.Load<SpriteFont>("arial");
    Vector2 stringSize = Arial.MeasureString("Hello World!");
    

    Tramite cui possiamo disegnare la nostra stringa di testo roteante intorno al proprio centro:

    float Rotation = (float)gameTime.TotalGameTime.TotalSeconds;
    SpriteFont arialBig = Content.Load<SpriteFont>("arialBig");
    Vector2 stringSize = arialBig.MeasureString("Hello World!");
    spriteBatch.DrawString(arialBig, "Hello World!", new Vector2(400, 300),
        Color.Yellow, Rotation, stringSize / 2, 1.0f, SpriteEffects.None, 0.0f);
    

    clip_image002[4]

    Font disegnati a mano

    Tutto cio’ che abbiamo visto finora e’ relativo a fonts true type che siano installati nel sistema. Immaginiamo pero’ di avere un font disegnato a mano da un artista, come quello riportato in figura:

    clip_image004

    Sara’ possibile usare questo font non come texture (ossia importandolo direttamente), ma elaborarlo con un content processor appropriato e caricarlo in quanto sprite font. Il primo passo e’ quello di importare il file tra i contenuti del nostro gioco:

    clip_image006

    A questo punto sappiamo che il file dovra’ essere importato con un caricatore di texture, ma vogliamo che sia processato con un processor in grado di leggere i singoli caratteri e metterceli a disposizione. Tale processor e’ chiamato Sprite Font Texture - XNA Framework, e puo’ essere attivato dalle proprieta’ del file “customfont.png”:

    clip_image002[6]

    Da tenere a mente che il font, per funzionare correttamente, dovra’ rispettare due punti fondamentali:

    · Il canale alfa dovra’ rispecchiare quali parti del testo sono caratteri e quali sono lo sfondo dei caratteri

    · In mezzo tra i vari caratteri dovra’ esserci un colore magenta con alfa impostato ad opaco

    Ed e’ fatta, a questo punto e’ possibile caricare ed usare il nostro font come vedete qui sotto:

    spriteBatch.DrawString(Content.Load<SpriteFont>("customfont"), "test-text",
        new Vector2(10, 10), Color.LightBlue);
    

    Caratteri Unicode speciali

    Ipotizziamo adesso di tornare al caso iniziale, ossia di caricare un font true type installato nel sistema. Vogliamo pero’ poter inserire dei simboli speciali, nel caso specifico vogliamo vedere come stampare tramite sprite font i caratteri elencati di seguito: “€™∞”. Se proviamo direttamente a stampare una stringa del genere pero’ otterremo un’errore runtime che causera’ il crash del nostro gioco! Ricordiamoci che alla creazione dello sprite font abbiamo visto che alla fine del file generato automaticamente ci sono una serie di “character regions”, che denotano gli insiemi di caratteri che intendiamo usare. Di default il solo insieme caricato e’ il cosiddetto Basic Latin, ma possiamo estenderlo in due modi. Il primo e’ naturalmente quello di trovare una lista di caratteri unicode ed aggiungere a mano i codici dei caratteri desiderati nella definizione dello sprite font. La seconda strada e’ apparentemente piu’ complessa, ma in realta’ ci da’ molto piu’ controllo. Per prima cosa creiamo una custom pipeline nella soluzione che contiene il nostro gioco:

    clip_image004[4]

    La sola classe che deve essere contenuta nel nostro processor sara’ una che aggiunge i caratteri desiderati al set di caratteri da caricare, e quindi lancera’ il processor originale con il set di caratteri desiderato:

    namespace FontProcessor
    {
        [ContentProcessor(DisplayName = "ExtendedCharacterSetFontProcessor")]
        public class ContentProcessor1 : FontDescriptionProcessor
        {
            public override SpriteFontContent Process(FontDescription input,
                ContentProcessorContext context)
            {
                input.Characters.Add('€');
                input.Characters.Add('™');
                input.Characters.Add('∞');            
    
                // should load this list of characters from a file!
    
                return base.Process(input, context);
            }
        }
    }

    A questo punto aggiungiamo questo nuovo progetto tra le references del progetto content:

    clip_image006[4]

    Ed infine definiamo come font processor dello sprite font da estendere il processor appena creato:

    clip_image008

    Il font arial adesso sara’ identico a prima, ma in aggiunta supportera’ i caratteri che il nuovo processor aggiunge, rendendo una chiamata come

    spriteBatch.DrawString(Content.Load<SpriteFont>("arial"), "€™∞",
        new Vector2(10, 70), Color.LightBlue);
    

    perfettamente valida!

    Conclusione

    Trovate il sorgente online qui, mentre il webcast in cui viene scritto e spiegato nel dettaglio tutto il sorgente puo’ essere scaricato da questo link, o guardato direttamente nella sua versione low-res embedded:

     

    3/14/2008

    XNA e Windows Forms (controlli di Windows)

    English version

    di Alessandro Piva

    Ciao a tutti! Nello sviluppo di un videogioco, si ha sempre bisogno di uno o più editor, cioè programmi che servono per creare mappe, missioni, livelli, eccetera. Spesso è possibile trovare, sulla rete, editor già scritti e messi liberamente a disposizione da altri sviluppatori - ma, se non si riesce a trovarne nessuno che soddisfi le nostre esigenze, l'unico modo per risolvere il problema è scriversi un proprio editor.

    Per creare l'interfaccia utente dei nostri editor, è possibile usare i Windows Forms - i controlli di Windows, cioè quei pulsanti, caselle di testo, barre, menù che possiamo vedere in quasi tutte le applicazioni. Questo webcast è dedicato proprio a questo argomento: come far apparire, nella finestra della nostra applicazione XNA, dei Windows Forms! Inoltre, vengono mostrati alcuni esempi concreti di utilizzo. Ecco una schermata dimostrativa:

    Windows Forms in XNA screenshot

    Purtroppo tali controlli non possono essere usati se eseguiamo il nostro gioco a pieno schermo (fullscreen) né tantomeno sull'XBox 360, ma come già detto sono molto utili per creare interfacce per editor o applicazioni simili.

    Buon divertimento!

    P.S.: dimenticavo: ovviamente avete a disposizione il codice sorgente, il webcast ad alta risoluzione (scaricabile in alternativa anche qui), e proprio qui sotto il webcast a bassa risoluzione.

     
    3/13/2008

    Gestione dell’input in XNA

    Come far interagire l’utente con il nostro gioco!

    by Giuseppe Maggiore

    Introduzione

    In questo documento spiegheremo brevemente come si gestisce l’input in XNA, in modo da rendere finalmente interattive le nostre opere J.

    La gestione dell’input pero’ non e’ cosi’ semplice come ci potremmo aspettare: l’input ha luogo (diciamo che si manifestano le interazioni dell’utente) con una frequenza molto differente e molto piu’ bassa rispetto a quella con cui l’applicazione si aggiorna. In un semplice gioco casual le interazioni tra utente e macchina sono dell’ordine di qualcuna al secondo (pochi Hz), mentre il gioco si aggiornera’ a svariate decine di frame al secondo (decine di Hz). Quindi servira’ filtrare la velocita’ di aggiornamento dell’applicazione in relazione all’input.

    Periferiche supportate da XNA

    XNA offre pieno e nativo supporto a tre periferiche:

    - Mouse

    - Tastiera

    - Controller XBox 360

    Tali periferiche sono state scelte non a caso, ma perche’ sono quelle tipicamente trovate connesse ad una delle piattaforme su cui XNA gira, ossia Windows o l’Xbox 360. Altre periferiche possono essere impiegate, ma bisognera’ usare una libreria diversa da XNA per gestirle e ovviamente perderemo il supporto per l’XBox 360...

    Lettura dell’input

    Leggere l’input consiste nel farsi fornire una copia dello stato istantaneo di un dato controller in un dato momento:

    GamePadState gps = GamePad.GetState(PlayerIndex.One);
    KeyboardState kbs = Keyboard.GetState();
    MouseState mss = Mouse.GetState();

    Ovviamente tale stato non sara’ valido per sempre, ne’ tantomeno si aggiornera’ da solo: per questo motivo dovremo considerare queste variabili come delle “fotografie istantanee” dell’input di un qualche controller in un dato istante di tempo.

    Se volessimo fare uscire il nostro gioco alla pressione di un qualsiasi tasto (magari il tasto back del controller XBox 360) allora dovremmo aggiungere questo controllo alla nostra funzione Update:

    protected override void Update(GameTime gameTime)
    {
        gps = GamePad.GetState(PlayerIndex.One);
    
        if (gps.Buttons.Back == ButtonState.Pressed)
            this.Exit();
    }
    

    In cui si puo’ notare come aggiorniamo come prima cosa ad ogni Update lo stato della periferica di cui usiamo l’input.

    Problematiche rispetto al tempo

    Ipotizziamo di avere costruito una semplice applicazione che mostra al centro dello schermo un’astronave, e vogliamo che ogni volta che l’utente preme il tasto ‘A’ del controller XBox l’astronave spari un missile. L’approccio naive a questo problema sarebbe una cosa del tipo:

    if (gps.Buttons.A == ButtonState.Pressed)
        shoot();
    

    purtroppo per noi le cose non sono cosi’ semplici. Immaginiamo che l’utente tenga premuto il pulsante per un tempo piuttosto breve, diciamo una frazione di due decimi di secondo e che l’applicazione vada a 60 fps:

    clip_image002

    allora durante la pressione del tasto ‘A’, verranno sparati

     clip_image004

    missili, uno per fotogramma. Il risultato che otteniamo e’ questo, anche per pressioni apparentemente istantanee:

    clip_image006

    Dobbiamo tenere traccia di pressioni prolungate, ma non vorremmo farlo esplicitamente (magari con un array di booleani che ci dicono se un tasto era gia’ premuto precedentemente o no). Beh, il modo piu’ immediato per ottenere questo scopo e’ quello di memorizzare non solo uno stato dell’input (GamePadState) ma anche quello del fotogramma precedente:

    protected override void Update(GameTime gameTime)
    {
        prevGamePadState = gamePadState;
        gamePadState = GamePad.GetState(PlayerIndex.One);
    
        /*…*/
    }
    

    Grazie a questa seconda struttura di supporto, possiamo trasformare il codice con cui decidiamo se sparare o meno un missile in:

    if (gamePadState.Buttons.A == ButtonState.Pressed &&
        prevGamePadState.Buttons.A == ButtonState.Released)
    {
        Missile m = new Missile();
    
        m.p = Vector3.Zero;
        m.v = Vector3.Forward;
    
        missiles.Add(m);
    }
    

    Con cui una singola (anche lunga) pressione del tasto ‘A’ produce l’effetto desiderato, ossia un solo missile viene lanciato:

    clip_image008

    Con una variazione su questo stesso tema possiamo ottenere anche altri risultati interessanti oltre al timing dei singoli tasti. Immaginiamo di voler far sparare la nostra astronave in tutte le direzioni. In particolare, vogliamo che l’astronave spari verso dove e’ rivolto lo stick analogico destro del controller XBox (evidenziato nella figura):

    clip_image010clip_image012

    Il primo tentativo e’ analogo a quello che abbiamo fatto prima con il tasto ‘A’: se lo stick e’ premuto piu’ di un certo valore, spariamo il missile:

    if (gamePadState.ThumbSticks.Left.Length() > 0.25f)
    {
        Missile m = new Missile();
        m.p = Vector3.Zero;
        m.v = new Vector3(gamePadState.ThumbSticks.Left.X, 0.0f,
        gamePadState.ThumbSticks.Left.Y);
        missiles.Add(m);
    }
    

    Chiaramente sappiamo gia’ come va a finire, ovvero un sacco di missili verranno sparati per pressioni apparentemente anche molto brevi dello stick:

    clip_image014

    Ora potremmo anche usare lo stato del gamepad al frame precedente, ma la soluzione migliore non e’ necessariamente questa. Ricordiamoci che stiamo parlando di uno stick analogico, e vorremmo che il giocatore rilasci la levetta prima di sparare un secondo missile. Quindi dobbiamo tenere traccia piu’ esplicitamente del fatto che in un dato frame stiamo aspettando che l’utente rilasci lo stick analogico prima di poter sparare di nuovo:

    if (waitThumbstick == true)
    {
        if (gamePadState.ThumbSticks.Left.Length() < 0.15f)
            waitThumbstick = false;
    }
    else
    {
        if (gamePadState.ThumbSticks.Left.Length() > 0.25f)
        {
            Missile m = new Missile();
            m.p = Vector3.Zero;
            m.v = new Vector3(gamePadState.ThumbSticks.Left.X, 0.0f, gamePadState.ThumbSticks.Left.Y);
            missiles.Add(m);
        }
    }

    Grazie alla variabile waitThumbstick il nostro gestore dell’input aspettera’ che l’utente abbia rilasciato il thumbstick ad almeno lo 0.15 della sua pressione totale, prima di poter sparare di nuovo. Il comportamento di questa soluzione e’ quello desiderato!

    Una semplice classe Camera

    Immaginiamo di voler gestire tramite l’input del giocatore lo sguardo dell’utente. In particolare desideriamo che la matrice View venga costruita e aggiornata sulla base di due angoli di rotazione, orizzontale e verticale, che poi vengono aggiornati tramite il mouse o un thumbstick del controller XBox. Spostare il mouse (o il thumbstick) a destra o sinistra fara’ girare la telecamera a destra o sinistra, e similarmente gestiremo la rotazione nella direzione verticale. Come implementare questa utilissima funzione? Beh, intanto dichiariamo gli angoli di rotazione verticale e orizzontale come un vettore a due dimensioni:

    Vector2 cameraRotationVector = Vector2.Zero;
    

    e poi nella funzione Update lo aggiorneremo con la rotazione richiesta dall’utente (moltiplicata per la durata del frame precedente, cosi’ che lo sguardo si sposti alla velocita’ dell’applicazione, e non in modo veloce proporzionalmente al numero di frame per secondo):

    cameraRotationVector += gamePadState.ThumbSticks.Right *
        (float)gameTime.ElapsedGameTime.TotalSeconds;
    

    A questo punto dobbiamo costruire la matrice View, e naturalmente per questo ci sono decine di modi per farlo, differenti a seconda del tipo di telecamera che si vuole implementare e di una enorme quantita’ di altri fattori. Un esempio e’ quello di creare la camera inizialmente sempre nello stesso modo, tramite una chiamata a Matrix.CreateLookAt, e quindi applicare la rotazione desiderata:

    Matrix View
     {
         get
         {
             Matrix cameraRotation = Matrix.CreateRotationY(cameraRotationVector.X) *
             Matrix.CreateRotationX(cameraRotationVector.Y);
             return Matrix.CreateLookAt(Vector3.Up * 5000.0f, Vector3.Zero,
             Vector3.Forward) * cameraRotation;
         }
     }
    

    Se usiamo questa proprieta’ al posto della matrice View in tutte le chiamate di rendering, avremo effettivamente implementato una telecamera capace di ruotare su se stessa. Per spostare avanti e indietro la posizione dell’osservatore, potremo usare cameraRotation.Forward, cameraRotation.Backward, etc.

    Conclusione

    Il sorgente completo di questo sample si puo’ trovare online, assieme al webcast in cui viene mostrata e spiegata la stesura di tale codice. E’ anche possibile guardare il webcast direttamente qui sotto:

     

    1/30/2008

    Riconoscimento vocale - non tecnico

    Versione inglese

    Cia a tutti! Sono Giulia Costantini, e in questa sessione non tecnica vi mostrero' in azione una feature del nostro gioco di cui andiamo particolarmente fieri: il riconoscimento vocale. Nel nostro gioco ogni cosa, dal menu

    screenshot1

    all'interfaccia utente

    screenshot3

    puo' essere controllato con la voce. L'esperienza e' sorprendente, e spero che questo video in cui mostro il gameplay ibrido voce/comandi tradizionali sia intrigante anche per voi. Ne approfitto per mettere qualche altro screenshot del nostro gioco:

    screenshot1 screenshot2 screenshot3

    Il video ad alta risoluzione si trova qui. Per chi non volesse/potesse scaricare il video, ecco anche un embedding dello stesso webcast a risoluzione ridotta:

     

    Giulia Costantini

    1/23/2008

    Primi passi con la Content Pipeline

    Versione inglese

    Ciao a tutti! Sono Giuseppe Maggiore, e in questo tutorial vi illustrero' uno degli aspetti meno blasonati, seppur tra i piu' importanti, di XNA. Costruiremo un compilatore di contenuti personalizzato, o per dirla in inglese una Custom Content Pipeline. Lo scopo e' quello di costruire un assembly (una dll) contenente una serie di classi e oggetti che vanno a precondizionare i file di contenuto in modo da semplificarne il caricamento nel nostro gioco. Suona complicato? In effetti di primo acchito puo' sembrare un po' un eccesso andare a complicare qualcosa di immediato e banale come importare file nel proprio gioco. A onor del vero pero', bisogna dire che c'e' ben di piu' sotto la superficie: prima di tutto, questa architettura e' pensata per astrarre il caricamento di file in tutto XNA, soprattutto considerata l'XBox. Caricare files potrebbe essere immediato sotto Windows, ma non necessariamente la stessa cosa si applica all'XBox 360. Inoltre, tenete presente che non esiste un caricamento diretto dei file di gioco, perche' un minimo di processing sara' sempre e comunque necessario per convertire i bits grezzi in una rappresentazione significativa in classi. Il diagramma con cui rompiamo il problema in sotto problemi e' questo:

    image

    Un artista o un designer crea il file di contenuto. Tale file viene caricato dall'importer nel suo formato ancora non elaborato, e passato al processor che carica i dati in una classe dopo averli interpretati. Il processor passa i dati al writer, che li salva direttamente dalla classe in un formato di dati intermedio: un file .xnb. Quando il gioco viene lanciato, il file .xnb sara' ricaricato direttamente (o al piu' una versione un po' rifinita pronta per il rendering) attraverso il reader. La soluzione di Visual Studio contenente un gioco con un custom processor e' simile a quella in figura:

    image

    il primo progetto e' la custom pipeline, che infatti contiene importer, processor, writer e reader. L'altro progetto, il gioco vero e proprio, contiene due riferimenti all'assembly della custom pipeline: uno per il tempo di compilazione dei contenuti, che usa importer-processor e writer, l'altro per il tempo di esecuzione del gioco, che usa il reader. Nel progetto dei contenuti del gioco vediamo anche un file di estensione .custom, l'estensione personalizzata che la nostra content pipeline gestisce.

    I sorgenti sono online qui, mentre il video del tutorial si trova da quest'altra parte. Includo anche una versione embedded dello stesso video per chi non potesse/volesse scaricarlo:

     
    1/14/2008

    We want you!

    English version

    we-want-youCiao, sono Giulia Costantini, e, stranamente, non sono qui per parlarvi di un tutorial su XNA!

    Se vi foste chiesti cosa abbiamo fatto nelle ultime settimane, ebbene sappiate che abbiamo lavorato (duramente :) ) al nostro progetto per l'Imagine Cup! L'Imagine Cup e' una competizione a livello mondiale per studenti universitari e delle scuole superiori; quest'anno e' stata aggiunta la categoria "Game Development", in cui si deve creare un videogioco con l'aiuto di XNA. Il videogioco deve riguardare un tema ben preciso: "Come la tecnologia puo' aiutare la sostenibilita' ambientale".

    Perche' vi stiamo parlando di tutto cio'? Perche', pur essendo abbastanza avanti nello sviluppo del nostro gioco, siamo tutti e 3 programmatori... abbiamo bisogno di un grafico, di modelli e textures!!!

    Se sei uno studente universitario/delle superiori puoi unirti ufficialmente al nostro team per tentare di vincere l'Imagine Cup assieme a noi! Se non lo sei, non preoccuparti: contattaci comunque e troveremo un qualche accordo.

    Allora, cosa stai aspettando...? Scrivici (xnalearners _at_ hotmail.com)!

    A presto! Giulia

    12/28/2007

    Metti un Pythone nel tuo gioco!

    English Version

    Ciao, sono Giulia Costantini e in questo tutorial vi verrà mostrato come mettere una console Python nel vostro gioco. Perché Python? Semplice, perché è uno dei linguaggio più potenti, flessibili e facili da usare che esistono.

    La console Python è in realtà un game component (guardate il nostro precedente tutorial sui components se non sapete cos'è un component). Con questa console, possiamo modificare i nostri oggetti in-game senza bisogno di ricompilare, modificare i file di configurazione, eccetera... possiamo anche leggere ed eseguire dinamicamente script Python a tempo d'esecuzione!

    Nel videotutorial Giuseppe Maggiore (tradotto in italiano da Alessandro Piva) vi spiegherà come potete "inserire" Python nel vostro progetto e come potete creare il Python GameComponent.

    tut1

    Come vedrete nel webcast, saremo in grado di modificare gli oggetti publici nel gioco tramite la console Python: per esempio, potremmo azzerare la lista di droni e tutti i droni sparirebbero dallo schermo! Poi, potremmo aggiungere qualche altro drone (la lista dei droni è accessibile come pubblica dalla variabile globale "game") ed ecco: nuovi droni apparirebbero!

    La console appare così:

    tut2

    Qui potete trovare il codice sorgente utilizzato in questo tutorial. Buon divertimento!

    Ecco una versione a bassa risoluzione del videotutorial:

     

    Giulia Costantini

    P.S.: ringraziamenti speciali ad Alessandro Piva per l'idea di mettere IronPython nei nostri giochi.

    Game Components Tutorial

    English version

    Ciao gente! Sono Giulia Costantini e questo tutorial riguarda i game components in XNA. I game components sono una caratteristica molto importante e utile della piattaforma XNA. Per avere servizi ordinati e riusabili nel vostro gioco, c'è bisogno di una struttura generica per questi servizi. Visto che un servizio tipicamente ha una forma standard, i GameComponents astraggono questa forma in un insieme speciale di classi XNA. I game components vi permettono di costruire ordinatamente moduli logici riusabili.

    In questo videotutorial (tenuto da Giuseppe Maggiore e tradotto da Alessandro Piva), dopo aver spiegato più in dettaglio cosa sono i game components, vi verrà mostrato come esempio un semplice, eppure sempre utile, game component: un componente che scrive su schermo il frame rate sopra la scena esistente.

    Potete trovare il codice sorgente del tutorial qui.

    Qui potete vedere una versione a bassa risoluzione del webcast:

     

    Buon divertimento!

    Giulia Costantini

    12/21/2007

    Tutorial sulle textures

    [English version]

    Ciao a tutti! Io sono Giulia Costantini, e in questo tutorial vi spieghero' come fare per applicare una texture su una mesh. Se non sapete ancora cos'e' una texture, non vi preoccupate: entro la fine del tutorial saprete di tutto e di piu'!

    Questo tutorial e' diviso in due parti. Nella prima parte vedremo cosa e' una texture (una immagine che possiamo applicare sugli oggetti per renderli piu' verosimili), come si puo' importare e caricare una texture in un progetto XNA e infine come applicare la texture caricata su un semplice quadrato. Il risultato che otteremo sara' di questo tipo:

    Screenshot 1

    Nella seconda parte del tutorial vedremo dei concetti un po' piu' avanzati: in particolare, faremo degli esperimenti con le coordinate di texture (introdotte nella prima parte) in modo da capirle meglio. Inoltre aggiungeremo alla classe BasicMesh (introdotta nel tutorial sulla geometria, ricordate?) il supporto per le textures; cosi' facendo ogni forma geometrica gestita da quella classe (quadrato, griglia, sfera, cono, cilindro) avra' le sue coordinate di texture, rendendo immediata l'applicazione di una texture.

    Screenshot 2

    Come al solito il codice sorgente di questo tutorial e' gia' online. Ricordatevi che siamo qui per rispondere alle vostre domande, quindi non esitate a scriverci!

    Il webcast ad alta risoluzione si trova qui. Mettiamo anche un embedding dello stesso video a risoluzione piu' bassa per chi non potesse/volesse scaricare l'altro:

     

    Detto cio'... buon webcast! Giulia Costantini

    12/13/2007

    Equazioni in Word 2007...con Latex (piu' o meno)!

    English version

    Salve a tutti, sono Giuseppe Maggiore ed eccezionalmente oggi non parlero' di XNA. Capisco che questo possa lasciare perplessi (in effetti e' un blog su XNA), ma l'eccezione e' comprensibilissima. Quest'anno, infatti, mi e' capitata l'opportunita' di essere un MSP, e quindi sono sempre a caccia di materiale Microsoft interessante (e quando possibile gratuito) per offrire a studenti e docenti del mio dipartimento un motivo di interesse (o quantomeno curiosita') verso le iniziative tecnologiche della Microsoft.

    Veniamo al sodo: avete mai dovuto scrivere un articolo o una tesi pieno di formule matematiche? Se si, saprete allora che LateX e' una scelta obbligata, pur essendo decisamente una delle esperienze piu' old-school e user-unfriendly che io abbia mai sperimentato. Almeno finora. In Word 2007 il sistema di editing di equazioni e' stato ristrutturato completamente, obliterando del tutto il discutibile Equation Editor che ormai ha raggiunto la versione 3.0. Questo giro troviamo un parser di un linguaggio molto stile-LateX che permette di scrivere in LateX, ma leggere matematica vera e propria (e di switchare tra vista LateX e vista matematica). Io lo trovo favoloso!

    Spero di non fare la parte dell'idiota colossale (tipo che qualcosa di identico esiste che la gente usa da anni tranne io :) ), ma qui c'e' il webcast che mostra come usare questa gustosa caratteristica di Word2007!

    12/12/2007

    Tutorial sulle Matrici

    English version

    Ciao!

    E' giunto il momento del nostro secondo videotutorial: questa volta lavoreremo sulle Matrici. Nella computer grafica, le matrici sono molto importanti per possono rappresentare trasformazioni che possiamo applicare ai vertici. Traslazione, rotation, scalatura, proiezione prospettiva, tutte queste operazioni possono essere fatte molto facilmente con le matrici.

    Introdurremo anche il concetto di istanza di una mesh. Tutte le istanze di una mesh condividono i dati riguardanti i vertici e i triangoli, ma possono differire per posizione, colore, texture, e così via... Metteremo tutto questo in pratica scrivendo un piccolo demo; ecco uno screenshot:

    Immagine

    Basta esitare, ecco tutti i link:

    Potete guardare il webcast, in italiano, a bassa risoluzione, qui:

     

    That's all folks!

    12/6/2007

    Tutorial sulla Geometria!

    Salve a tutti, sono Giuseppe Maggiore e sono qui impegnato per dare la stura ad un evento storico: il nostro primo video-tutorial ufficiale. Speriamo di avviare un trend di una certa lunghezza!

    Bene, concluse le celebrazioni, diciamo un po' cosa fa questo tutorial: mostriamo un primo esempio basilare di come mandare geometria in wireframe sullo schermo, in 2D, come nell'immagine qui sotto:

    screenshot1

    per fare cio' ci servira' un VertexBuffer, ossia un puntatore a della memoria video appositamente creata per contenere i vertici che, a tre a tre, comporranno il nostro oggetto. Nell'esempio di sopra, il VertexBuffer contiene 6 vertici differenti, e il suo contenuto e' rappresentato qui sotto:

    quad vb

    quad

    Purtroppo pero' stiamo facendo qualcosa di non raccomandabile: stiamo memorizzando i vertici della diagonale ben due volte, sprecando la nostra preziosa memoria video! Possiamo allora evitare di memorizzare le ripetizioni, specificando un ulteriore oggetto, il cosiddetto IndexBuffer, che descrive quali triplette di vertici vanno prese per costruire i nostri triangoli:

    quad vb ib

     

    Nella seconda parte del videotutorial creiamo anche una classe, BasicMesh, per semplificarci la vita nel trattare oggetti (appunto, le Mesh) composti da un Vertex e da un IndexBuffer.

    I link al file video sono a maggiore risoluzione, ma qui trovate un embedding low-res del videotutorial:

     
    12/2/2007

    Demo Bicocca - SoftCity

    English version

    Ciao!

    Il mio nome è Alessandro Piva e sono un membro del team VS85. Come i miei compagni hanno già detto nei due post precedenti siamo persone molto interessate nella tecnologia e nella programmazione dei videogiochi; intendiamo utilizzare questo sito per pubblicare demo, webcast e tutorial per aiutare le persone (e anche noi stessi Sorriso) ad imparare la fantastica piattaforma XNA.

    Oggi, spiegherò il terzo demo che abbiamo presentato alla conferenza all'Università Bicocca, a Milano, il 28 Novembre. Questo demo è chiamato "SoftCity" e mira a risolvere due sfide tecnologiche:

    • Una tecnica di rendering avanzata chiamata deferred shading (a volta chiamata deferred rendering)
    • La gestione dei contenuti (texture, modelli, l'intera città) attraverso l' XNA Content Pipeline.

    Iniziamo dal deferred shader. Cos'è? E' una tecnica di rendering (cioè una procedura per disegnare cose su schermo), attualmente usata in molti videogiochi commerciali, che consente a grandi quantità di luci di essere presenti su schermo contemporaneamente. La nostra demo mostra quasi un centinaio di luci, e c'è spazio (e potenza computazionale) per averne di più.

    La tecnica è abbastanza complessa da implementare, ma semplice da capire: anzitutto, si disegna il colore degli oggetti (l'albedo); dopo, si marca nello stencil buffer quei pixel che sono toccati da almeno una luce; infine, si calcolano le pesanti equazioni della luce, ma solo per i pixel interessati. In questo modo è possibile illuminare solo i pixel giusti, e non uno di più.

    Ecco una paio di screenshot della nostra città:

    screenshot1 

    screenshot12 

    E adesso parliamo della gestione dei contenuti. Nonostante questo possa sembrare più triviale di complesse tecniche di rendering, è stato un problema non da poco. Questo perché importare contenuti nel proprio programma (demo, gioco, o altro) crea molte difficoltà come la conversione tra file di diverso formato, la corrispondenza tra i dati forniti dal programma di creazione dei contenuti (nel nostro caso un programma di modellazione 3D) e le informazioni di cui si ha effettivamente bisogno nel proprio codice, eccetera. Nel risolvere questi problemi, abbiamo fatto un uso estensivo della Content Pipeline integrata nell'XNA Framework.

    Abbiamo utilizzato il gratuito Softimage Mod Tool per costruire la nostra città. Grazie all'Add-On XNA Game Studio, abbiamo potuto importare i contenuti nel nostro demo; abbiamo scritto un nostro processore di contenuti per adattare i dati importati alle necessità del nostro renderer.

    Ecco una coppia di screenshot della città mentre vi stavamo lavorando in Softimage:

    screenA 

    screenB 

    Potete scaricare il codice sorgente o l'eseguibile (nonostante abbia bisogno ancora di molti rifinimenti). Buon divertimento!

    Demo Bicocca - Star Wars

    English version

    Salve gente! Sono Giuseppe Maggiore, membro di un gruppo di sviluppatori XNA noto (piu' o meno ;) ) come VS85Team (gli altri due sono Giulia Costantini e Alessandro Piva). In questo blog vi mostreremo, con una serie di tutorials, come scrivere videogiochi in XNA. Non servono conoscenze di sorta, tranne, un goccio di programmazione e d algebra (tranquilli, ci limiteremo al minimo indispensabile :) ).

    Il mio personale lavoro sara' di mostrarvi una battaglia spaziale tra astronavi "buone" e "malefiche". Qui c'e' qualche immagine delle features che arriveremo ad imparare a realizzare.

    Cominciamo dall'effetto warp con il cosiddetto "bloom", che fa apparire un alone di luce intorno agli oggetti che entrano nella scena. Questo effetto

    screenshot2 

    usa i render target, nonche' molti pixel e vertex shader,

    screenshot3

    e l'impiego di una potente tecnica di postprocessing

    screenshot5  

    chiamata "sfumatura gaussiana". Per ottimizzare il tutto dovremo anche impiegare lo stencil buffer, imparando a rendere piu' efficiente un pixel shader pesante ma che lavora su una sola porzione dello schermo!

    A questo punto, implementeremo la battaglia vera e propria: le navi si aggireranno a vicenda, sparandosi missili che esploderanno sull'obiettivo frantumandosi in mille pezzi. Scudi di energia proteggeranno le navi dai

    screenshot8

    danni degli avversari, ma alla fine le astronavi troppo

    screenshot10 screenshot21

    danneggiate esploderanno in un effetto particellare piuttosto dinamico basato sull'alpha blending.

    A breve cominceremo coi tutorials, ma al momento trovate online anche il sorgente. E' molto disordinato, ma se foste curiosi buttate pura una occhiata :) !

    Demo Bicocca - AnnX

    [English version

    Ciao a tutti, io sono Giulia Costantini, un membro del VS85Team, e il mio compito sara' di mostrarvi una rete neurale in azione. Questa e' una cosa piuttosto particolare perche' delle reti neurali si parla (anche molto) ma vederle e' tutt'altro paio di maniche! Questa demo costituisce dunque un importante risultato, dal momento in cui le reti neurali sono difficili da visualizzare e anche da debuggare (se avete provato almeno una volta a programmarle capirete cosa intendo :) ). Con questa demo (centrata sul problema dello XOR, ma facilmente estendibile ad altri problemi) finalmente potrete VEDERE una rete neurale in azione!!.

    Cominciamo dicendo che una Artificial Neural Network (ANN, da cui viene il nome della demo: AnnX :) ) puo' essere considerata uno "strumento magico" che riproduce il funzionamento del cervello umano. Per potersi comportare come un cervello, pero', la rete deve essere allenata. Non intendo annoiarvi con le "interiora" matematiche di una rete neurale; vi mostero' la demo AnnX e, un po' alla volta, vi insegnero' a costruirla da zero.

    La nostra rete, che e' abbastanza piccola, risolve il problema dello XOR, quindi l'input della rete consiste di 2 bits (ad esempio 0 e 1) e l'output consiste di un valore (lo XOR dei due bit di input). All'avvio del programma dovete premere il tasto "E" per allenare la rete: in questo modo la rete impara a risolvere il problema dello XOR. Quando l'allenamento e' finito (non vi preoccupate, dura solo pochi secondi!) il programma mostra un messaggio:

    epochDone

    Dopodiche', potete premere "spazio" e... ha inizio la magia!!! Piccole palline rotanti (rappresentanti il potenziale dei nodi) cominciano a fluire attraverso la rete, portandovi alla fine al risultato corretto del problema considerato. Ad esempio, il risultato dello XOR tra 0 e 1 e'...

    0xor1

    ...0.98!!! Il risultato preciso dovrebbe essere 1, ma direi che possiamo accettare questo tipo di approssimazione e considerare il risultato corretto :) !

    Vorrei richiamare la vostra attenzione su due cose:

    • sulle sfere che rappresentano i nodi e' presente un effetto grafico, chiamato environment mapping. Grazie a questo effetto l'ambiente circostante viene riflesso sull'oggetto considerato (nel nostro caso le sfere). Per implementare questo effetto dovremo scrivere un vertex e pixel shader, ma, non essendo l'effetto troppo complicato, sara' abbastanza facile. Qui potete scaricare un PDF in cui si spiega il cube mapping, cioe' l'implementazione dell'environment mapping che abbiamo usato.
    • per scegliere dove effettivamente mettere i singoli nodi sullo schermo (avreste mai immaginato che questo potesse essere un problema? :) ) dobbiamo usare un tool per disegnare grafi. Infatti una rete neurale e' molto simile ad un grafo (nodi + archi). Grazie a tale tool, possiamo cambiare la struttura della rete a piacimento senza doverci preoccupare del layout dei nodi. Abbiamo scelto la libreria Microsoft GLEE, che e' gratuita e funziona meglio di tutti gli altri tool che abbiamo provato.  

    Per concludere, perche' questa demo e' importante? Perche' abbiamo mostrato che, con l'aiuto di alcune potenti tecnologie(XNA, GLEE e non dimentichiamoci il C#!), le reti neurali non sono piu' uno strumento matematico mistico e incomprensibile: le reti neurali possono essere davvero divertenti, sia da programmare che da usare!!! Nell'arco dei prossimi mesi posteremo dei webcast in cui spiegheremo nei dettagli come realizzare questa demo, ma, se proprio non potere resistere :), sappiate che il codice sorgente della demo e' online (come anche l'eseguibile), quindi, se volete guardarlo o provare a modificare, voi (e le vostre domande ;) ) siete i benvenuti!!!

    Giulia Costantini