Creare Videogiochi - Game Developer
Tutorial 7 - Collision - Versione stampabile

+- Creare Videogiochi - Game Developer (https://www.making-videogames.net/giochi)
+-- Forum: Altri Programmi per la Creazione di Videogames (https://www.making-videogames.net/giochi/Forum-Altri-Programmi-per-la-Creazione-di-Videogames)
+--- Forum: Irrlicht Engine (https://www.making-videogames.net/giochi/Forum-Irrlicht-Engine)
+--- Discussione: Tutorial 7 - Collision (/thread-Tutorial-7-Collision)



Tutorial 7 - Collision - Chip - 13-08-2015

Tutorial 7: Collision

[Immagine: 007shot.jpg]

Descriveremo 2 metodi: collisione automatica per muoversi attraverso ambienti 3d con scale da salire e scendere e poi il metodo manuale che prevede il picking dei triangoli tramite raggio. In questo caso useremo un raggio che uscirà dalla camera ma potrebbe essere un qualsiasi altro raggio.
Per cominciare partiremo dal sorgente usato nel tutorial 2 che carica una mappa di quake 3. Utilizzeremo proprio il livello per camminarci e per il picking (colpire) i suoi triangoli. In aggiunta piazzeremo 3 modelli animati sempre per il picking dei tirangoli. Il codice seguente avvia l'engine e carica il livello come per il tutorial 2.
Codice PHP:
#include <irrlicht.h>
#include "driverChoice.h"

using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

enum
{
    
// I use this ISceneNode ID to indicate a scene node that is
    // not pickable by getSceneNodeAndCollisionPointFromRay()
    
ID_IsNotPickable 0,

    
// I use this flag in ISceneNode IDs to indicate that the
    // scene node can be picked by ray selection.
    
IDFlag_IsPickable << 0,

    
// I use this flag in ISceneNode IDs to indicate that the
    // scene node can be highlighted.  In this example, the
    // homonids can be highlighted, but the level mesh can't.
    
IDFlag_IsHighlightable << 1
};

int main()
{
    
// ask user for driver
    
video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (
driverType==video::EDT_COUNT)
        return 
1;

    
// create device

    
IrrlichtDevice *device =
        
createDevice(driverTypecore::dimension2d<u32>(640480), 16false);

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

    
video::IVideoDriverdriver device->getVideoDriver();
    
scene::ISceneManagersmgr device->getSceneManager();

    
device->getFileSystem()->addFileArchive("../../media/map-20kdm2.pk3");

    
scene::IAnimatedMeshq3levelmesh smgr->getMesh("20kdm2.bsp");
    
scene::IMeshSceneNodeq3node 0;

    
// The Quake mesh is pickable, but doesn't get highlighted.
    
if (q3levelmesh)
        
q3node smgr->addOctreeSceneNode(q3levelmesh->getMesh(0), 0IDFlag_IsPickable); 
Alla fine abbiamo caricato la mappa di quake 3 come nel tutorial 2. Ora facciamo una cosa diversa: creiamo un selettore di triangoli. Si tratta di una classe che può catturare i singoli trinagoli presenti nei nodi di scena per farci varie cose come ad esempio la verifica delle collisioni. Ci sono differenti tipi di selettore di triangoli e vanno tutti creati a partire dal ISceneManager. In questo esempio andiamo a creare un OctreeTriangleSelector, usato anche per ottimizzare il disegno delle scene organizzando i trinagoli in un octree riducendone il numero. Questo è un metodo molto utile specie per le mesh molto grandi come i livelli di quake 3. Dopo aver creato un triangle selector, lo attacchiamo al node del livello q3node. Stavolta non droppiamo la risorsa appena utilizzata, anche perché abbiamo intenzione di riutilizzarla.
Codice PHP:
scene::ITriangleSelectorselector 0;

    if (
q3node)
    {
        
q3node->setPosition(core::vector3df(-1350,-130,-1400));

        
selector smgr->createOctreeTriangleSelector(
                
q3node->getMesh(), q3node128);
        
q3node->setTriangleSelector(selector);
        
// We're not done with this selector yet, so don't drop it.
    

Aggiungiamo una camera in stile FPS in modo da vedere e muoversi nel livello come accadeva nel tutorial 2. Stavolta però aggiungiamo alla camera uno speciale animator: un Collision Response animator. Questo genere di animator agisce sui nodi a cui è agganciato impedendo che attraversino i muri e applicandogli la gravità. L'unica cosa che dobbiamo dire all'animator è come sia fatto il mondo che lo circonda (la geometria che lo avvolge), quanto grande è la scena e quanta gravità deve applicare. Dopo che avremo applicato l'animator alla camera non dovremo fare altro, è tutto automatico. Il resto del codice per le collisioni è relativo al picking. Notate che l'animator che abbiamo agganciato alla camera può anche essere agganciato a qualsiasi altro nodo della scena. Possiamo anche mixarlo con altri nodi della scena. Usando questa tecnica le collisioni con Irrlicht sono piuttosto facili da realizzare.
Ora andremo ad osservare meglio ogni singolo parametro del createCollisionResponseAnimator(). Il primo parametro è il TriangleSelector, che specifica come interagisce il mondo circostante riguardo le collisioni. Il secondo parametro è il nodo di scena, cioè l'oggetto, che sarà interessato dalla collisione, nel nostro caso parliamo della camera. Il terzo parametro è la dimensione della collisione, per noi si tratta del raggio dell'ellissoide che lo circonda. Se provate a rifurre il raggio vedrete come la camerà uscirà ad avvicinarsi ai muri. Il prossimo parametro è la direzione e l'intensità della gravità. Andiamo a settare la gravità a (0, -10, 0), che approssima quella reale (9,81m/s), assumendo che le nostre unità siano metri. Per disabilitare la gravità basterà impostarla a (0,0,0). L'ultimo valore è una traslazione: senza di essa il nostro ellissoide per la collisione si troverebbe intorno alla camera ed essa starebbe esattamente in mezzo. Ma come sappiamo gli umani hanno gli occhi sopra il corpo e per simulare la collisione con il solo corpo. Quindi piazziamo il nodo a 50 unità sopra il centro dell'ellissoide. Questo è tutto, le collisioni ora ora funzionano.
Codice PHP:
// Set a jump speed of 3 units per second, which gives a fairly realistic jump
    // when used with the gravity of (0, -10, 0) in the collision response animator.
    
scene::ICameraSceneNodecamera =
        
smgr->addCameraSceneNodeFPS(0100.0f.3fID_IsNotPickable00true3.f);
    
camera->setPosition(core::vector3df(50,50,-60));
    
camera->setTarget(core::vector3df(-70,30,-60));

    if (
selector)
    {
        
scene::ISceneNodeAnimatoranim smgr->createCollisionResponseAnimator(
            
selectorcameracore::vector3df(30,50,30),
            
core::vector3df(0,-10,0), core::vector3df(0,30,0));
        
selector->drop(); // As soon as we're done with the selector, drop it.
        
camera->addAnimator(anim);
        
anim->drop();  // And likewise, drop the animator when we're done referring to it.
    
}

    
// Now I create three animated characters which we can pick, a dynamic light for
    // lighting them, and a billboard for drawing where we found an intersection.

    // First, let's get rid of the mouse cursor.  We'll use a billboard to show
    // what we're looking at.
    
device->getCursorControl()->setVisible(false);

    
// Add the billboard.
    
scene::IBillboardSceneNode bill smgr->addBillboardSceneNode();
    
bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
    
bill->setMaterialTexture(0driver->getTexture("../../media/particle.bmp"));
    
bill->setMaterialFlag(video::EMF_LIGHTINGfalse);
    
bill->setMaterialFlag(video::EMF_ZBUFFERfalse);
    
bill->setSize(core::dimension2d<f32>(20.0f20.0f));
    
bill->setID(ID_IsNotPickable); // This ensures that we don't accidentally ray-pick it 
Aggiungiamo 3 modelli animati che potremo colpire (picking) tramite una intersezione raggio contro triangolo. Sono animati al rallentatore in modo che si possa vedere l'accuratezza della selezione dei triangoli all'azione.
Codice PHP:
scene::IAnimatedMeshSceneNodenode 0;

    
video::SMaterial material;

    
// Add an MD2 node, which uses vertex-based animation.
    
node smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/faerie.md2"),
                        
0IDFlag_IsPickable IDFlag_IsHighlightable);
    
node->setPosition(core::vector3df(-90,-15,-140)); // Put its feet on the floor.
    
node->setScale(core::vector3df(1.6f)); // Make it appear realistically scaled
    
node->setMD2Animation(scene::EMAT_POINT);
    
node->setAnimationSpeed(20.f);
    
material.setTexture(0driver->getTexture("../../media/faerie2.bmp"));
    
material.Lighting true;
    
material.NormalizeNormals true;
    
node->getMaterial(0) = material;

    
// Now create a triangle selector for it.  The selector will know that it
    // is associated with an animated node, and will update itself as necessary.
    
selector smgr->createTriangleSelector(node);
    
node->setTriangleSelector(selector);
    
selector->drop(); // We're done with this selector, so drop it now.

    // And this B3D file uses skinned skeletal animation.
    
node smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/ninja.b3d"),
                        
0IDFlag_IsPickable IDFlag_IsHighlightable);
    
node->setScale(core::vector3df(10));
    
node->setPosition(core::vector3df(-75,-66,-80));
    
node->setRotation(core::vector3df(0,90,0));
    
node->setAnimationSpeed(8.f);
    
node->getMaterial(0).NormalizeNormals true;
    
node->getMaterial(0).Lighting true;
    
// Just do the same as we did above.
    
selector smgr->createTriangleSelector(node);
    
node->setTriangleSelector(selector);
    
selector->drop();

    
// This X files uses skeletal animation, but without skinning.
    
node smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/dwarf.x"),
                        
0IDFlag_IsPickable IDFlag_IsHighlightable);
    
node->setPosition(core::vector3df(-70,-66,-30)); // Put its feet on the floor.
    
node->setRotation(core::vector3df(0,-90,0)); // And turn it towards the camera.
    
node->setAnimationSpeed(20.f);
    
node->getMaterial(0).Lighting true;
    
selector smgr->createTriangleSelector(node);
    
node->setTriangleSelector(selector);
    
selector->drop();


    
// And this mdl file uses skinned skeletal animation.
    
node smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/yodan.mdl"),
                        
0IDFlag_IsPickable IDFlag_IsHighlightable);
    
node->setPosition(core::vector3df(-90,-25,20));
    
node->setScale(core::vector3df(0.8f));
    
node->getMaterial(0).Lighting true;
    
node->setAnimationSpeed(20.f);

    
// Just do the same as we did above.
    
selector smgr->createTriangleSelector(node);
    
node->setTriangleSelector(selector);
    
selector->drop();

    
material.setTexture(00);
    
material.Lighting false;

    
// Add a light, so that the unselected nodes aren't completely dark.
    
scene::ILightSceneNode light smgr->addLightSceneNode(0core::vector3df(-60,100,400),
        
video::SColorf(1.0f,1.0f,1.0f,1.0f), 600.0f);
    
light->setID(ID_IsNotPickable); // Make it an invalid target for selection.

    // Remember which scene node is highlighted
    
scene::ISceneNodehighlightedSceneNode 0;
    
scene::ISceneCollisionManagercollMan smgr->getSceneCollisionManager();
    
int lastFPS = -1;

    
// draw the selection triangle only as wireframe
    
material.Wireframe=true;

    while(
device->run())
    if (
device->isWindowActive())
    {
        
driver->beginScene(truetrue0);
        
smgr->drawAll();

        
// Unlight any currently highlighted scene node
        
if (highlightedSceneNode)
        {
            
highlightedSceneNode->setMaterialFlag(video::EMF_LIGHTINGtrue);
            
highlightedSceneNode 0;
        }

        
// All intersections in this example are done with a ray cast out from the camera to
        // a distance of 1000.  You can easily modify this to check (e.g.) a bullet
        // trajectory or a sword's position, or create a ray from a mouse click position using
        // ISceneCollisionManager::getRayFromScreenCoordinates()
        
core::line3d<f32ray;
        
ray.start camera->getPosition();
        
ray.end ray.start + (camera->getTarget() - ray.start).normalize() * 1000.0f;

        
// Tracks the current intersection point with the level or a mesh
        
core::vector3df intersection;
        
// Used to show with triangle has been hit
        
core::triangle3df hitTriangle;

        
// This call is all you need to perform ray/triangle collision on every scene node
        // that has a triangle selector, including the Quake level mesh.  It finds the nearest
        // collision point/triangle, and returns the scene node containing that point.
        // Irrlicht provides other types of selection, including ray/triangle selector,
        // ray/box and ellipse/triangle selector, plus associated helpers.
        // See the methods of ISceneCollisionManager
        
scene::ISceneNode selectedSceneNode =
            
collMan->getSceneNodeAndCollisionPointFromRay(
                    
ray,
                    
intersection// This will be the position of the collision
                    
hitTriangle// This will be the triangle hit in the collision
                    
IDFlag_IsPickable// This ensures that only nodes that we have
                            // set up to be pickable are considered
                    
0); // Check the entire scene (this is actually the implicit default)

        // If the ray hit anything, move the billboard to the collision position
        // and draw the triangle that was hit.
        
if(selectedSceneNode)
        {
            
bill->setPosition(intersection);

            
// We need to reset the transform before doing our own rendering.
            
driver->setTransform(video::ETS_WORLDcore::matrix4());
            
driver->setMaterial(material);
            
driver->draw3DTriangle(hitTrianglevideo::SColor(0,255,0,0));

            
// We can check the flags for the scene node that was hit to see if it should be
            // highlighted. The animated nodes can be highlighted, but not the Quake level mesh
            
if((selectedSceneNode->getID() & IDFlag_IsHighlightable) == IDFlag_IsHighlightable)
            {
                
highlightedSceneNode selectedSceneNode;

                
// Highlighting in this case means turning lighting OFF for this node,
                // which means that it will be drawn with full brightness.
                
highlightedSceneNode->setMaterialFlag(video::EMF_LIGHTINGfalse);
            }
        }

        
// We're all done drawing, so end the scene.
        
driver->endScene();

        
int fps driver->getFPS();

        if (
lastFPS != fps)
        {
            
core::stringw str L"Collision detection example - Irrlicht Engine [";
            
str += driver->getName();
            
str += "] FPS:";
            
str += fps;

            
device->setWindowCaption(str.c_str());
            
lastFPS fps;
        }
    }

    
device->drop();

    return 
0;


Versione in pdf scaricabile da QUI