Collisions are tricky in Panda3D because, at it's most basic, Panda3D is a renderer - it displays things. They actually have no substance at all, and in fact can run through each other with ease.
You need to do a lot of extra things to use collisions.
Note that this is the relatively simplistic way to detect collisions - there is also the Panda3D Physics Engine that you can use, although it's much more complicated.
Collision Nodes and Meshes
To allow Panda3D to detect collisions, you need to attach something called a collision node and a collision mesh to your insubstantial object. Think of it as putting on an invisible shell over your object that makes it solid instead of a ghost. The collision mesh will have it's own shape - most of the time, you're going to be adding a shell the same size and shape as the object, but you can make it bigger/smaller in relative to the object for further away / closer 'collision' detection. Panda3D can only detect collisions on the collision nodes/meshes, so the actual shape of the object is not relevant to the collision.
Note that you need to already have loaded your Blender model into Panda3D.
First, lets import some stuff:
from pandac.PandaModules import CollisionHandlerQueue from pandac.PandaModules import CollisionNode from pandac.PandaModules import CollisionSphere from pandac.PandaModules import CollisionTraverser from pandac.PandaModules import BitMask32
There are a lot of built in collision node shapes, like the
CollisionSphere node that is imported here. You can view the others on the website and import them.
Generally, there is an object or two that you want to see if it will collide into other things. These objects need a collision node and a collision mask. All other objects just need a collision mask, and not a collision node.
Lets start with putting the
CollisionSphere on the model that needs a
self.modelSphere = CollisionSphere(0, 0, 0, radius)
This creates a collision sphere at location (0, 0, 0) with a certain radius that you can set. This your mesh. Now, we want to use a
CollisionNode to attach it to the object.
self.modelCollisionNode = CollisionNode('model')
Where 'model' is just a label.
This puts the mesh and the node together.
The next step's explanation is a little in depth for 112, but you need to set some collision masks. Don't really worry about this, but you should include these lines:
Now, attach your collision node to your model so it moves around with it.
self.modelTask = self.model.attachNewNode(self.modelCollisionNode)
This function returns an object that we attached to
self.modelTask - we need to hold onto this for later.
It's actually easier with a complex object to attach a mesh - this just glues a mask to all the points in your model. That's all you need to do to attach a collision mesh.
In this situation,
self.model is our main model, and
secondaryModel is just the one we want to see if it crashes into. We're going to use our mystery self.modelTask to detect collisions:
self.cTrav = CollisionTraverser() self.modelGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.modelTask, self.modelGroundHandler) taskMgr.add(self.traverseTask, "tsk_traverse")
Ok, this gets a little complicated. You need to create a collision traverser that basically continually looks at all the points in the collision masks to see if they intersect. You need to create a
CollisionHandlerQueue that will store all the collision points for you. Using the
addCollider() method, you can add the
CollisionNode and your new
CollisionHandlerQueue to your
CollisionTraverser. Note that this will go through all the points on your primary model (which we did the most work to) and see if it interacts with any secondary model. Be careful not to have too many primary models, or else scanning through all the points will take a long time.
The last thing you do is to add a method called
self.traverseTask to the task manager. Take a look at the Timers and Timing section for some information on the task manager.
You're going to use traverseTask as your method of reacting to a collision, which the
CollisionHandlerQueue will show:
def traverseTask(self, task=None): self.modelGroundHandler.sortEntries() for i in range(self.ballModelGroundHandler.getNumEntries()): entry = self.ballModelGroundHandler.getEntry(i) if task: return task.cont
You're going to sort the entries in the
CollisionHandlerQueue and loop through them (these entries are going to be the different collisions). In here, you can write some code that controls what happens when there is a collision.
The last "
if task: return task.cont" is essentially telling the
CollisionTraverser to continue it's traversing for a collision. Not necessary if you don't wan that to happen, but if you want to keep looking for collisions, you'll need to continue.