Collisions with relief

Hi, I am new on panda3d and thus I am French so sorry for the faults. I have a problem, because i’m french i don’t really understand the differents tutorials about collision in panda3d. I already did the collision between 2 collisions solids but i want to make collision between a collision sphere and all the model of my world, i saw this on the ralph sample but i totaly don’t understand how he did it. If someone is french here, please help me with fench words :slight_smile: else if you have the patience to help me, do it :stuck_out_tongue:

Bonjour, je suis disponible pour porter assistace.

Est-ce possible de me dire quel section du tutoriel donne problème? Sinon lorsque le je pourai, j’étudierai le cas de roaming ralf et tenterai de le résumer

Bon, moi aussi je suis relativement nouveau :p, donc je vais faire mon possible.

J’ai regardé le cas de Roaming Ralph. De la façon que ça fonctionne, il y a utlisation d’un raycast. Prenons juste le cas de la position de ralph (la caméra se positionne essentiellement de la même façon)

self.cTrav = CollisionTraverser()
self.ralphGroundRay = CollisionRay()
self.ralphGroundRay.setOrigin(0, 0, 9)
self.ralphGroundRay.setDirection(0, 0, -1)
self.ralphGroundCol = CollisionNode('ralphRay')
self.ralphGroundCol.addSolid(self.ralphGroundRay)
self.ralphGroundCol.setFromCollideMask(CollideMask.bit(0))
self.ralphGroundCol.setIntoCollideMask(CollideMask.allOff())
self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol)
self.ralphGroundHandler = CollisionHandlerQueue()
self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler)

self.ralphGroundRay est un raycast. Donc c’est un peu comme un ‘‘scaner’’ linéaire. Il part du point d’origine (9 de haut), et trace une ligne droite dans la direction donné, dans ce cas, -1, donc vers le bas. Lorsque ce traceur touche une ‘‘mesh’’ de colision, il le rajoute dans une liste (self.ralphGroundHandler). Plus loins dans le code, il y a

entries = list(self.ralphGroundHandler.getEntries())
entries.sort(key=lambda x: x.getSurfacePoint(render).getZ())

Ainsi, ‘‘entries’’ consiste en l’ensemble des collision et ils sont classé par ordre de distance en Z (on s’intéresse seulement a la colission avec la mesh la plus proche. Avec le code :

if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain":
    self.ralph.setZ(entries[0].getSurfacePoint(render).getZ())
else:
    self.ralph.setPos(startpos)

On vérifie l’’‘entries’’ la plus proche en Z, c’est a dire entries[0], et on va chercher son nom. Si c’est un terrain, on dit de continuer d’avancer et de rajuster la hauteur de Ralph selon la hauteur du terrain détecté, sinon on dit de rester sur place (colission).

Merci pour ta réponse, cependant je reste perdu avec les fonctions utilisées pour gérer ces collisions, pourrais tu m’expliquer en détail l’utilité de chacune s’il te plait.

self.cTrav = CollisionTraverser()

C’est “l’outil” qui permet de gérer les groupe de colission, il sert plus tard

self.ralphGroundRay = CollisionRay()

C’est une instance du détecteur de collision, c’est comme une flèche, qui enregistre tout ce qu’elle touche, cependant, elle n’est pas orienté et placée. Donc:

self.ralphGroundRay.setOrigin(0, 0, 9)      #Met la fleche a la position 0,0,9
self.ralphGroundRay.setDirection(0, 0, -1)       #la fait pointer vers le bas.
self.ralphGroundCol = CollisionNode('ralphRay')

est un Node qui contient l’objet permettant les collisions, c’est la ‘‘poignée’’ de ralphGroundRay, que l’on viens d’ailleur rattaché ensemble par:

self.ralphGroundCol.addSolid(self.ralphGroundRay)

Donc nous avons maintenant ralphGroundCol qui contient ralphGroundRay qui est une Node de collision, pointant vers le bas. Ensuite, il fait lui dire avec quoi elle doit rentrer en contact. (On ne veut pas toujours que tout rentre en collision avec tout, donc l’on met ce qui s’appelle des mask.

self.ralphGroundCol.setFromCollideMask(CollideMask.bit(0))

Dit que ralphGroundCol est considéré comme un mask bit(0) (ici on peut ecrire n’importe quoi, mais c’est bien d’habitude de commencer a 0 dans nos mask et incrementer par la suite.

self.ralphGroundCol.setIntoCollideMask(CollideMask.allOff())

Ca dit de ne pas masquer ce dans quoi il peut faire un collision, ainsi il peut rentrer en collision avec tout.

Donc maintenant notre ralphGroundCol appartient a la categorie 0, peut rentrer en collision avec tout et contient une fleche de collision CollisionRay.

Ensuite on ratache le tout dans le Node de ralph (permet de les lié ensemble):

self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol)

Maintenant, panda3D a tout ce qu’il faut pour faire les collissions, mais il n’y a rien qui ‘‘écoute’’,donc:

self.ralphGroundHandler = CollisionHandlerQueue()

Est une ‘‘liste’’ de ces collision, présentement vide, et avec rien pour la remplir. Pour cela, on utilise:

self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler)

Qui dit de rajouter les collision de ralphGroundColNp dans la liste ralphGroundHandler

On peut visionner les collision grace a

self.ralphGroundColNp.show()

Maintenant, tout les collision seront enregistrées dans ralphGroundHandler, et il nous reste a ce servir de cela comme on veut! dans le cas présent, on veut juste s’assurer que ralph reste sur le sol et non dans le sol. Donc on fait un trie dans la liste afin de tout classer en fonction de la la hauteur Z

entries = list(self.ralphGroundHandler.getEntries())      #Sort la liste de collision de ralphGroundHandler
entries.sort(key=lambda x: x.getSurfacePoint(render).getZ())  #reclasse les entries en fonction de Z. C'est un outil de python, pas panda3D

Donc, on a une liste de tout ce qui a rentrer en collision avec notre ‘‘flèche’’, et elle est classée en fonction de la hauteur Z. Comme on se fout de tout sauf la collision la plus haute (On marche sur le sol, donc que notre flèche de collission frappe un sous-terrain (car la fleche continue a l’infinie), ca ne change rien, on s’intérêsse toujours seulement a la première collission rapportée, donc la plus haute (d’ou le trie en Z).

Ensuite on fait

if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain":
    self.ralph.setZ(entries[0].getSurfacePoint(render).getZ())
else:
    self.ralph.setPos(startpos)

len(entries) > 0 regarde si il y a des collission, si oui, avec entries[0].getIntoNode().getName() == “terrain” on test si ce dans quoi on viens de frapper s’appelle terrain (Le nom de ca est donné dans le fichier egg)
Dans le cas ou effectivement il y a collision et qu’elle est avec le sol, on fait self.ralph.setZ(entries[0].getSurfacePoint(render).getZ()), où self.ralph.setZ() permet de mettre ralph a la hauteur, entries[0] représente la première entries de la liste, donc la plus haute et .getSurfacePoint(render).getZ() donne la position Z du point de contact avec la surface.

Dans le cas ou non, pas de collision ou que la collision n’étais pas avec le sol, on remet ralph à sa position initiale grace à startpos qui est = à self.ralph.getPos()

Désolé si ma grammaire est dégeulasse et qu’il y a un million de typo, j’avais peu de temps pour écrire cette réponse :stuck_out_tongue:

En espérant t’avoir éclairé!