26) Importiamo nuove .dll
Per eseguire questa lezione ci serviremo del progetto che avevamo lasciato in precedenza, nella lezione 24.
Come accennato in precedenza, è possibile importare nuove classi e componenti programmati da terzi che ci faciliteranno il lavoro.
Esistono due modi per importare delle nuovi classi esterne. Uno è importando un nuovo progetto all'interno della nostra soluzione e poi aggiungendolo ai "Riferimenti".
Il secondo modo è importando un progetto già compilato sottoforma di .dll aggiungendola sempre ai "Riferimenti"..
Usando la prima opzione, potremmo effettuare eventuali modifiche alla classe del progetto, mentre importando una .dll non potremmo fare modifiche al codice.
La seconda opzione è sicuramente più veloce, trattandosi di un file già compilato, non dovremo ricompilare il codice ad ogni compilazione del nostro progetto. Inoltre avremo una soluzione molto più ordinata senza la presenza di altri progetti escluso il nostro. Daltronde lo stesso XNA è un insieme di .dll, infatti, se spuntiamo la cartella "Riferimenti" della nostra soluzione, potremmo vedere alcune delle .dll che compongono la libreria di XNA.
Si tratta dei namespace che contengono le classi di XNA e di System che abbiamo usato fin'ora, richiamandoli con la parola using.
Andremo ora ad importare una nuova .dll che ho creato appositamente per questo tutorial, utile per la visualizzazione dei BoundigBox.
I BoundigBox hanno la stessa funzione delle BoundigSphere che abbiamo visto nella lezione 24. Essi sono però di forma parallelepipeda, come una scatola che contiene un determinato oggetto.
Normalmente, per inizializzare un BBox dovremo usare due Vector3, con questa sintassi:
La creazione di un BoundigBox richiede due parametri di tipo Vector3 che identificano il punto minimo e il punto massimo del parallelepipedo che vorremo costruire.
Nel nostro caso non dovremo specificare nessun Vector3 perché usaremo un metodo (della classe che importeremo con la .dll), che creerà un BoundingBox in base alle dimenzioni del modello.
Come abbiamo visto per la BoundigSphere, anche i BoundigBox non vengono renderizzati su schermo, ci sono, fanno la loro funzione, ma non si vedono. Sarà utile in alcuni casi, renderli visibili per avere un'idea immediata di quello che succede.
A questo scopo servirà la .dll che ho programmato: ci renderà disponibile una nuova classe che ho chiamato Wz_BoundingBox, una versione modificata dei BoundigBox di XNA.
La classe Wz_BoundingBox
La classe Wz_BoundingBox permette la creazione di un BoundingBox che contiene un modello con le relative trasformazioni (posizione, scala, rotazione) e permette la stampa a video del BoundingBox in questione .
La classe base BoundingBox di XNA non implementa tali opzioni, ovvero, un BoundingBox non viene spostato automaticamente allo spostamento dell'oggetto che contiene e nello stesso modo, esso non possiede un metodo per essere visualizzato su schermo.
Ho fornito la classe Wz_BoundingBox di due metodi, uno per la creazione del BBox e una per l'eventuale Draw a video dello stesso (o di qualunque altro BBox).
Il metodo per la creazione del BBox l'ho chiamato GetModelBBox e richiede due parametri:
mio_BBox = Wz_BoundingBox.GetModelBBox(mio_modello, World);
Dove il primo parametro è il modello di cui vorremo creare la BBox e il secondo parametro è la matrice World dello stesso modello. La matrice sarà necessaria per avere il BBox sempre aggiornato in base alle trasformazioni dell'oggetto, se per esempio, l'oggetto diventerà più piccolo, anche il BBox si trasformerà in base alle dimensioni dell'oggetto, o se l'oggetto si muoverà, il BBox lo seguirà tenendolo sempre al suo interno (in realtà il BBox non ruoterà mai, anche se il modello lo farà, essendo i BBox sempre orientati con gli assi, ma si deformerà per contenere sempre il modello al suo interno) .
Il metodo per il Draw del BBox l'ho chiamato Render e richiede cinque parametri:
Wz_BoundingBox.Render(mio_BBox , GraphicsDevice, View, Projection, colore);
Primo parametro: il BoundigBox che vogliamo renderizzare.
Secondo parametro: il nostro graphicDevice.
Terzo parametro: la matrice View.
Quarto parametro: la matrice Projection.
Quinto parametro: il colore di cui vorremmo la BoundingBox.
Ovviamente potremo usare uno o l'altro metodo a nostro piacimento, se per esempio volessimo visualizzare un semplice BoundingBox di XNA su schermo, potremo usare il metodo Render della classe Wz_BoundingBox senza problemi inviandogli come primo parametro il BoundingBox che vogliamo renderizzare. Per questo motivo ho preferito dividere l'inizializzazione e il draw in due metodi distinti.
Scaricate la dll da questo link: Wz_BBox v1.0.zip (versione XNA 2)
NOTA: ho ricompilato la dll per la versione 3.1 di XNA : Wz_BBox for XNA3.1.rar ma non ho avuto la possibilità di testarlo. Se ci dovessero essere problemi, vi prego di contattarmi.
Scompattate il file in una cartella qualsiasi del vostro progetto, per rendere più ordinato il nostro lavoro potremmo creare una nuova cartella chiamata "dll" dove terremo tutte le eventuali risorse esterne del nostro progetto.
Ora andiamo sulla nostra soluzione ed aggiungiamo un nuovo riferimento.
Fate attenzione ad aggiungere la .dll nella cartella Riferimenti che trovate nel progetto, non quella presente nella cartella Content .
Si aprirà una finestra da dove potremo scegliere che tipo di riferimento intendiamo importare.
Portiamoci su "Sfoglia" così da poter scegliere un riferimento di tipo dll presente nel nostro hard disk.
Sa avessimo usato il primo metodo descritto all'inizio di questa lezione, importando cioè un nuovo progetto non compilato nella nostra soluzione, avremmo dovuto cercare dalla finestra "Progetti" .
Scegliamo la dll appena scaricata e clicchiamo su OK.
Adesso nella nostra soluzione apparirà un nuovo riferimento, Wz_BBox.
Per usare le classi contenute in questo nuovo namespace, non dovremo far altro che usare la parola using, come abbiamo sempre fatto per le librerie System e di XNA.
Dunque, aggiungiamo la direttiva using Wz_BBox; su tutti i file .cs dove intendiamo usare la nostra nuova classe, nel nostro caso, sia su pg.cs che su Stampa_Modelli.cs.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using Wz_BBox; <---usiamo il nostro nuovo namespace e le classi in esso contenute
namespace tutorial_XNA_3D
{....
A questo punto avremo a disposizione tutte le classi e i metodi contenuti nel namespace Wz_BBox; appena importato come riferimento.
Andiamo a modificare i file .cs del pg e dei modelli (pg.cs e Stampa_modelli.cs) sostituendo la BoundigSphere con il nostro nuovo Wz_BoundingBox.
Come prima cosa però dovremo modificare il file della struttra Mie_variabili cambiando la BSphere con un BBox:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
namespace tutorial_XNA_3D
{
public struct Mie_variabili
{
public static Matrix View;
public static Matrix Projection;
public static Vector3 posizione_pg;
public static Vector3 vecchia_posizione_pg;
public static BoundingBox BBox_pg; <----
}
}
Nel metodo Draw() del file pg.cs andremo a modificare il codice in questo modo:
Draw() di pg.cs
public override void Draw(GameTime gameTime)
{
// ricordiamoci queste due righe che trovano la posizione nel fotogramma precedente
// e la salvano nella variabile Mie_variabili.vecchia_posizione_pg
Mie_variabili.vecchia_posizione_pg = tmp_posizione_pg1;
tmp_posizione_pg1 = Mie_variabili.posizione_pg;
// mettiamo la matrice World in una variabile così da poterla spedire
//
al metodo per l'inizializzazione del nostro Wz_BoundingBox
// nella riga a seguire
Matrix World =Matrix.CreateTranslation(Mie_variabili.posizione_pg);
// Impostiamo il BBox del pg come un Wz_BoundingBox a cui spediremo i parametri richiesti:
// il modello e la matrice di trasformazione World
Mie_variabili.BBox_pg = Wz_BoundingBox.GetModelBBox(mio_modello, World);
// Facciamo stampare a video il nostro BBox inviado i parametri richiesti:
// il BBox che vogliamo renderizzare, il graphicDevice, le matrici View e Projection
//
e il colore che vorremo dare al BBox (ovviamente voi potete scegliere qualsiasi colore)
Wz_BoundingBox.Render(Mie_variabili.BBox_pg, GraphicsDevice, Mie_variabili.View, Mie_variabili.Projection, new Color(0, 255, 255));
foreach (ModelMesh mesh in mio_modello.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.World = World; <-- Diciamo che la matrice World dell'oggetto è quella impostata poco sopra
effect.View = Mie_variabili.View;
effect.Projection = Mie_variabili.Projection;
}
mesh.Draw(SaveStateMode.SaveState);
}
base.Draw(gameTime);
}
Ricordiamoci anche di dichiarare il nuovo Vector3 tmp_posizione_pg1; all'inizio del codice di questo .cs, insieme alle altre dichiarazioni di variabili.
Facciamo la stessa cosa anche per i modelli statici, modifichiamo il Draw() del file Stampa_modelli.cs.
Draw() di Stampa_modelli.cs
public override void Draw(GameTime gameTime)
{
//////////////////////////////////////////////////////////////////
//Anche qui mettiamo le trasformazioni del modello in una variabile
Matrix World = Matrix.CreateTranslation(posizione_modello);
//Creiamo un BBox con la classe Wz_BoundingBox
BoundingBox BBox_modello = Wz_BoundingBox.GetModelBBox(mio_modello, World);
//Decidiamo di stampare a video il BBox che contiene il modello
Wz_BoundingBox.Render(BBox_modello, GraphicsDevice, Mie_variabili.View, Mie_variabili.Projection, new Color(250, 250, 0));
//Rileviamo la collisione tra i due BBox appena creati
if (Mie_variabili.BBox_pg.Intersects(BoundingBox))
{
Mie_variabili.posizione_pg = Mie_variabili.vecchia_posizione_pg;
}
//////////////////////////////////////////////////////////////////
foreach (ModelMesh mesh in mio_modello.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.World = World; <--- Anche quì impostiamo la matrice appena creata sopra.
effect.View = Mie_variabili.View;
effect.Projection = Mie_variabili.Projection;
}
mesh.Draw(SaveStateMode.SaveState);
}
base.Draw(gameTime);
}
Abbiamo usato il nostro BBox customizzato ed il risulatato sarà questo:
Ora potremo controllare visivamente le collisioni tra gli oggetti.
Quando non ci servirà più visualizzare i limiti degli BBox potremmo semplicemente cancellare o meglio, commentare, la riga del render, semplicemente aggiungendo i due slash che indicano un commento:
// Wz_BoundingBox.Render(BBox_modello, GraphicsDevice, Mie_variabili.View, Mie_variabili.Projection, new Color(250, 250, 0));
In questo modo avremmo eliminato il rendering dal video ed avremmo sempre questa riga pronta per essere usata in futuro, nel caso volessimo rivisualizzare il BBox. |