La classe Ray (Cliccare su un oggetto in uno spazio 3D)

Vedremo ora una delle classi più utili di XNA: Ray (raggio).
Questa classe non fa altro che creare una linea che va da da un Vector3 verso un'altro Vector3. Tale raggio ha lo stesso metodo Intersects delle BoundingSphere e dei BoundingBox così che da rendere possibile la rilevazione di collisione con Plane, BoundingSphere, BoundingBox o un altro Ray.
Come tutti gli elementi che rilevano le collisioni, esso non verrà stampato su schermo e non lo vedremo ammenochè non creeremo un metodo apposito che ne faccia il draw, come fatto con i Wz_BoundingBox.

La classe Ray è molto semplice, ha due parametri, la posizione(il Vector3 di partenza) e la direzione(altro Vector3) .

Ray mio_ray = new Ray(V3_partenza, V3_a_cui_punta);

Nota: Per "direzione" non si intende il punto finale del raggio ma il punto in cui "guarda". Esso infatti continuerà anche dopo il Vector3 di direzione.

Ray

Dunque, impostare per esempio, un Ray così:

Ray mio_ray = new Ray( Vector3(0,0,0), Vector3(1,0,0) );

cioè un Ray che va dal punto 0,0,0 in direzione 1,0,0, produrrebbe lo stesso identico risultato se la direzione fosse (2,0,0) o (13,0,0) ecc... Ovvero, ci troveremo sempre di fronte ad un Ray che dal punto 0,0,0 punterà orizzontalmente verso destra.

 

Calcoliamo un Ray che va dal puntatore verso l'interno dello schermo

L'applicazione più utile della classe Ray è sicuramente quella che ci permette la rilevazione del click su un oggetto in uno spazio 3D.
Quando avremo la necessità di rilevare se il puntatore del mouse è sopra ad un oggetto, o un particolare punto del nostro mondo 3D, utilizzeremo questa classe.
Dunque, sarà utile per tutti i giochi "punta e clicca" in 3D dove si utilizza il puntatore per cliccare sugli oggetti o muoversi in uno spazio tridimensionale.
Ovviamente, se vogliamo rilevare la collisione del puntatore con un oggetto, il modello in questione dovrà possedere una BSphere o un BBox.

Il medoto che vi propongo quì sotto serve a calcolare il Ray che va dal puntatore verso l'interno dello schermo, perpendicolarmente ad esso, dunque esattamente sotto al puntatore.
Dipende anche dalla vista, dunque anche dalle matrici che stiamo utilizzando ProjMatrix e ViewMatrix: (WorldMatrix non ci interessa, può essere anche Vector3.Zero).


       

public Ray GetPickRay()
        {

//Mette le posizioni X e Y su due variabili int
            int mouseX = mouseState.X;
            int mouseY = mouseState.Y;

//Mettele due variabili su due Vector3 per calcolare il punto di partenza e la direzione
// la prima con Z=0 e la seconda con Z=1 cioè che punta dentro lo schermo.

            Vector3 nearsource = new Vector3((float)mouseX, (float)mouseY, 0f);
            Vector3 farsource = new Vector3((float)mouseX, (float)mouseY, 1f);

//Trasforma il Vector3 di partenza in base alle matrici. (Viewport è la vista che stiamo utilizzando.)
             nearPoint = m_graphicsMgr.GraphicsDevice.Viewport.Unproject(nearsource, ProjMatrix, ViewMatrix, WorldMatrix);

//Stessa cosa, trasforma il Vector3 di direzione in base al punto in cui guarda la telecamera
             farPoint = m_graphicsMgr.GraphicsDevice.Viewport.Unproject(farsource, .ProjMatrix, ViewMatrix, WorldMatrix);

//Trova il Vector3 che rappresenta la direzione
            Vector3 direction = farPoint - nearPoint;
            direction.Normalize();

//Crea il nostro Ray in base ai punti appena trovati
            Ray pickRay = new Ray(nearPoint, direction);

//Ci restituisce il Ray appena trovato
            return pickRay;
       }

Con questo metodo avremo in uscita il nostro fantastico Ray che parte dal puntatore e prosegue in avanti, sotto di esso.
Ora ci basterà fare il check della collisione Ray/Oggetto come se fossero due BSphere.
Sapendo che scrivendo GetPickRay() è come se scrivessimo il Ray che abbiamo calcolato nel metodo, potremmo scrivere semplicemente così:


 

   if( GetPickRay().Intersects(BS_oggetto) ){

      //il mouse è sopra l'oggetto

   }

A questo punto avremo determinato il passaggio del puntatore sopra alla BSphere, potremmo ora fare tutti gli altri controlli, come per esempio controllare se viene premuto il tasto del mouse ecc..