martedì 28 aprile 2009

3D.4) Xna tutorial 3d: Creare una camera FPS

Nello scorso articolo avete visto come si carica un modello 3d, ora sfrutteremo lo stesso codice apportando qualche modifica alla camera per imparare a gestire un modello di navigazione della scena simile a quello delle camera FPS (first person shooter).

Anteprima Video:

Prendete i codici sorgenti a fondo pagina o utilizzate quelli della scorsa "lezione".

Quello che vogliamo fare è aggiungere alla camera delle informazioni di posizione dinamiche, fare in modo quindi che tramite determinati eventi (presisone di tasti e spostamento del mouse) la camera cambi posizione e direzione.

Prima id mostrarvi il codice della camera vi descrivo quali sono le legiche che andremo ad implementare:

Una prima modifica rispetto alla camera base vista negli scorsi post, sta nel identificare una direzione verso la quale la camera punta.

Fino ad ora abbiamo avuto a che fare solo con il target e la posizione della camera, ora tramite una semplice sottrazione di vettori andremo a definire la direzione = targetCamera - posizioneCamera.


Esempio:
Guardando l'immagine sopra diciamo che :
- il punto dal quale partono i vettori è il nostro punto (0,0,0) origine
- il vettore a è la posizione della camera [esempio (2,3,0)]
- il vettore a + b è il target della camera [esempio (7,3,0)]
- il vettore b è la direzione della camera [esempio (5,0,0)]

Come calcoliamo la spostamento della camera?
Associando alla pressione di uno dei tasti classici per una camera FPS (w,a,s,d), un movimento della camera nello spazio.

Per mantenere gli spostamenti uniformi, prima di tutto rendiamo il vettore direzione "normalizzato" ovvero poniamolo sempre di lunghezza 1.
I tasti W e S faranno muovere la camera di una "unità" nella direzione attuale (in avanti o indietro).
Nel caso dell'esempio di prima, la camera posizionata in a-> si sposterà lungo la proiezione di b-> su a-> ad ogni ciclo update. Quindi l'asse che determina lo spostamento è quello definito dalla direzione della camera.
Il movimento dei tasti A e D richiede un pò di calcoli in più:
Per definire lo spostamento laterale della camera dovremo eseguire il prodotto cross (in italiano prodotto vettoriale) del vettore che identifica il verso Up della camera e la direzione. Questo prodotto genera il vettore (l'asse) laterale della camera. Quindi la camera si sposterà sul prodotto vettoriale del vettore Up con il vettore direzione.
Se non conoscete il prodotto cross non preoccupatevi, esiste una funzione predefinita di xna che eseguirà il calcolo per noi(l'importante per ora è capire la logica).

Le formule di spostamento:
Spostamento avanti (W)
posizioneCamera += Direzione

Spostamento indietro (S)
posizioneCamera -= Direzione


Spostamento destra (D)
posizioneCamera -= ProdottoCross(Direzione,Up)

Spostamento Sinistra (A)
posizioneCamera += ProdottoCross(Direzione,Up)

Come calcoliamo le Rotazioni della camera ?
In questo caso per definire una rotazione dobbiamo prima capire quale asse interviene nel calcolare l'angolo di spostamento dell'obbiettivo in base agli spostamenti del mouse.

Muovendo il mouse a destra o sinistra vogliamo che la camera ruoti sull'asse Y (movimento yaw)
mentre muovendo il mouse avanti o indietro la camera deve ruotare sull'asse laterale della camera, il prodotto vettoriale tra Up e Direzione (movimento Pitch)

In entrambi i casi il vettore che subirà modifiche sarà il vettore direzione e nel caso del movimento Pitch verrà modificato anche il vettore Up. (Provate a simulare con le mani la rotazione della camera usando il pollice come vettore UP e L'indice come Direzione mettendoli perpendicolari tra di loro)

Le formule di rotazione:
Rotazione Yaw
Direzione +-= Rotazione di direzione sull'asse Up * spostamento del mouse (destra o sinistra)

Rotazione Pitch
Direzione +-= Rotazione di Direzione sull'asse (Up*Direzione) * spostamento del mouse (avanti o indietro )
Up +-= Rotazione di Up sull'asse (Up*Direzione) * spostamento del mouse (avanti o indietro)

A questo punto non resta che incollare qui il codice della camera:

FPSCAMERA.cs
using System;
using
System.Collections.Generic;
using
System.Linq;
using
Microsoft.Xna.Framework;
using
Microsoft.Xna.Framework.Audio;
using
Microsoft.Xna.Framework.Content;
using
Microsoft.Xna.Framework.GamerServices;
using
Microsoft.Xna.Framework.Graphics;
using
Microsoft.Xna.Framework.Input;
using
Microsoft.Xna.Framework.Media;
using
Microsoft.Xna.Framework.Net;
using
Microsoft.Xna.Framework.Storage;


namespace
imparandoxna_3dmodello
{

///
/// This is a game component that implements IUpdateable.
///

public class FPSCamera : Microsoft.Xna.Framework.GameComponent
{


public
Matrix view { get; protected set; }
public
Matrix projection { get; protected set; }

public
Vector3 cameraPosition { get; protected set; }
Vector3 cameraDirection;
Vector3 cameraUp;

MouseState prevMouseState;
float
speed = 1.5f;


public
FPSCamera(Game game,Vector3 pos, Vector3 target, Vector3 up)
:
base(game)
{

//Inizializzo varaibili di posizione
cameraPosition = pos;
cameraDirection = target - pos;
cameraDirection.Normalize();
cameraUp = up;

//Rigenero la variabile view
FPSCreateLookAt();

//Inizializzo projection
projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.PiOver4,
(
float)Game.Window.ClientBounds.Width /
(
float)Game.Window.ClientBounds.Height,
1
, 3000);
}


public
override void Initialize()
{

// TODO: Add your initialization code here
Mouse.SetPosition(Game.Window.ClientBounds.Width / 2, Game.Window.ClientBounds.Height / 2);
prevMouseState = Mouse.GetState();
base.Initialize();
}


public
override void Update(GameTime gameTime)
{

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

Mouse.SetPosition(Game.Window.ClientBounds.Width / 2, Game.Window.ClientBounds.Height / 2);
prevMouseState = Mouse.GetState();
}


// Spostamento Aventi indietro
if (Keyboard.GetState().IsKeyDown(Keys.W)) {
cameraPosition += cameraDirection * speed;
}

if
(Keyboard.GetState().IsKeyDown(Keys.S))
{

cameraPosition -= cameraDirection * speed;
}


// Spostamento Destra Sinistra
if (Keyboard.GetState().IsKeyDown(Keys.A))
{

cameraPosition += Vector3.Cross(cameraUp,cameraDirection) * speed;
}

if
(Keyboard.GetState().IsKeyDown(Keys.D))
{

cameraPosition -= Vector3.Cross(cameraUp, cameraDirection) * speed;
}


//Rotazione sulla Y
cameraDirection = Vector3.Transform(cameraDirection,
Matrix.CreateFromAxisAngle(cameraUp, (-MathHelper.PiOver4 / 150) *
(
Mouse.GetState().X - prevMouseState.X)));

//Rotazione sulla Cross tra Direzione e UP
cameraDirection = Vector3.Transform(cameraDirection,
Matrix.CreateFromAxisAngle(Vector3.Cross(cameraUp,cameraDirection),
(-
MathHelper.PiOver4 / 150) *
(
prevMouseState.Y - Mouse.GetState().Y)));

cameraUp = Vector3.Transform(cameraUp,
Matrix.CreateFromAxisAngle(Vector3.Cross(cameraUp, cameraDirection),
(-
MathHelper.PiOver4 / 150) *
(
prevMouseState.Y - Mouse.GetState().Y)));

prevMouseState = Mouse.GetState();

//Rigenero la view
FPSCreateLookAt();

base.Update(gameTime);
}


public
void FPSCreateLookAt() {
view = Matrix.CreateLookAt(cameraPosition, cameraPosition + cameraDirection, cameraUp);
}
}
}
L'unica cosa che non vi ho mostrato prima è la funzione FPSCreateLookAt() che si occupa di chiamare la funziona createLookAt per ricreare la matrice view della camera. Questa funzione va richiamata ogni volta che si vuole modificare / aggiornare la posizione della camera.
Rispetto alal camera standard sono state estrapolate le variabili di posizione, direzione, up e sono state messe come variabili di classe in modo da poter essere aggioranbili di volta in volta dall'update.
Un'ultima cosa da notare è la presenza di una variabile speed... immaginate a cosa serve? :P
Mentre il resto del codice è quanto vi ho descritto sopra.

Ora ricordatevi di modificare le occorrenze della classe Camera in FPSCamera nel file game1.cs,
mentre nella classe model3d troverete giù un overloading della funzione Draw che accetta in ingresso una FPScamera invece che una Camera (le funzioni sono identiche cambia solo il parametro in ingresso).

Sorgenti
imparandoxna_3d.3.zip

6 commenti:

Anonimo ha detto...

Complimenti per i tutorial, sono molto dettagliati.
Continua così che vari forte.

Anonimo ha detto...

Vai cosi forte e ti interrompi proprio sul più bello?? vogliamo il seguitooo se mi dai il tuo indirizzo di posta elettronica vorrei chiederti una cosa...

Yari ( ImparandoXNA ) ha detto...

Ciao! sto facendo una pausa per problemi di lavoro :P
Avrei già un nuovo articolo sugli shader.. ma devo sistemarlo un pò prima.

Puoi scrivermi qui!

xna@fastwebnet.it

Anonimo ha detto...

Ciao pure io seguo i tuoi tuorial vai avnti ti prego sono interessantissimi :-P

Odino ha detto...

Ciao!
Ho scoperto solo ora questo blog e devo farti i complimenti!
Mi ha sempre interessato la programmazione con XNA a tal punto che mi sono comprato XNA game studio unleashed 3.0 (eh lo so sono andato dalla concorrenza invece che affidarmi alla o'really ^^)

Volevo scrivere anche io qualcosa su XNA ma sono ancora gli inizi, quindi mi sa che mentre studicchio il manule continuerò a seguirti!

Ancora complimenti per le guide!

Anonimo ha detto...

cazzo figa culo troia pompino rottinculo frocio barbara straisand se è figooooooooo!!!!!!!!!!!!

Posta un commento