selective collisions

Hello I was wondering how to make an object collide with some things and not others, I’ll show you my code thwn explain in more detail.

import direct.directbase.DirectStart
from panda3d.core import CollisionTraverser,CollisionNode
from panda3d.core import CollisionHandlerQueue,CollisionRay, CollisionSphere, CollisionHandlerPusher
from panda3d.core import Filename,AmbientLight,DirectionalLight
from panda3d.core import PandaNode,NodePath,Camera,TextNode
from panda3d.core import Vec3,Vec4,BitMask32
from direct.gui.OnscreenText import OnscreenText
from direct.actor.Actor import Actor
from direct.showbase.DirectObject import DirectObject
import random, sys, os, math


class World(object, DirectObject):

    def __init__(self):

        # get the centre of the window
        
        self.winXhalf = base.win.getXSize()/2 # get the X size, then halve it.
        self.winYhalf = base.win.getYSize()/2 # get the Y size, then halve it.

        # define the traverser and our collision handlers

        base.cTrav = CollisionTraverser() # traverser
        pusher = CollisionHandlerPusher() # collison handler pusher
        self.manGroundHandler = CollisionHandlerQueue() # collision handler queue

        # set the base win colour to (0,0,0,1) (black)

        base.win.setClearColor(Vec4(0,0,0,1))

        # load up environment
        
        self.terrain = loader.loadModel('models/world')
        self.terrain.reparentTo(render)
        self.terrain.setPos(0,0,0)
        
        # Create the main character, man

        manStartPos = self.terrain.find("**/start_point").getPos()
        self.man = loader.loadModel('frowney') # load frowney as our model that we move around
        self.man.reparentTo(render)
        self.man.setScale(.1)
        self.man.setPos(manStartPos) # set his start pos to "manStartPos" whixh we defined ealier

        # add a collision solid to "man"
        
        Cnode = CollisionNode('man') # define a Cnode and name it "man"
        Cnode.addSolid(CollisionSphere(0,0,0,1)) # add a collision sphere solid to "Cnode"
        Cman  = self.man.attachNewNode(Cnode) # attach the collision solid to "man"        

        # Create a floater object.  We use the "floater" as a temporary
        # variable in a variety of calculations.
        
        self.floater = NodePath(PandaNode("floater"))
        self.floater.reparentTo(render)

        # a tonne of variables that we will use later
        
        speed = 25
        Forward = Vec3(0,speed*2,0)
        Back = Vec3(0,-speed,0)
        Left = Vec3(-speed,0,0)
        Right = Vec3(speed,0,0)
        Stop = Vec3(0)
        self.walk = Stop
        self.strife = Stop

        # accept all the controls

        self.accept( "escape",sys.exit ) # close window
        self.accept( "s" , self.__setattr__,["walk",Back] ) # walk back
        self.accept( "w" , self.__setattr__,["walk",Forward]) # walk forward
        self.accept( "s-up" , self.__setattr__,["walk",Stop] ) 
        self.accept( "w-up" , self.__setattr__,["walk",Stop] )
        self.accept( "a" , self.__setattr__,["strife",Left]) # walk left
        self.accept( "d" , self.__setattr__,["strife",Right] ) # walk right
        self.accept( "a-up" , self.__setattr__,["strife",Stop] )
        self.accept( "d-up" , self.__setattr__,["strife",Stop] )
        taskMgr.add(self.move, 'move-task')

        # Game state variables
        self.isMoving = False

        # Set up the camera
        
        base.disableMouse() # disables default camera controls
        base.camera.reparentTo(self.man)  # reparent the camera to "man"
        base.camera.setPos(0, 5, 2) # set the position of the camera relative to "man"
        base.camLens.setNearFar(1.1,1000)
        
        # We will detect the height of the terrain by creating a collision
        # ray and casting it downward toward the terrain.  One ray will
        # start above ralph's head, and the other will start above the camera.
        # A ray may hit the terrain, or it may hit a rock or a tree.  If it
        # hits the terrain, we can detect the height.  If it hits anything
        # else, we rule that the move is illegal.

        self.manGroundRay = CollisionRay() # define a collision ray
        self.manGroundRay.setOrigin(0,0,1000) # set the origin
        self.manGroundRay.setDirection(0,0,-1) # set the direction
        self.manGroundCol = CollisionNode('manRay') # define a collision node
        self.manGroundCol.addSolid(self.manGroundRay) # add a solid to the collision node
        self.manGroundCol.setFromCollideMask(BitMask32.bit(0)) # set a from bitmask
        self.manGroundCol.setIntoCollideMask(BitMask32.allOff())# set an into bitmask
        self.manGroundColNp = self.man.attachNewNode(self.manGroundCol) #attach "manGroundCol" to "man"

        base.cTrav.addCollider(self.manGroundColNp, self.manGroundHandler) # add "man" to the traverser
        base.cTrav.showCollisions(render) # show the collisions

        
        base.cTrav.addCollider(Cman, pusher) # add the man's collision solid to the traverser
        pusher.addCollider(Cman, self.man, base.drive.node()) # add the man's collison solid to the pusher collision handler
    
    def move(self, task):

        # move

        dt=globalClock.getDt() # get the global clock 
        # mouse
        md = base.win.getPointer(0) # calls the mouse
        x = md.getX()
        y = md.getY()
        # makes it so that when the mouse moves the camera moves
        if base.win.movePointer(0, self.winXhalf, self.winYhalf):
            self.man.setH(self.man, (x - self.winXhalf)*-0.1)
            base.camera.setP( clampScalar(-90,90, base.camera.getP() - (y - self.winYhalf)*0.1) )
        # move where the keys set it
        moveVec=(self.walk+self.strife)*dt # horizontal
        self.man.setFluidPos(self.man,moveVec)
        
        # Now check for collisions.
        base.cTrav.traverse(render)

        # Adjust man's Z coordinate.  If man's ray hit terrain,
        # update his Z. If it hit anything else, or didn't hit anything, put
        # him back where he was last frame.

        # get man's current pos
        
        startpos = self.man.getPos()

        entries = []
        for i in range(self.manGroundHandler.getNumEntries()):
            entry = self.manGroundHandler.getEntry(i)
            entries.append(entry)
        entries.sort(lambda x,y: cmp(y.getSurfacePoint(render).getZ(),
                                     x.getSurfacePoint(render).getZ()))
        if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain"):
            self.man.setZ(entries[0].getSurfacePoint(render).getZ())
        else:
            self.man.setPos(startpos)
        
        return task.cont

w = World()
run()

Okay so I have it so that “man” can walk around on uneven terrain but what what happens is when I add a collision sphere I collide with the collision mesh that’s around the world model and stops me cause I’ve added man to the pusherhandler which I will use later for other objects. So what I need is to say to the program that I don’t want to collide with the mesh around the world model but I want to collide with everything else.

And there was another thing that didn’t make sense to me in the roaming-ralph tutorial what does this mean

        for i in range(self.manGroundHandler.getNumEntries()):

I don’t get the “for i in range” bit can someone please help by explaining this to me

And now just one more thing you may notice that if you take away the collision sphere around man it will he will walk around like on uneven terrain but when he collides with say a rock or tree the collision ray knows it’s colliding with it but the model just goes through/over it so can someone please point out the bit of code in the roaming-ralph tutorial that makes him Ralph stop when he hits a tree/rock

thanks.

As to selective collision, bitmasks should do the job for you, I imagine.

It’s a loop, I believe, repeating the code within a given number of times, updating the value of “i” each time.

To explain, if I’m not much mistaken:

  • The call to “getNumEntries” should return the number of collision entries that the ground handler has stored - in short, the number of collisions, I believe.
  • “range()” produces a list of values, starting with 0 and ending with -1. You can also, optionally, include a starting value and a “step” - the value by which to increment or decrement, but the code that you game uses neither option.
  • "for i in " iterates over the list, assigning to “i” each value of the list in sequence. Since this list is a list of numbers, “i” should end up, in turn, as each of the numbers from 0 to self.manGroundHandler.getNumEntries().
        for i in range(self.manGroundHandler.getNumEntries()):
            entry = self.manGroundHandler.getEntry(i) 
            ...

A small sidenote: the roaming ralph tutorial is quite old. By now we have convenience methods which return Python lists in many places. So we can use:

        for entry in self.manGroundHandler.getEntries():
            ...

Thanks guys I just have one more question about the roaming ralph tutorial that I thought I put in but must have forgot:
If you take away the collision sphere from “man” he walks around on the uneven terrain fine but when he collides with say a tree or rock he goes through it but in the roaming ralph tutorial he stops as if assigned to collision handler pusher so can someone just point out the bit of code in the roaming ralph tutorial that makes ralph stop when he hits a rock/tree.

thanks

I don’t have the tutorial code at hand, but I dare say that ralph uses several collision rays for detecting ground and obstacle collisions.

A quick look at the Roaming Ralph sample that I have seems to indicate that it happens in the “move” method. Following the comments there, it appears that the basic idea is roughly as follows:

  1. Record Ralph’s position.
  2. Move Ralph.
  3. Check for collisions.
  • If the first collision is with the ground, then simply set Ralph’s z-position to that of the terrain.
  • If it’s something else, then Ralph has hit an obstacle; in this case, reset Ralph’s position to the stored starting position.

In the code that you posted, that seems to be the section a few lines from the bottom. As to your problem regarding these collisions, are you setting the bitmasks appropriately on your obstacle colliders?