3rd person, telecamere, collisioni & eventi da tastiera (3)

Attenzione: il tutorial pubblicato sul sito è stato suddiviso in 5 parti. Linee di codice troppo lunghe potrebbero essere tagliate dal browser. Per la completezza e l'affidabilità del codice fate riferimento al tutorial completo in pdf.

scarica il seguente tutorial in formato pdf (161 kb)

scarica il codice completo in formato testo (.cpp, 10.6 Kb)

COME TI RILEVO I TASTI:

Il metodo che voglio presentare è abbastanza standard ma presenta delle particolarità piuttosto interessanti e permette di evitare che il frame rate della tastiera produca un movimento “a singhiozzo” del personaggio (per capirci… provate a dare un’occhiata all’esempio del movimento fornito con Irrlicht).

Si tratta fondamentalmente di derivare dalla classe IeventReceiver una classe ed implementare il metodo OnEvent per gestire gli eventi.

Una volta verificato che si tratta di un evento di tipo EET_KEY_INPUT_EVENT, si utilizzerà un array bool di tanti elementi quanti sono i tasti di irrlicht e si “accenderà” (ponendo a true) il tasto (posizione dell’array corrispondente) in relazione al tasto premuto.

Difficile? Non troppo:

// Un array di bool lungo come il numero dei tasti "mappati" su Irrlicht

bool tasti[irr::KEY_KEY_CODES_COUNT];

class MyEventReceiver : public IEventReceiver

{

public:

virtual bool OnEvent(const SEvent &event)

// virtual bool OnEvent(SEvent &event) per c++ vc 6.0

{

/*

Bufferizza la pressione dei tasti

*/

if(event.EventType == irr::EET_KEY_INPUT_EVENT)

{

tasti[event.KeyInput.Key] = event.KeyInput.PressedDown;

}

return true;

 

}

};

Ricordiamoci poi, per una mera questione di pulizia, all’interno del main per esempio, di porre a “false” tutti i tasti, ovvero:

//Riempie gli array di "false —> tutti i tasti "off"

for(int x=0; x<irr::KEY_KEY_CODES_COUNT; x++) tasti[x] = false;

In fondo non sono altro che poche righe di codice. Tutto questo non basta però ad “animare” il nostro personaggio.

Occorrerà per tanto utilizzare una funzione (nel nostro caso) che verrà richiamata all’interno del ciclo principale del programma e che individuerà i tasti premuti effettuando:

  • il calcolo della nuova posizione/ rotazione sull’asse orizzontale del personaggio

  • il cambio del tipo di animazione della nostra mesh (nel nostro esempio si tratta di un file MD2), giusto per dare un tocco di realismo

  • nel caso non si stia premendo nulla, tornare in una animazione di quiete…

Prima di illustrare questa funzione, vediamo un po’ come è stato strutturato il main del nostro esempio…

IL CORPO DEL PROGRAMMA

int main()

{

MyEventReceiver receiver;

 

device = createDevice(video::EDT_DIRECT3D9, core::dimension2d<s32>(800, 600),

32, true, false, false, &receiver);

//Riempie gli array di "false —> tutti i tasti "off"

for(int x=0; x<irr::KEY_KEY_CODES_COUNT; x++) tasti[x] = false;

driver = device->getVideoDriver();

smgr = device->getSceneManager();

//Carico la scena (e il relativo triangle selector)

loadSceneData();

//Utilizzo la classe MyPlayer

Player = new MyPlayer(1,smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/sydney.md2"))) ;

Player->addTheCollisionAnimator(smgr->createCollisionResponseAnimator( mapSelector, Player->TheNode, core::vector3df(6,27,6), core::vector3df(0,-0.5f,0), core::vector3df(0,0,0)));

 

if (Player->TheNode!=0)

{

Player->TheNode->setMaterialFlag(video::EMF_LIGHTING, false);

Player->TheNode->setMD2Animation("stand");

Player->WhatIDo=1;

Player->TheNode->setAnimationSpeed(80);

Player->TheNode->setRotation(core::vector3df(0,0,0));

Player->TheNode->setMaterialTexture(0, driver->getTexture("../../media/sydney.bmp"));

 

}

//creo la followingCamera

g_cam = new followingCamera(Player->TheNode, smgr);

smgr->setActiveCamera( g_cam->getCam() );

device->getCursorControl()->setVisible(false);

/*

********************************************

* CICLO PRINCIPALE DEL PROGRAMMA *

********************************************/

while(device->run())

{

if(g_cam) g_cam->Update();

driver->beginScene(true, true, video::SColor(255,113,113,133));

MuoviTipa();

smgr->drawAll(); // draw the 3d scene

// device->getGUIEnvironment()->drawAll(); // draw the gui

driver->endScene();

if(g_cam) g_cam->Update();

}

device->drop();

if(g_cam) delete g_cam;

return 0;

}

Cerchiamo di spiegarlo per bene, almeno nei punti più interessanti.

Le prime operazioni effettuate sono quelle di creazione dell’oggetto receiver. Dopodiché lo passeremo alla nostra “device”:

MyEventReceiver receiver;

 

device = createDevice(video::EDT_DIRECT3D9, core::dimension2d<s32>(800, 600),

32, true, false, false, &receiver);

Nulla vi vieta di personalizzare la device come più vi aggrada.

Ora puliamo l’array dei tasti ponendoli tutti a false (tutti “non premuti”) e memorizziamo i puntatori a “driver” e “scenemanager” (ci saranno poi utili in seguito…o meglio… in tutti i tutorial che ho letto fin’ora fanno così e così faccio anche io :P ):

//Riempie gli array di "false —> tutti i tasti "off"

for(int x=0; x<irr::KEY_KEY_CODES_COUNT; x++) tasti[x] = false;

driver = device->getVideoDriver();

smgr = device->getSceneManager();

carichiamo la scena, ovvero la mappa BSP (vedi la parte precedente):

//Carico la scena (e il relativo triangle selector)

loadSceneData();

E finalmente ci occupiamo del personaggio:

//Utilizzo la classe MyPlayer

Player = new MyPlayer(1,smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/sydney.md2"))) ;

Player->addTheCollisionAnimator(smgr->createCollisionResponseAnimator( mapSelector, Player->TheNode, core::vector3df(6,27,6), core::vector3df(0,-0.5f,0), core::vector3df(0,0,0)));

 

if (Player->TheNode!=0)

{

Player->TheNode->setMaterialFlag(video::EMF_LIGHTING, false);

Player->TheNode->setMD2Animation("stand");

Player->WhatIDo=1;

Player->TheNode->setAnimationSpeed(80);

Player->TheNode->setRotation(core::vector3df(0,0,0));

Player->TheNode->setMaterialTexture(0, driver->getTexture("../../media/sydney.bmp"));

}

Abbiamo creato un oggetto Player, che avrà l’ID 1 (inutile come ho già spiegato) e caricherà la mesh della nostra splendida Sidney (personaggio presente negli esempi di Irrlicht).

Dopodiché abbiamo aggiunto al Player (o meglio al nostro nodo che contiene la mesh) un Collision Animator tramite il metodo “addTheCollisionAnimator”.

E qui bisogna fermarsi e ragionare sui diversi parametri passati a “createCollisionResponseAnimator”.

Difatti, passiamo il mapselector (il “triangle selector” della nostra mappa, ve lo ricordate?Viene creato nella “loadscenedata()”), il nodo della mesh del nostro Player (Player->TheNode), un vettore che indica le dimensioni dell’ellissoide che fungerà da “involcruo” per la nostra collisione, un vettore che specificherà una sorta di “forza di gravità” verso il basso (che farà cadere Sidney verso il basso) e infine un vettore “nullo” che sta ad identificare la traslazione dell’elissoide sui diversi assi (nel nostro caso non ci interessa minimamente).

Importante per non essere salami come me: ricordatevi sempre di definire il “triangle selector” della mappa (il nostro mapselector, per intenderci) PRIMA di creare il “Response Animator” del personaggio ed assegnarlo al nodo… questo lo dico perché se non avessi ricevuto aiuto sul forum sarei ancora lì a piangere…

Di seguito vengono poi definite le caratteristiche del nodo (la mesh… vedetela come “la rappresentazione grafica” del nostro player, giusto per comodità mentale), specificando animazione di partenza, texture, velocità di animazione e tutto quanto ci serve.

Infine, l’ultima parte del programma principale:

//creo la followingCamera

g_cam = new followingCamera(Player->TheNode, smgr);

smgr->setActiveCamera( g_cam->getCam() );

device->getCursorControl()->setVisible(false);

/*

********************************************

* CICLO PRINCIPALE DEL PROGRAMMA *

********************************************/

while(device->run())

{

if(g_cam) g_cam->Update();

driver->beginScene(true, true, video::SColor(255,113,113,133));

MuoviTipa();

smgr->drawAll(); // draw the 3d scene

// device->getGUIEnvironment()->drawAll(); // draw the gui

driver->endScene();

if(g_cam) g_cam->Update();

}

device->drop();

if(g_cam) delete g_cam;

return 0;

}

A parte la creazione della nostra classe telecamera (che punta al nodo del nostro Player), la sua attivazione come camera attiva e la sua cancellazione all’uscita del ciclo while (uscita dal programma), non c’è nulla di differente dagli altri esempi di irrlicht… tranne per quella curiosa procedura, l’ultima che ci rimane da vedere, che serve a fare proprio quello che dice…

 


5 commenti per “3rd person, telecamere, collisioni & eventi da tastiera (3)”

  1. Gaetano ha scritto:

    Salve,
    Ho cpiato paro paro il codice dal file che c’è nel post, l’ho compilato senza problemi ma appena provo ad eeseguire l’eseguibile, l’applicazione crasha, ho eseguito il debag con dev-c++ è mi dà questo messaggio:”il programma ha causato una violazione di accesso (errore di segmentazione)”, da premettere che non ho toccato nulla, solo solo fatto copia e incolla dal file cpp allegato a questo post.
    dove sbaglio?

  2. Gaetano ha scritto:

    Piccola aggiunta.
    Il debug mi dice che alla line 267 dove c’è questa istruzione:”driver = device->getVideoDriver();” mi dice:”KEY_KEY_CODES_COUNT= Could not watch this variable”

  3. webmaster ha scritto:

    quale versione di Irrlicht stai usando?
    hai provato a scaricare il codice .cpp allegato? anche con quello ti da errore?

  4. mestoppespert ha scritto:

    I’d prefer reading in my native language, because my knowledge of your languange is no so well. But it was interesting! Look for some my links:

  5. mestoppespert ha scritto:

    I’d prefer reading in my native language, because my knowledge of your languange is no so well.

Lascia un commento