Sviluppare un gioco in Python: Panda3D – collisioni

Introduzione

Con questo articolo concludiamo il nostro breve tour nel mondo di Panda3d. Purtroppo lo facciamo trattando molto superficialmente un tema spinoso e vasto, ovvero le collisioni.

In questo esempio vedremo semplicemente come individuare delle collisioni, visualizzandole nella nostra scena.

Collisioni

L’individuazione delle collisioni è il primo passo per far sì che due oggetti urtino e reagiscano all’evento.

Ci sono due tipi di collisioni (o modalità) che si possono individuare:

  1. Creare una speciale geometria per la collisione di un particolare oggetto, come una sfera, un cilindro, un cubo o anche un insieme di essi.
  2. Permettere le collisioni tra qualsiasi tipo di geometria (quindi si prende in considerazione l’oggetto stesso).

Mentre la prima opzione comporta un notevole impegno da parte del programmatore, che sostanzialmente deve costruire un poligono simile all’oggetto da far collidere e gestire queste collisioni geometriche, la seconda non ha nessun lavoro di questo tipo, visto che si prende l’oggetto così com’è.

Per applicazioni che non puntano alle prestazioni (per qualsiasi genere di motivo, anche per scelta del programmatore) e per costruire qualcosa velocemente, la seconda soluzione è quella più indicata.

La prima ha invece il vantaggio di essere molto più veloce della seconda, nonché più mantenibile nel tempo.

In questa demo vedremo un esempio di collisione tra solidi (CollisionSolid), visto che è fondamentale in un sistema di collisioni.

Questi solidi rappresentano una geometria speciale che viene creata solamente per gestire i test di collisione e vengono memorizzati nello scene graph a fianco dei poligoni visibili normalmente.

Queste geometrie (normalmente “invisibili”) sono ottimizzate per eseguire i test di collisione molto velocemente, diciamo anche istantaneamente (per rendere l’idea), cosa che con i poligoni inseriti da noi (che possono avere geometrie più complesse) non accade.

Quando si crea un solido di collisione dobbiamo associarlo ad un nodo di collisione (CollisionNode), a meno che non importiamo il solido tramite lo stesso file egg o bam; in tal caso, il CollisionNode verrà creato automaticamente per noi.

Ora abbiamo le basi per comprendere l’esempio di questo articolo. Questa volta non posterò tutto il codice, visto che prendiamo come punto di riferimento quello dell’articolo precedente. Metterò invece solo le differenze ed il solito link dei sorgenti a fine pagina.

Codice

        self.colNode1 = CollisionNode("colNode1")
        colSphere1 = CollisionSphere(0,7,3.5,2)
        self.colNode1.addSolid(colSphere1)
        self.colNP1 = render.attachNewNode(self.colNode1)
        self.colNP1.show()

        self.colNode2 = CollisionNode("colNode2")
        colSphere2 = CollisionSphere(5,15,5,1)
        self.colNode2.addSolid(colSphere2)
        self.colNP2 = render.attachNewNode(self.colNode2)
        self.colNP2.show()

        self.colNode3 = CollisionNode("colNode3")
        colSphere3 = CollisionSphere(7,15,5,1)
        self.colNode2.addSolid(colSphere3)
        self.colNP3 = render.attachNewNode(self.colNode3)
        self.colNP3.show()

        self.cTrav = CollisionTraverser()
        self.cTrav.showCollisions(self.render)
        self.cHan = CollisionHandlerQueue()
        self.cTrav.addCollider(self.colNP1, self.cHan)

        taskMgr.add(self.checkCollisions, "Collisions")

    def checkCollisions(self, task):
        dt = globalClock.getDt()
        self.cTrav.traverse(self.render)
        if (self.cHan.getNumEntries() > 0):
            self.gestioneGas("down",dt)
        print(self.cHan.getNumEntries())
        return task.again

    def move(self, dt):
        mps = self.velocita * 1000 / 3600
        self.oggetto2.setY(self.oggetto2, mps * dt)
        #Sfera collisioni
        self.colNP1.setPos(self.oggetto2, Vec3(0,-7,-2))

    def gira(self, dir, dt):
        tempGiro = self.gestGira * (3 -(self.velocita / self.maxVel))
        if(dir == "r"):
            tempGiro = -tempGiro
        self.oggetto2.setH(self.oggetto2, tempGiro * dt)
        #Sfera collisioni
        self.colNP1.setH(self.colNP1, tempGiro * dt)

Analisi

E’ possibile inserire questa parte di codice dopo la creazione del musicManager e del pulsante. Potete anche semplicemente scaricare i sorgenti.

Il codice non è di difficile lettura. Per prima cosa creiamo tre sfere per gestire le collisioni, la prima delle quali viene posizionata sopra il nostro carosello (linea 2). Per ogni sfera, creiamo un CollisionNode e lo associamo. Inoltre ci preoccupiamo che le sfere siano visualizzate sullo schermo con il comando show (linee 5,11,17).

Alla linea 21 creiamo una coda per il gestore delle collisioni, mentre alla linea 19 generiamo il più semplice gestore di collisioni : CollisionTraverser. Quest’ultimo tiene conto solamente delle collisioni che sono state rivelate durante l’ultimo “attraversamento”. Queste possono essere iterate utilizzando queue.getNumEntries() che restituisce il numero di collisioni totali avvenute. In questo caso tracceremo le collisioni del carosello, visto che aggiungiamo solo lui al gestore (linea 22). Il comando della linea 20 serve per visualizzare il punto di attraversamento durante la collisione.

Fatto questo creiamo un task che decellera il nostro carosello quando avviene una collisione con una delle altre due sfere presenti sulla scena. Da notare che le collisioni tra le due sfere non vengono calcolate, ma solo quelle tra la sfera del carosello e le altre sfere.

Sul terminale verrà stampato il numero di sfere con cui collide il carosello (linea 31).

Per far muovere la sfera con il carosello sono state aggiunte le linee 38 e 46 alle funzioni move e gira, così da rimanere incollata all’oggetto.

Conclusioni

Per quanto sia difficile questo campo, il codice risulta comunque pulito e di facile lettura. Purtroppo rimane comunque un esempio molto didattico di quello che si potrebbe fare con Panda3d. Infatti stiamo utilizzando delle “primitive che ci consentono di costruire delle semplici collisioni (o anche un semplice motore fisico). Ma Panda3d permette molto più di questo integrando come base la famosa libreria open source ODE e permette anche l’utilizzo di Bullet o Physx, o anche tutte e tre contemporaneamente (ma geometrie di collisione di physics engine differenti non potranno reagire tra loro, visto che fanno parte di due simulazioni diverse).

Spero di non avervi annoiato troppo con questo tour all’interno di Panda3d, ma è uno dei migliori motori di gioco utilizzabili in python (se non l’unico così completo) e con molte sfaccettature, mi sembrava doveroso presentare almeno gli aspetti di base.

Di seguito rilascio i sorgenti di questa ultima demo.

http://tinyurl.com/6tc23df

Press ESC to close