XNA: Gioco (molto) Semplice con Fisica

 Consulenze disponibili!

XNA: Gioco (molto) Semplice con Fisica

Giuseppe Maggiore

Ph.D. Student – Languages and Games

Senior Microsoft Student Partner

 

Webcast (con sorgenti)

Nel webcast cominciamo con una minima introduzione ad XNA: sprite batch, content pipeline e modelli 3D, per poi passare all’assemblaggio di un giochino rudimentale dotato di fisica e persino di un game component per pensare a come far scalare l’architettura nel caso uno volesse estendere l’opera ordinatamente (l’ingegneria del software é importante J). Sfruttiamo un motore di fisica preconfezionato, JigLibX, rilasciato presso CodePlex con sorgente aperto (o libero? Bof…):

 [video http://storage.academicclub.org/xna/TieFightersWebcast.wmv nolink]

Introduzione a WPF – Parte 3

_Giuseppe Maggiore_

_Ph.D. Student (Languages and Games)_

_Microsoft Student Partner_

Consulenze disponibili su WPF!

Descrizione

In questo webcast discutiamo di alcuni concetti fondamentali che caratterizzano fortemente lo stile di programmazione di WPF.

I concetti che esploriamo in questa introduzione sono:

  1. Come costruire una Custom Dialog
  2. Cosa sono le DependencyProperties
  3. Come funzionano gli Eventi
    1. Tunneling
    2. Direct
    3. Bubbling
  4. Ancora sul DataBinding
  5. Come definire e riferire risorse in un controllo
    1. Statiche
    2. Dinamiche
  6. Come costruire Stili e Animazioni
    1. Ridefinire completamente l’aspetto di un controllo e il suo comportamento visuale con i ControlTemplate
    2. Definire la traduzione di una qualsiasi classe del CLR in un UIElement con DataTemplate
    3. Animare dependency properties tramite Storyboard ed EventTrigger

 [video http://storage.academicclub.org/xna/wpfintro3.wmv nolink]

Global Game Jam

Giuseppe Maggiore

Ph.D. Student (Languages and Games)

Microsoft Student Partner

Materiali e Codice XNA

Metto a disposizione un po’ di materiali utilizzabili in giochi XNA nonché il codice visto a lezione. Aggiungo il codice di un gioco di esempio aggiuntivo, che per funzionare richiede Microsoft F# (1.9.7.8): questo sample mostra come realizzare (lo potete copiare in tronco, ovviamente J) una battaglia spaziale con tanto di esplosioni cinematiche e suoni posizionali…

Vi allego qualche screenshot dei due giochi:

Slides

Gioco 1

(in Gioco 1, togliere e rimettere da Visual Studio i riferimenti a Boids.dll e FSharp.dll)

Gioco 2

Introduzione a WPF – Parte 2

_Giuseppe Maggiore, Ph.D. Student_

 Consulenze disponibili su WPF!

Descrizione

 In questo webcast realizziamo i nostri primi passi nel mondo di WPF. Discutiamo i concetti fondamentali e quindi costruiamo la nostra prima, semplice applicazione WPF.

I concetti che esploriamo in questa introduzione sono:

  1. Differenza tra Window e Page
  2. Come funziona la classe Application
  3. Distinguere tra
    1. Content, ossia il contenuto di un controllo, che puó essere una qualsiasi classe del CLR
    2. Control Template, ossia il template che descrive come viene costruito l’aspetto di un controllo
    3. Stile, che setta alcune proprietá di un controllo in modo da customizzarlo
  4. Come funziona il DataTemplate, per definire in che modo si traduce una qualsiasi classe del CLR in un insieme di controlli WPF disegnabili
  5. Come funziona il DataBinding, per la sincronizzazione automatica di dati e controlli

Dopo aver esplorato questi concetti di base, realizziamo una applicazione molto semplice che sfrutta WPF in stile Windows Forms. Sfruttiamo un wrap panel per posizionare dinamicamente alcuni UserControl i quali possono mandare del feedback alla Window contenitore tramite delle callback rappresentate tramite Action.

 

Webcast

 [video http://storage.academicclub.org/xna/wpfintro2.wmv nolink]

Monadi in dettaglio – Versione Haskell

Monadi in dettaglio – Versione Haskell

Giuseppe Maggiore, Ph.D. Student

Chapter 1 Exception Handling

Vogliamo gestire i possibili errori che le varie porzioni del nostro programma potrebbero generare. Consideriamo l’approccio ormai standard delle eccezioni, con un programma in pseudo-codice á-la-Java/C#:








In un linguaggio imperativo sappiamo che le funzioni usate possono tutte lanciare un’eccezione, anche se a guardarne il tipo non si direbbe:



Nessuna di queste funzioni ritorna esplicitamente un’eccezione, e infatti a meno di non sapere cosa accade “di nascosto” pare proprio che tutte queste funzioni restituiscano un valore corretto in qualsiasi condizione vengano chiamate. Ovviamente ció non é vero: che intero potrebbe venire restituito da ? Cosa ritorna  quando il file é terminato? Come deve comportarsi  se la stringa non rappresenta un file esistente?

Tutti i comportamenti sbagliati fanno si che le nostre funzioni lancino una eccezione ogni volta che non é possibile ritornare un risultato sensato. La firma completa delle funzioni che stiamo considerando é quindi:



Siccome peró tipicamente tali funzioni vengono chiamate con parametri corretti, e gli errori rappresentano un comportamento inusuale (appunto, “eccezionale”), facciamo si che la gestione degli errori avvenga da un’altra parte rispetto al codice che potrebbe generarla. In Haskell saremmo costretti a ritornare valori da un tipo unione, in cui un costruttore rappresenta l’esecuzione corretta (contenente il risultato desiderato) e un altro costruttore rappresenta l’esecuzione con errori e la descrizione dell’errore avvenuto:

Le firme delle nostre funzioni sono ora modificate in:



Il vantaggio immediato é che ora risulta evidente da dove possono uscire errori, e una semplice ispezione del tipo della funzione ci dice subito che il risultato potrebbe essere un descrittore di errore invece del risultato desiderato. Inoltre il tipo rappresenta piú accuratamente il comportamento della funzione, e la semantica del linguaggio é piú chiara: non c’é alcun misterioso meccanismo con cui le eccezioni vengono propagate “sotto le coperte”. Nonostante i vantaggi di questo metodo, c’é una evidente ragione per cui non si usa questo sistema in linguaggi come Java: ne risulta codice illeggibile. Ogni assegnamento nel codice originale diventerebbe un pattern-matching (o un if). Il codice Haskell che realizza il programma iniziale sarebbe infatti:










In questo listato la logica originale é completamente sepolta sotto tonnellate di gestione degli errori, ed effettivamente non si capisce quasi nulla di quello che accade. Fortunatamente possiamo osservare un aspetto ripetitivo di questa modalitá di scrittura del codice, nella speranza di poterlo astrarre via:

Codice originale

Codice con eccezioni esplicite








Lo stesso é accaduto per trasformare GetLine e int.Parse in un sistema con eccezioni esplicite. Questo suggerisce che potremmo definire un operatore che prende un risultato di tipo Result a, controlla che il risultato sia corretto (non un’eccezione) e se lo é passa il valore contenuto in esso al resto del programma, e altrimenti propaga l’errore:



Grazie a questo operatore (detto operatore di binding) valutiamo un’espressione che potrebbe dare errori, e in caso di errore ne propaghiamo la descrizione, altrimenti proseguiamo con la valutazione del resto del programma passandogli il risultato della valutazione dell’espressione. L’operatore di binding rende il codice Haskell notevolmente piú leggibile, e la gestione delle eccezioni é adesso generalizzata:







Le differenze tra questo listato e quello iniziale sono:

1.       L’assegnamento é verso destra, ossia file=Open(…) diventa

2.       Il resto del codice é diventato una funzione che prende come parametro il valore assegnato; questo sembra strano di primo acchito, ma in effetti ogni volta che si assegna una variabile, il codice successivo che la usa puó essere visto come una funzione che aspetta il valore di quella variabile; in effetti sappiamo che

3.       Il try…catch… é diventato

4.       Ritornare il risultato finale richiede di incapsularlo nel costruttore Value

I punti 2 e 3 sono accettabili, mentre i punti 1 e 4 lo sono assai di meno: assegnare verso destra é decisamente spiacevole, specialmente considerate le nostre abitudini; inoltre costruire un valore non dovrebbe mai richiedere di incapsularlo in un tipo apposito. Fortunatamente da un po’ di tempo alcuni linguaggi avanzati hanno cominciato a supportare una sintassi apposita per questa classe di costrutti (in particolare Haskell e F#). Siccome in realtá le comprensioni di liste, come vedremo piú avanti, sono un caso particolare di tali costrutti, si puó scoprire che moltissimi linguaggi sono parzialmente equipaggiati con una sintassi per realizzare queste funzionalitá.

La soluzione all’”inestetismo” introdotto dal punto 1 é presto detta: la do-notation di Haskell (per F# guardare i Workflows). Il compilatore ci tradurrá


In

La “bruttura” del punto 4 invece si risolve con un altro operatore, comunemente definito assieme all’operatore di binding: l’operatore unitario:


In questo modo ogni volta che arriviamo al valore “finale” da restituire, possiamo farlo con la (ormai consolidata nell’immaginario comune) keyword return.

Adesso possiamo definire un’utile funzione:



Arrivati a questo punto possiamo confrontare il codice iniziale con una traduzione che sfrutti tutti i costrutti introdotti finora:

Codice originale

Codice tradotto














Ora vediamo che le differenze sono decisamente marginali: la forma del codice é notevolmente simile, a parte qualche simbolo diverso (= invece di ) e il gestore delle eccezioni é esplicitamente definito come una funzione che prende come argomento l’eccezione stessa, invece che un blocco di codice che ha la variabile di tipo eccezione in scope.

Chapter 2 Monadi

Finora abbiamo definito in maniera piuttosto informale una serie di tipi di dato e di operatori per la gestione degli errori. In questo modo (pur senza accorgercene, ma qualche lettore accorto lo sospettava giá da tempo) abbiamo realizzato la nostra prima monade. Una monade é definita come una terna:

  

Monade

Exceptions Monad

Tipo monadico

Operatore di binding


Operatore unitario


 

M a é il costruttore di tipo della monade, ossia il tipo di dato che verrá gestito “dietro le quinte” dagli operatori di binding e unitario. Il tipo di dato M a contiene una sequenza (anche vuota!) di valori di tipo a.

L’operatore di binding opera in quattro stadi:

1.       Apre la monade di input, estraendone tutti i valori di tipo a

2.       Applica il secondo argomento a ciascuno dei valori generati al punto 1

3.       Apre tutte le monadi ottenute al punto 2, ottenendo una serie di valori di tipo b

4.       Assembla tutti i valori di tipo b ottenuti al punto 3

L’operatore unitario fa da costruttore della piú “semplice” monade che incapsuli il valore di tipo a che viene passato in input. Naturalmente la semplicitá va intesa in modo appropriato rispetto alla definizione della monade.

Assiomi

Cosa rende una monade una monade vera e propria? Effettivamente per assicurare che una monade abbia un comportamento sensato sono stati definiti degli assiomi che definiscono come si comportano gli operatori monadici in alcuni casi “al limite”:



Gli stessi assiomi possono essere scritti con la notazione do di Haskell:



Oltre agli assiomi una monade puó (opzionalmente) definire uno “zero”, ossia un particolare valore della monade che non contiene al suo interno alcun valore di tipo a. Se la monade definisce uno zero, allora deve essere vero che:


Una monade puó ulteriormente definire un’operazione di somma, ossia un’operazione che dati due valori della monade li componga in uno solo:

La somma monadica é associativa e lo zero ne é identitá destra e sinistra.

Chapter 3 Le monadi come DSL

La monade di gestione degli errori vista finora é una monade semplice e tutto sommato intuitiva. Esistono peró moltissime monadi, in grado di coprire sostanzialmente tutte le computazioni immaginabili.

Le monadi possono essere viste come il modo per implementare esplicitamente la semantica di un qualche  linguaggio, che tramite la monade diventa un “sotto-linguaggio” del linguaggio ospite. Tramite le monadi é possibile costruire quelli che vengono comunemente chiamati Linguaggi Specifici di Dominio (DSL, o Domain Specific Language) che permettono di implementare la soluzione ad un qualche problema costruendo un linguaggio di programmazione “ideale” per affrontare tale problema. I DSL dipendono ovviamente dal dominio del problema. Il problema puó essere generale: scrivere programmi concorrenti, paralleli o in stile imperativo. Il problema puó anche essere piú specifico: manipolare liste in modo immediato, nascondere ottimizzazioni algoritmiche, scrivere la logica di una UI in modo dichiarativo. In ogni caso si definiscono le istruzioni del proprio mini-linguaggio, la semantica di tali istruzioni (ossia che effetto ha l’esecuzione di ciascuna istruzione) e le si implementa in una monade.

Nel seguito discuteremo come costruire due monadi estremamente rilevanti: la monade lista e la monade di stato. La monade lista ci permette di rappresentare una lista e le operazioni su di essa in modo estremamente elegante, e di dare una solida base di naturale generale che supporti la sintassi delle list-comprehensions in Haskell. La monade di stato, invece, ci permette di costruire un linguaggio imperativo (“stile C”, per intenderci) come monade dentro Haskell. La monade di stato consente infatti di nascondere le transizioni di stato (un tipo di dato che rappresenta la memoria), poiché in un programma imperativo lo stato viene modificato da una istruzione all’altra, ma sarebbe scocciante e soggetto ad errori portarcelo dietro esplicitamente da un’istruzione alla successiva.

Monade lista

Consideriamo ora un iteratore in un linguaggio imperativo:




Possiamo immaginare il corpo del ciclo for come una funzione che dipende dal valore corrente x. Per ogni elemento della lista l, viene invocata la funzione . In questa ottica, possiamo dare un tipo all’iteratore (in maniera del tutto informale):

Naturalmente all’iteratore in un linguaggio imperativo non serve ritornare altro che Unit poiché puó accedere alla memoria del processo e modificarla liberamente. Poiché in un linguaggio funzionale come Haskell non ci sono alternative per una funzione se non di ritornare i valori risultato della computazione, un eventuale costrutto di iterazione avrebbe firma:

Nell’iteratore Haskell ogni elemento a della lista genera una lista List b e alla fine tutte le liste risultanti vengono concatenate tra loro. Se diamo una descrizione testuale del comportamento dell’operatore foreach per come lo abbiamo definito in Haskell, tale descrizione sarebbe:

1.       Apre la lista di input, estraendone tutti i valori di tipo a

2.       Applica il secondo argomento a ciascuno dei valori generati al punto 1

3.       Apre tutte le liste ottenute al punto 2, ottenendo una serie di valori di tipo b

4.       Concatena tutti i valori di tipo b ottenuti al punto 3

Questa descrizione rispecchia perfettamente la descrizione generale dell’operatore di binding che abbiamo visto nel capitolo Monadi. L’operatore foreach é dunque il candidato ideale per farci da operatore di binding. Questo implica che il tipo della monade M a sará in realtá il tipo List a, che in effetti sappiamo contenere una serie di valori di tipo a. L’operatore unitario piú ragionevole sará poi quello che genera una lista con un solo elemento. Il resto della monade lista é facile da ricostruire:

Per capire meglio come funziona l’operatore di binding, vediamo un semplice esempio:

Dalla definizione dell’operatore di binding sappiamo che dobbiamo applicare una map alla lista originale e alla funzione di trasformazione, e quindi concatenare la lista di liste risultante:



Il vantaggio portato dal fatto di considerare le liste come monadi é che moltissime computazioni sulle liste possono adesso venire definite con grandissima naturalezza. La map di una lista rispetto ad una funzione é:


Il filtraggio di una lista é:


Il prodotto cartesiano filtrato di due liste é:



In generale poi le comprensioni di liste altro non sono che zucchero sintattico costruito sopra l’interpretazione monadica delle liste. In effetti l’operatore ← che adoperiamo nelle comprensioni di liste é esattamente lo stesso operatore di binding visto finora (non si tratta di una banale sovrapposizione sintattica):

Comprensione

Monade

Notazione do







Il vantaggio di usare le comprensioni di liste é evidente nella leggibilitá che tale notazione ha, soprattutto grazie alla somiglianza con la notazione insiemistica a cui siamo tutti piú o meno abituati:

diventa:

Gli ulteriori punti di forza del fatto che le monadi stanno sotto alle comprensioni di liste sono:

1.       Il compilatore é piú semplice in quanto implementa una caratteristica di meno (solo le monadi piuttosto che monadi + comprensioni di liste ad hoc)

2.       Possiamo costruire monadi per qualsiasi collezione in modo analogo alle comprensioni su liste, anche se senza lo zucchero sintattico.

Monade di stato

Come ultima monade che vediamo in dettaglio, scegliamo quella che mostra (a mio avviso) senza ombra di dubbio l’estrema potenza di linguaggi come Haskell o F# dotati di monadi di prima classe: realizziamo un intero mini-linguaggio imperativo sotto forma di monade, all’interno di Haskell e senza alcuna problematica risultante (non é una soluzione “sporca”).

Per semplicitá il nostro linguaggio imperativo definisce solamente due locazioni di memoria, che chiamiamo v1  e v2. Essendo un linguaggio non puro, vi saranno delle istruzioni di lettura e scrittura che leggono, ma soprattutto “modificano in-place” le nostre due variabili. Un possibile programma valido (impossibile da scrivere in Haskell puro) sarebbe:





Se all’inizio dell’esecuzione avessimo  e , il risultato ritornato sarebbe .

Come si comporta questo programma? Chiaramente per descrivere questa esecuzione, possiamo immaginare che ogni istruzione prenda in input lo stato (la coppia del valore corrente di v1  e v2) e ritorni come output un valore (la valutazione dell’espressione) e il nuovo stato (che potrebbe essere stato modificato durante la valutazione dell’istruzione):

Stato prima della valutazione

Istruzione

Valore ritornato

Stato dopo la computazione

Questo significa che per valutare ogni nostra istruzione, in un sistema funzionale che ne rappresenta uno imperativo, una istruzione prende in input lo stato corrente (la “memoria” che contiene le variabili) e ritorna lo stato modificato piú il risultato della valutazione delle espressioni. Il sistema di esecuzione dovrebbe anche essere fatto in modo da garantire che un programmatore “sbadato” non usi uno stato vecchio, altrimenti si genererebbero inconsistenze.

Il tipo di una istruzione (che é il nostro tipo monadico) risulta essere:

Le funzioni che interagiscono con le variabili di stato sono rispettivamente i metodi di lettura e scrittura delle due variabili v1  e v2:

Getter

Setter

Adesso vogliamo definire un operatore di binding che esegua una istruzione in modo da rispettare la logica del passaggio di stato. Per fare ció l’operatore di binding dovrá passare all’istruzione da eseguire lo stato corrente, ottenere un valore e un nuovo stato, e passare al resto del programma sia il valore calcolato che il nuovo stato.

Ricordiamo che il tipo dell’operatore di binding é:

Ossia nel nostro caso




Il nostro esempio di codice imperativo viene ora riscritto come:

Codice originale

Codice con monadi

Notazione do













Il codice originale e il codice in notazione do sono quasi identici, ed é notevole come siamo riusciti a spremere da un linguaggio funzionale e “puro” come Haskell codice completamente impuro e imperativo. Non a caso molti Haskellers sostengono Haskell essere il piú elegante e completo linguaggio imperativo esistente .

Chapter 4 Codice Haskell della monade di gestione degli errori


 


 


 


 

Introduzione a WPF

Giuseppe Maggiore, Ph.D. Student, MSP

Universitá Ca’ Foscari – Venezia

Consulenze disponibili su WPF!

Descrizione

In questo webcast presentiamo una introduzione al mondo di WPF. Tale tecnologia é molto speciale, perché integra nel framework .Net una serie di eccezionali capacitá di creazione di interfacce utente con l’intento di agevolare sia il designer che lo sviluppatore.

Tramite WPF é possibile realizzare UI animate, dotate di grafica avanzata, dotate di stili (aggiunta attesissima da legioni di sviluppatori di Windows Forms) e completamente flessibili e customizzabili. Ogni controllo puó ospitare contenuto di qualsiasi tipologia (altri controlli, oggetti CLR) ed essere customizzato a piacere: il content model distingue tra contenuto e presentazione in ogni controllo, in modo da poter customizzare i contenuti tramite DataTemplate e la visualizzazione tramite ControlTemplate.

Il dialogo tra la logica dell’applicazione e l’UI avviene ancora tramite eventi (anche se potenziati in modo da lavorare con grazia su tutto l’albero dell’applicazione – RoutedEvents) ma anche in modo largamente automatico se si sceglie di creare dei legami tra dati e controlli (i cosiddetti data binding).

Per esempio possiamo inserire il risultato di una query LINQ in una ListBox e mostrare tali oggetti in orizzontale e mostrare ogni oggetto con uno UserControl definito dall’utente tramite data template.

Il codice di una applicazione WPF é diviso in markup XAML e C#. Esistono due designer Microsoft per questi due aspetti: Expression e Visual Studio. Mentre Visual Studio si rivolge principalmente agli sviluppatori, Expression é pensato principalmente per i designer. Tramite il meccanismo di stili ed estensibilitá lo sviluppatore ed il designer possono lavorare a stretto contatto aggiornando ciascuno il codice dell’altro senza causarsi disturbo e senza dover usare i vecchi trucchi che si impiegavano con WindowsForms.

Una menzione speciale la merita la grafica di WPF: pixel indipendenti dalla risoluzione, supporto per font antialiased, supporto per accelerazione della scheda video (per effetti 2D), pieno supporto del 3D (anche per disegnare controlli su superfici tridimensionali), permettono di realizzare applicazioni che sfruttino la massima potenza disponibile in un PC moderno per realizzare applicazioni veramente eccezionali dal punto di vista visivo.

In chiusura, ricordiamo anche l’integrazione di WPF con tutte le aree solitamente interessate dallo sviluppo di applicazioni desktop: mai prima d’ora é stato cosí facile integrare documenti, stampa ed elementi multimediali nel proprio sistema.

Webcast

In questo webcast viene discussa la “teoria” di WPF. Ho trovato opportuno iniziare da qui invece piuttosto che subito dal codice per evitare di fare confusione: WPF é un sistema molto ingegnerizzato, e in quanto tale tuffarsi subito nel codice risulta fonte di grande confusione. Per questo suggerisco a chi inizia con WPF di “trattenersi” dal googlare “wpf tutorial” e di avere un po’ di pazienza J

Nella prima parte discutiamo di user-experience, poi di WPF come sistema integrato di documenti, e quindi vediamo snippet di XAML circa stili, data template, control template, data binding.

[video http://storage.academicclub.org/xna/wpfintro.wmv nolink]

Monadi in F#

 Giuseppe Maggiore, Ph.D. Student, MSP

Universitá Ca’ Foscari – Venezia

 Consulenze disponibili su F#!

Problemi aperti nella programmazione

Definire la gestione di eccezioni. Costruire un parser. Accumulare delle informazioni di stato ad ogni riga di una computazione. Definire un sistema concorrente. Maneggiare collezioni in modo dichiarativo á la LINQ. Cosa hanno in comune queste applicazioni? Che ogni riga del codice scritto deve eseguire delle operazioni relativamente ripetitive. Tramite le monadi é possibile realizzare un wrapper che viene applicato ad ogni riga del nostro codice in modo da nascondere completamente al programmatore cosa sta accadendo “dietro” le quinte; le operazioni nascoste possono creare thread, gestire collezioni, costruire e manipolare GUI o eseguire complessi calcoli matematici, il tutto senza che il programmatore debba muovere un dito.

Cosa sono le monadi?

Le monadi sono un costrutto sintattico (che in linguaggi come Haskell o F# viene “zuccherato” in modo da renderlo piú piacevole da vedere e intuitivo da usare) tramite cui possiamo fare si che ogni riga di codice del nostro programma svolga anche una serie di operazioni nascoste; in tal modo possiamo crearci dei mini-linguaggi di altissimo livello in cui le istruzioni sono completamente definite da noi: siccome la gestione della sintassi é giá fatta dal linguaggio ospite, peró, non abbiamo nessuno dei problemi associati alla costruzione di un vero linguaggio. Inoltre siamo comunque ospitati dall’ambiente di esecuzione del linguaggio ospite, quindi ad esempio nel caso di F# il nostro mini-linguaggio di altissimo livello puó poggiarsi a qualsiasi funzionalitá offerta dal .Net.

Ok, bello, ma io cosa posso farci?

L’elenco di cose fattibili con le monadi é lunghissimo. L’idea di base é che una monade rende facile nascondere una logica sottostante che viene ripetuta spesso. Ad esempio possiamo creare una monade per la gestione dell’iterazione di liste, in cui parliamo solo dell’elemento corrente di una serie di liste:

list{

  let! x = [1..10]

  let! y = [1..10]

  if x > y then return (x,y)

}

 In cui costruiamo il prodotto cartesiano delle liste e manteniamo solo le coppie in cui il primo elemento é maggiore del secondo.

Possiamo costruire una monade di gestione di messaggi asincroni, in cui scriviamo

msg{

  let! x = receive_from ip

  do! send_to (x+1) ip

}

 In cui riceviamo e mandiamo dei messaggi ad un altro host senza menzionare tempi di attesa o errore o creazione di canali di comunicazione.

Infine (per il resto vi rimando ai webcast) é possibile semplificare un vastissimo insieme di operazioni, praticamente quasi qualsiasi cosa in cui é possibile individuare una logica ripetuta sottostante in contesti in cui la libertá di programmare di chi usa la logica ripetuta deve essere mantenuta. Insomma, le monadi sono lo strumento ideale per il programmatore che vuole costruire delle “über-macro” J per il proprio codice…

  Read the rest of this entry »

XNA@DSI – Ingegneria del Software

Quest’anno al corso di Ingegneria del Software del dipartimento di Informatica dell’Universitá Ca’ Foscari di Venezia é stato svolto un interessante esperimento: affiancare al classico lavoro teorico di produzione di documenti tecnici sulla creazione di un progetto informatico anche la produzione del progetto stesso. In particolare, é stato chiesto agli studenti di produrre un videogioco (piú precisamente, un sistema che permettesse di giocare a vari giochi di carte): per lo sviluppo di tale videogioco é stata usata la tecnologia XNA.

Nonostante la maggior parte degli studenti non avesse alcuna esperienza pregressa con XNA (e, a volte, nemmeno con C#, il linguaggio che si accompagna ad XNA), i ragazzi sono riusciti a padroneggiare la tecnologia in breve tempo (la durata del corso), producendo dei progetti finali veramente degni di nota.

Abbiamo quindi chiesto ad un gruppo (“I Cordiali”) di presentarci il loro lavoro e di darci il loro feedback sull’esperienza, abbastanza “diversa” da quella di un classico corso universitario:

 

 

N.B.: Il corso di Ingegneria del Software é stato tenuto dal docente Agostino Cortesi, con l’assistenza di Giuseppe Maggiore e Giulia Costantini. Il gruppo dei “Cordiali” é formato da Francesco Boscariol, Mauro Barbon. Luca Cosmo, Chiara Poma e Nicola Loiolino (da sx a dx come appaiono nel video).

SpriteBatch per effetti speciali – parte 4

By Davide Luzzu – Webcast by Giuseppe Maggiore

 

Il (resto del) codice C#

 

      // Generiamo un array di float che sono le altezze (y) dei punti di

 controllo.

      var tmp =

        Enumerable.Range(0, controlPoints.Width)

        // i [0,controlPoints.Width-1]

        .Select(i => i / (float)controlPoints.Width)

        // x [0,1]

        .Select(x => sin(-4.0f * t + 32.0f * pi * sigmoid(x + 0.5f) * x)

          * 0.1f + 0.5f)

        // y é l’altezza di un punto di controllo. La convertiamo in formato

  integrale.

        .Select(y => (UInt16)(y * (UInt16.MaxValue – 1)))

        .ToArray();

      // Salviamo l’array di altezze nella texture.

      controlPoints.SetData<UInt16>(tmp);

 

Per dare la forma al serpente definiamo i punti di controllo sulle x e sulle y. Per fare ciò ci serviremo delle funzioni sin e sigmoid, che abbiamo definito in precedenza. Definiamo un range da 0 a 256, e ne facciamo il rapporto tra 0 e 1 in modo che x [0,1]. Dato che x è nel range [0,1] possiamo definire la forma del serpente usando il seno. Tale forma varia in base al tempo    ( 4.0f * t…) ed ha, ad ogni istante di tempo, una forma curva
definita da  “…
32.0f * pi * sigmoid(x + 0.5f) * x…”; variando tali parametri determineremo un’alterazione a piacimento della forma o del tempo di esecuzione del serpente.

Una delle peculiarità derivanti da questa porzione di codice è il fatto che, ciò che viene disegnato, è ad una dimensione algoritmicamente, ma a due dimensioni visivamente; la x difatti è un seno, e ciò è sufficiente per disegnare il serpente. Ma la x è l’unica variabile da cui dipende effettivamente il controllo dello spazio. Quindi è monodimensionale.

A questo punto la y è libera, e la si utilizza per inserire i dati di illuminazione, quelli che avevamo previsto inserendo SurfaceFormat.Luminance16 nella definizione della texture. Convertiamo la y in intero a 16 bit Unsigned, e trasformiamo la struttura in un array. Quindi la variabile “tmp” è un array di interi a 16 bit Unsigned.

Non resta che inserire i dati dei punti di controllo nella Texture2D.

 

      // Carichiamo la texture di sfondo.

      var background = Content.Load<Texture2D>("background");

      /// Disegnamo la texture di sfondo. Questa operazione non dipende in alcun

 modo da come poi disegneremo il serpente.

 

      spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate,

                  SaveStateMode.SaveState);

      spriteBatch.Draw(background, new Rectangle(0, 0, 800, 600), Color.White);

      spriteBatch.End();

 

Per rendere l’applicazione più gradevole prevediamo uno sfondo, di nostro gradimento, che disegnamo con lo SpriteBatch. Ci preme ribadire che tale operazione non dipende in alcun modo da come disegneremo il serpente.

 

      /// Disegnamo il serpente con il nostro shader in alpha blending sulla

 scena precedente. L’operazione di disegno genera un serpente che passa

 per i punti di controllo della texture "controlPoints".

 

      new Vector3[] { new Vector3(0, 1, 1}

        .Select((color, index) => new {color, index})

        .ToList().ForEach(snake =>

        {

          fx.Parameters["SnakeColor"].SetValue(snake.color);

 

          spriteBatch.Begin(SpriteBlendMode.AlphaBlend,

                        SpriteSortMode.Immediate, SaveStateMode.SaveState);

 

          fx.Begin();

          pass(0, p => p.Begin());

 

          spriteBatch.Draw(controlPoints, new Rectangle(0, 100 + 100 *

                              snake.index, 800, 400), Color.White);

 

          pass(0, p => p.End());

          fx.End();

 

          spriteBatch.End();

        });

 

      base.Draw(gameTime);

}

 

Finalmente disegnamo il serpente!

Definiamo innanzittutto il colore con un vettore, un azzurro tenue.

Inseriamo il colore in ogni posizione di un array di Vector3 e lo modifichiamo con la tecnica dello Shader.

Il primo passo è impostare il parametro “SnakeColor” dello Shader con l’azzurro appena definito.

Iniziamo lo SpriteBatch con il metodo “Begin()” e avviamo il rendering dello shader con

 

fx.Begin();

 

che provoca, inoltre, la sostituzione automatica dello Shader di default nello SpriteBatch.

A questo punto non resta che aprire il passo zero di rendering con

 

pass(0, p => p.Begin());

 

e disegnamo sullo schermo la Texture2D.

Chiudiamo il passo e indichiamo che lo shader ha terminato la sua tecnica con

 

fx.End();

 

ricordandoci di chiudere anche le funzioni dello SpriteBatch con la funzione “End()”.

 

SpriteBatch per effetti speciali – parte 3

By Davide Luzzu – Webcast by Giuseppe Maggiore

Il codice C#

 

Effect fx = null;

Texture2D controlPoints;

const int numControlPoints = 256;

protected override void LoadContent()

{

  /// Costruiamo il renderer 2D.

  spriteBatch = new SpriteBatch(GraphicsDevice);

  /// Costruiamo la texture di control points, in cui carichiamo interi

  /// unsigned a 16 bit. La texture é una matrice numControlPoints × 1,

  /// cioè 256 colonne e una riga.

  controlPoints = new Texture2D(GraphicsDevice, numControlPoints, 1, 1,

        TextureUsage.None, SurfaceFormat.Luminance16);

 

  // Carichiamo lo shader HLSL.

fx = Content.Load<Effect>("snake shader");

}

 

La prima operazione da compiere è ottenere la configurazione grafica ( GraphicsDevice ) e applicarla ad uno SpriteBatch. Il GraphicsDevice ci permette di ottenere informazioni riguardo alla risoluzione grafica con cui si sta eseguendo l’applicazione, e, di conseguenza, impostare il Pixel Shader. Lo SpriteBatch è il render 2D che ci consentirà di disegnare il nostro serpente.

Il secondo passaggio fondamentale è creare una Texture2D che contiene dei punti di controllo (256), che daranno la forma al nostro “serpente”. Questi punti di controllo possono essere concettualmente associati ai key
frames
di una animazione; cioè definiamo quali sono le posizioni per cui la nostra animazione dovrà assolutamente passare, il resto viene fatto per interpolazione. Sarebbe, infatti, impensabile dover inserire tutti e 25 i frames ( riferito ad un video in formato PAL) di ogni secondo della animazione, poiché, per ottenere un minuto di animazione, servirebbe agire su 1500 singoli frames! Allo stesso modo quello che facciamo noi è definire dei “frames” (detti anche nodi o punti di controllo ) e nessun dato nella Texture 2D, in modo da avere la possibilità di inserirli a nostro piacimento. Per poter manipolare una elevata quantità di informazioni di illuminazione sulla texture, indichiamo che il formato della superficie è Luminance16; è un formato che comprende 16 bit di dati solo per illuminazione, cioè potremo manipolare 216 combinazioni di colore.!

L’ultima operazione da effettuare è il caricamento dello Shader. Si fa riferimento alla classe Content che contiene il metodo ad accesso statico Load. Tale metodo riceve in input una stringa che designa il percorso di accesso al file (*.fx ) che contiene il codice dello Shader. Ricordiamo che il nome dello Shader va indicato senza l’estensione. Il metodo Load è parametrizzato con un tipo Effect; ciò indica che la variabile di ritorno conterrà dati di tipo Effect.

 

protected override void Draw(GameTime gameTime)

{

      //Resettiamo lo sfondo dell’applicazione ad azzurro.

      graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

 

      // Alcuni piccoli helper trigonometrici e non.

      Func<float, float> sin = x => (float)Math.Sin((double)x);

      Func<float, float> sigmoid = x =>

                             (float)MathHelper.SmoothStep(0.5f, 0.1f, x);

 

      var pi = (float)Math.PI;

 

      // Da quanti secondi l’applicazione é in esecuzione?

      var t = (float)gameTime.TotalGameTime.TotalSeconds;

 

      // Helper di attivazione/disattivazione di un passo dello shader HLSL.

      Action<int, Action<EffectPass>> pass = (i, a) =>

                                                a(fx.CurrentTechnique.Passes[i]);

 

Eseguiamo l’override del metodo Draw di default, dove scriviamo tutto il codice per effettuare il disegno, senza demandare nulla ad altri metodi.

Definiamo innanzitutto due funzioni per aiutarci nei calcoli, una (sin) che calcolerà il seno per definire la forma del serpente, e l’altra (sigmoid) per interpolare i dati.

Di particolare interesse, in questa porzione di codice, è la definizione dei passi dello Shader HLSL.

 

Action<int, Action<EffectPass>> pass=(i, a) => a(fx.CurrentTechnique.Passes[i]);

 

Quello che intendiamo fare è definire in modo elegante l’attivazione e la disattivazione del passo dello Shader. Il codice ML-style ci viene incontro in questo senso, poiché creiamo una labda expression. Diremo che l’azione pass deve compiere una certa azione ad un determinato indice. L’azione che pass eseguirà la chiameremo (a), e (a) effettua un passo della tecnica dello Shader; dato che la tecnica può contenere più di un passo, dovremo indicare quale è l’indice del passo che (a) deve effettuare, e lo indichiamo con (i). Come abbiamo visto, la nostra tecnica contiene un singolo passo, quindi non dovremo aggiornare l’indice ( che sarà sempre zero ). In altre parole piuttosto che scrivere

 

fx.Begin();

fx.CurrentTechnique.Passes[0].Begin();

 

//…code…

 

fx.CurrentTechnique.Passes[0].End();

fx.End();

 

avremo una espressione decisamente più elegante

 

fx.Begin();

pass(0, p => p.Begin());

 

//…code…

 

pass(0, p => p.End());

fx.End();

 

L’azione che (a) esegue è di tipo EffectPass, cioè un tipo di effetto derivante dalla tecnica dello shader in uso.