• Benvenuto in Making Videogames!
  • Dai sfogo alla tua fantasia!
  • Crea il tuo Videogioco!
Benvenuto ospite! Login Registrati




Valutazione discussione:
  • 1 voto(i) - 5 media
  • 1
  • 2
  • 3
  • 4
  • 5
Tutorial 4 - Movement
#1
Tutorial 4: Movement

[Immagine: 004shot.jpg]

Questo tutorial spiega come muovere e animare un qualsiasi SceneNode. Mostreremo i concetti base degli SceneNodeAnimators come il movimento dei nodi tramite la tastiera. Verrà mostrato come rendere i movimenti indipendenti dal framerate, cioè come muovere gli oggetti in base al tempo che è intercorso dall'ultimo loop scenico di Irrlicht.
L'esempio 19. MouseAndJoystick mostra come utilizzare questo genere di input.
Come sempre iniziamo mostrando l'header dove si utilizza il namespace irr e diamo indicazioni al linker di inserire la libreria irrlicht.lib.
Codice PHP:
#ifdef _MSC_VER
// We'll also define this to stop MSVC complaining about sprintf().
#define _CRT_SECURE_NO_WARNINGS
#pragma comment(lib, "Irrlicht.lib")
#endif

#include <irrlicht.h>
#include "driverChoice.h"

using namespace irr
Per catturare eventi proveniente da mouse, tastiera o dalla GUI come “il tasto OK è stato premuto” ci serve un oggetto del tipo irr::IEventReceiver , servirà sovrascrivere (overriding) un solo metodo irr::IEventReceiver::OnEvent(). Questo metodo verrà richiamato dall'engine ogni qual volta un evento verrà catturato. Quello che ci serve veramente sapere, per il nostro esempio, è quando un tasto è premuto in modo da ricordarci lo stato di ciascun tasto.
Codice PHP:
class MyEventReceiver : public IEventReceiver
{
public:
    
// This is the one method that we have to implement
    
virtual bool OnEvent(const SEventevent)
    {
        
// Remember whether each key is down or up
        
if (event.EventType == irr::EET_KEY_INPUT_EVENT)
            
KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
        return 
false;
    }

    
// This is used to check whether a key is being held down
    
virtual bool IsKeyDown(EKEY_CODE keyCode) const
    {
        return 
KeyIsDown[keyCode];
    }
    
    
MyEventReceiver()
    {
        for (
u32 i=0i<KEY_KEY_CODES_COUNT; ++i)
            
KeyIsDown[i] = false;
    }

private:
    
// We use this array to store the current state of each key
    
bool KeyIsDown[KEY_KEY_CODES_COUNT];
}; 
Nel nostro oggetto sovrascriviamo il metodo OnEvent() salvandoci tutte le volte che un tasto è premuto (EET_KEY_INPUT_EVENT) in un array di booleani KeyIsDown[] con il codice del tasto premuto, il max è dato dal KEY_KEY_CODES_COUNT. Creiamo anche un metodo che serve a indicarci se quel determinato tasto risulta premuto, in input il parametro col codice del tasto, in output lo stato indicato da quel indice nel nostro array. Il metodo MyEventReceiver() azzera tutto l'array per il nuovo ciclo.

L' event receiver è pronto, le risposte agli eventi verranno gestite dentro al loop di rendering poco prima del disegno della scena. Quindi andiamo a creare un irr::IrrlichtDevice e il nodo nella scena che vogliamo muovere. Creiamo anche altri nodi di scena per mostrare ulteriori movimenti e animazioni.
Codice PHP:
int main()
{
    
// ask user for driver
    
video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (
driverType==video::EDT_COUNT)
        return 
1;

    
// create device
    
MyEventReceiver receiver;

    
IrrlichtDevicedevice createDevice(driverType,
            
core::dimension2d<u32>(640480), 16falsefalsefalse, &receiver);

    if (
device == 0)
        return 
1// could not create selected driver.

    
video::IVideoDriverdriver device->getVideoDriver();
    
scene::ISceneManagersmgr device->getSceneManager(); 
Creiamo il nodo che sarà mosso con i tasti WASD. Creiamo una sfera come nodo utilizzando le primitive fornite da Irrlicht. Piazziamo il nodo a (0,0,30) e assegniamogli una texture per renderlo un po' più interessante. Poiché non prevediamo di utilizzare luci dinamiche andiamo a disabilitare l'illuminazione di ciascun modello che altrimenti verrebbe mostrato scuro.
Codice PHP:
scene::ISceneNode node smgr->addSphereScenNode();
    if (
node)
    {
        
node->setPosition(core::vector3df(0,0,30));
        
node->setMaterialTexture(0driver->getTexture("../../media/wall.bmp"));
        
node->setMaterialFlag(video::EMF_LIGHTINGfalse);
    } 
Ora creiamo un altro oggetto nodo da muovere usando un nodo animator. I nodi animators possono essere agganciati a ciascun nodo di scena come mesh, billboards, luci e anche una camera; tutti nodi di scena. I nodi di scena di tipo animators non sono adatti solo a cambiare la posizione degli altri nodi di scena ma possono anche gestire l'animazione delle texture negli oggetti stessi. Così creiamo un altro nodo di scena, stavolta un cubo e attacchiamogli un animator di tipo “fly circle” (volo circolare), così vedremo il cubo ruotare intorno alla sfera.
Codice PHP:
scene::ISceneNodesmgr->addCubeSceneNode();
    if (
n)
    {
        
n->setMaterialTexture(0driver->getTexture("../../media/t351sml.jpg"));
        
n->setMaterialFlag(video::EMF_LIGHTINGfalse);
        
scene::ISceneNodeAnimatoranim =
            
smgr->createFlyCircleAnimator(core::vector3df(0,0,30), 20.0f);
        if (
anim)
        {
            
n->addAnimator(anim);
            
anim->drop();
        }
    } 
L'ultimo nodo di scena che aggiungeremo è un modello, un file b3d (formato interno di Blitz3D). A questo applicheremo un animator di tipo “fly straight” (volo attraverso) che muove l'oggetto da un punto all'altro e viceversa.
Codice PHP:
scene::IAnimatedMeshSceneNodeanms =
     
smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/ninja.b3d"));

    if (
anms)
    {
        
scene::ISceneNodeAnimatoranim =
            
smgr->createFlyStraightAnimator(core::vector3df(100,0,60),
            
core::vector3df(-100,0,60), 3500true);
        if (
anim)
        {
            
anms->addAnimator(anim);
            
anim->drop();
        } 
Per mostrare correttamente il modello disabilitiamo l'illuminazione, settiamo i frames esatti per indicare quale animazione vogliamo loopare, ruotiamolo di 180 gradi, aggiustiamo la velocità dell'animazione e indichiamo la texture per il modello. Per impostare l'animazione giusta dei modelli esistebbe anche “anms->setMD2Animation(scene::EMAT_RUN)" da usarsi al posto di “setFrameLoop” e “setAnimationSpeed” ma funziona solo con i modelli MD2 (all'interno dei quali si trovano le animazioni preimpostate a determinate chiavi da richiamare). Ora sappiamo come utilizzare le animazioni richiamandole dal codice frame per frame (dallo 0 al 13 nell'esempio) anche se questa è una soluzione da evitare (meglio renderle indipendenti senza usare numeri espliciti, come accade negli MD2).
Codice PHP:
anms->setMaterialFlag(video::EMF_LIGHTINGfalse);
        
anms->setFrameLoop(013);
        
anms->setAnimationSpeed(15);
//      anms->setMD2Animation(scene::EMAT_RUN);

        
anms->setScale(core::vector3df(2.f,2.f,2.f));
        
anms->setRotation(core::vector3df(0,-90,0));
//      anms->setMaterialTexture(0, driver->getTexture("../../media/sydney.bmp"));
    

Per permetterci di guardare intorno a noi nella scena creiamo una camera in stile FPS e rendiamo invisibile il puntatore del mouse.
Codice PHP:
smgr->addCameraSceneNodeFPS()
   
device->getCursorControl()->setVisible(false); 

Aggiungiamo un logo di Irrlicht
Codice PHP:
device->getGUIEnvironment()->addImage(
         
driver->getTexture("../../media/irrlichtlogoalpha2.tga"),
        
core::position2d<s32>(10,20));

    
gui::IGUIStaticTextdiagnostics device->getGUIEnvironment()->addStaticText(
        
L""core::rect<s32>(101040020));
    
diagnostics->setOverrideColor(video::SColor(2552552550)); 
Abbiamo fatto tutto, ora disegniamo la scena. Adiamo anche a scrivere a schermo il conteggio dei frame per secondo e il nome del driver sul titolo della finestra (*).
Codice PHP:
int lastFPS = -1;
    
// In order to do framerate independent movement, we have to know
    // how long it was since the last frame
    
u32 then device->getTimer()->getTime();

    
// This is the movemen speed in units per second.
    
const f32 MOVEMENT_SPEED 5.f;

    while(
device->run())
    {
        
// Work out a frame delta time.
        
const u32 now device->getTimer()->getTime();
        const 
f32 frameDeltaTime = (f32)(now then) / 1000.f// Time in seconds
        
then now
Controlliamo la pressione dei tasti WASD e muoviamo il resto dei nodi oggetto.
Codice PHP:
core::vector3df nodePosition node->getPosition();
        if(
receiver.IsKeyDown(irr::KEY_KEY_W))
            
nodePosition.+= MOVEMENT_SPEED frameDeltaTime;
        else if(
receiver.IsKeyDown(irr::KEY_KEY_S))
            
nodePosition.-= MOVEMENT_SPEED frameDeltaTime;

        if(
receiver.IsKeyDown(irr::KEY_KEY_A))
            
nodePosition.-= MOVEMENT_SPEED frameDeltaTime;
        else if(
receiver.IsKeyDown(irr::KEY_KEY_D))
            
nodePosition.+= MOVEMENT_SPEED frameDeltaTime;

        
node->setPosition(nodePosition);
        
driver->beginScene(truetruevideo::SColor(255,113,113,133));
        
smgr->drawAll(); // draw the 3d scene
       
device->getGUIEnvironment()->drawAll(); // draw the gui environment (the logo)
        
driver->endScene();
        
int fps driver->getFPS();
        if (
lastFPS != fps)
        {
            
core::stringw tmp(L"Movement Example - Irrlicht Engine [");
            
tmp += driver->getName();
            
tmp += L"] fps: ";
            
tmp += fps;

            
device->setWindowCaption(tmp.c_str());
            
lastFPS fps;
        }
    } 
Come sempre alla fine cancelliamo il device principale.
Codice PHP:
device->drop();    
    return 
0;

E' tutto compiliamo e avviamo il programma.

(*) In realtà in questo tutorial l'autore aveva detto che avrebbe spiegato come rendere i movimenti indipendenti dal frame rate, peccato che non lo spieghi limitandosi al solo codice scritto. Vediamo noi come si fa.
Per contare gli FPS basta contarli ogni volta usando la variabile lastFPS e il metodo getFPS() per aggiornarla.
Per il deltaTime occorre conoscere il tempo intercorso tra un frame renderizzato e il precedente. Lo facciamo usando device->getTimer->getTime() preso subito (“now”) e dopo il rendering (“then”). Stabiliamo che il movimento sarà di 5 unità al secondo per tutto (MOVEMENT_SPEED = 5.f).
Fuori loop prendiamo la prima volta il tempo in millisecondi:
u32 then = device->getTimer()->getTime();

Inziamo il loop:
Salviamo in now l'orario in millisecondi.
const u32 now = device->getTimer()->getTime();
Quindi calcomiamo il delta per correggere i nostri movimenti
const f32 frameDeltaTime = (f32)(now - then) / 1000.f; // Time in seconds
Poi ci riconduciamo alla condizione iniziale per il prossimo calcolo ponendo "then" che parte dall'ultimo now ricordato:
then = now;

Ora ogni movimento di MOVEMENT_SPEED andrà corretto moltiplicandolo per frameDeltaTime.
if(receiver.IsKeyDown(irr::KEY_KEY_W))
nodePosition.Y += MOVEMENT_SPEED * frameDeltaTime;
...

Immaginiamo che tra il frame A (then=1439068224270 millisec) e il B (now=1439068224290 millisec) vengano impiegati 20millisendi (cioè una media di 50FPS); abbiamo un delta di 0,02, per andare a 5 unità al secondo la velocità dovrà essere 5.f * 0,02.f = 0,1f da muovere ogni singolo frame.
Per convincerne immaginiamo gli FPS costanti, sempre 50FPS, in tre secondi l'oggetto si sarà spostato fino a 15 unità lontano e dovrà farlo in 50frame x 3 secondi pari a 150 frames, per ciascun scatto o frame andrà avanti di 0,1 unità, che alla fine saranno proprio 150*0,1=15 unità.
Se tra il farme B e il C passassero 15millisecondi (66,66FPS quindi renderizziamo più veloci) allora la velocità corretta dovrà ridursi, infatti avremo un delta di 0,015.f

Versione pdf da scaricare QUI
 
Rispondi
#2
Ok, cosi' mi viene voglia di rincominciare con irrlicht Big Grin
 
Rispondi
#3
Mi incuriosisce questo Irrlicht, dovrei provare a fare qualcosa, grazie per il tutorial Chip Wink
 
Rispondi
#4
Ahah! Consiglio sempre, una volta trovato un ambiente stabile per la compilazione (il nuovo devcpp, vs2013,code::block) di copiare questi tutorials e smanettarci. Ancora meglio gli esempi del sorgente. Esiste un libro che era allegato con un vecchio numero di IoProgrammo e una pubblicaziobe della Packt sul Irrlicht1.7
Poi individuate un buon editor per le scene per non impazzire, che sia IrrEdit (ora inglobato nel CopperCube) Deled o Blender con opportuni script di estrazione o tanti tra i vari BSPbuilder come gtkradiant.
 
Rispondi
#5
Qualcuno tra voi ha ancora le traduzioni in pdf dei primi due tutorials?
 
Rispondi
  


Vai al forum:


Browsing: 2 Ospite(i)