## Using floor collider to handle wall collisions

### Using floor collider to handle wall collisions

Roaming Ralph is repositioned back to his previous position whenever he steps off the terrain or, which is to say the same thing, whenever the floor collider handler queue is empty. In an urban environment floorplans are mostly squares so I am thinking this methodolgy would work for handling walls... if the player's floor collision queue is empty they have either stepped into open space or hit a wall. In which case I am also thinking there is a performance bonus to handling wall colisions without adding wall colliders and engaging physics engines. So that is what this code is about. The problem with the Roaming Ralph example is that it does not allow for sliding along walls when strafing or moving forward or back. This code adds that functionalilty.

For the purposes here, I have used a CardMaker instance as the floor and collider. In the stuff I am working on I use a model with a floor collider geometry and named empties which determine the origin (lower left) and maximum (upper right) of each floor collider. The empties are necessary unless you know of an easy way of finding these positions from the floor colliders themselves... I gave up and used empties is what I am saying.

Code: Select all
`from pandac.PandaModules import loadPrcFileData from direct.directbase import DirectStartfrom direct.showbase.DirectObject import DirectObjectfrom pandac.PandaModules import *from direct.gui.DirectGui import * import sysfrom random import randomimport math"""The Qad class contains information about the current quadrant the player is in. At this stage only the min X/Y, max X/Y positions of each floor collider are held,but floor type, whether the floor is wet, whether it is stairs; the wall types,whether they are tiles or concrete and therefore how they determine how the playerslides along them can all be included. At this stage the floor colliders of the worldneed to be broken up into squares or rectangles. """class Qad():    def __init__(self):      self.maxx=0.0      self.minx=0.0      self.maxy=0.0      self.miny=0.0      self.maxz=0.0      self.minz=0.0    #setters    def setXyz(self, minPos, maxPos):      self.minx=minPos[0]      self.miny=minPos[1]      self.minz=minPos[2]      self.maxx=maxPos[0]      self.maxy=maxPos[1]      self.maxz=maxPos[2]    #getters    def getMaxx(self):      return self.maxx    def getMinx(self):      return self.minx    def getMaxy(self):      return self.maxy    def getMiny(self):      return self.miny    """For the purposes here I have dumped what I have spread about 3 classes into this oneclass... hope its not too messy."""class Main(DirectObject):  def __init__(self):    #variables    self.vel=Vec3(0.0,0.0,0.0)     self.strafespeed=4.5     self.forwardspeed=15.0    self.backspeed=7.0     self.mouseSen=0.1    #track which qad the player is in    self.qad="qad01"    #keep a dictionary of Qad objects that we can access via self.qad (above)    self.qads={}            #code begins    base.setBackgroundColor(0.2,0.2,0.2)    self.props=WindowProperties()    self.props.setCursorHidden(True)     base.win.requestProperties(self.props)     base.disableMouse()    self.initQads()    self.createHud()    self.createFloor()    self.createPlayer()  """ for every (square) floor collider create a Qad object and give it min max positions.      The floor collider has to stop short of the wall otherwise the player will stick      their head into the wall and see beyond it. I haven't implemented that here      because I have used just one plate, a CardMaker object. You have to fiddle with      it to get it right. And I haven't tested it on different resolutions.  """  def initQads(self):    self.qads["qad01"]=Qad()    self.qads["qad01"].setXyz(Point3(-50,-50,0),Point3(50,50,0))  #just a text field to say whether the player is on the floor or not  def createHud(self):     self.objText = OnscreenText(text = '', pos = (0,0,0), scale = 0.07)     self.frame = None      #a card maker suffices as the floor and the collider. I have a model  #and a series of rectangle floor colliders  def createFloor(self):    self.cm=CardMaker("ground")     self.cm.setFrame(-50, 50, -50, 50)     self.cm.setUvRange((0, 1), (1, 0))    self.cm.setColor(0.3,0.8,0.3, 1)    self.ground = render.attachNewNode(self.cm.generate())     self.ground.setPos(0, 0, 0);    self.ground.lookAt(0, 0, -1)          #Setup player, camera, keys, and floor collider colision ray and call task   def createPlayer(self):    #this is FPS without accompanying models... just a player wondering about as a camera    self.player=NodePath(ActorNode("Player"))     self.player.reparentTo(render)    self.player.setH(base.camera.getH())    base.camera.reparentTo(self.player)     base.camera.setPos(0,-0.2,0)    self.player.reparentTo(render)    self.player.setPos(0,0,2)    #setup event input    self.accept("escape", sys.exit)    self.keyMap = {"forward":0,"backward":0,"strafeleft":0,"straferight":0}     self.accept("e",self.setKey, ["forward",1])     self.accept("f",self.setKey, ["backward",1])     self.accept("a",self.setKey, ["strafeleft",1])     self.accept("space",self.setKey, ["straferight",1])             self.accept("e-up",self.setKey, ["forward",0])     self.accept("f-up",self.setKey, ["backward",0])     self.accept("a-up",self.setKey, ["strafeleft",0])     self.accept("space-up",self.setKey, ["straferight",0])    #setup collision handlers    self.cTrav = CollisionTraverser()    self.cTrav.setRespectPrevTransform(True)    #floor    self.playerGroundRay = CollisionRay()    self.playerGroundRay.setOrigin(0,0,100)    self.playerGroundRay.setDirection(0,0,-1)    self.playerGroundCol = CollisionNode('playerRay')    self.playerGroundCol.addSolid(self.playerGroundRay)    self.playerGroundCol.setFromCollideMask(GeomNode.getDefaultCollideMask())     self.playerGroundCol.setIntoCollideMask(BitMask32.allOff())    self.playerGroundColNp = self.player.attachNewNode(self.playerGroundCol)    self.playerGroundHandler = CollisionHandlerQueue()    self.cTrav.addCollider(self.playerGroundColNp, self.playerGroundHandler)    #call the task to update player    taskMgr.add(self.updatePlayer, "updatePlayer")   #map keys  def setKey(self, key, value):     self.keyMap[key] = value  #task to update player  def updatePlayer(self, task):     elapsed = globalClock.getDt()    self.objText['text'] = ''    x=0.0    y=0.0    mousex=0.0    mousey=0.0    #store the players position prior to moving them so if they are off the floor    #and at a corner we'll stop them by resetting them     startPos=self.player.getPos()    #this variable is modified (x,y,z) as necessary and fed to the player    #if they are off the floor, moving, and not at a corner    modPos=startPos            #move player ford, back, and strafe. We do this regardless of whether the player    #is on the floor or not. That's why we keep startPos    if (self.keyMap["strafeleft"]!=0):         x = -self.strafespeed    if (self.keyMap["straferight"]!=0):         x = self.strafespeed    if (self.keyMap["forward"]!=0):         y = self.forwardspeed    if (self.keyMap["backward"]!=0):         y = -self.backspeed        self.vel = Vec3(x,y,0)    self.vel *= elapsed     self.player.setFluidPos(self.player, self.vel)                    #get mouse pos     if base.mouseWatcherNode.hasMouse():        md = base.win.getPointer(0)         mousex = md.getX()         mousey = md.getY()         # rotate the camera based on the mouse coordinates         if base.win.movePointer(0, base.win.getXSize()/2, base.win.getYSize()/2):             base.camera.setP((base.camera.getP()-(mousey-base.win.getYSize()/2)*self.mouseSen) % 360)             self.player.setH((self.player.getH()-(mousex-base.win.getXSize()/2)*self.mouseSen) % 360)            #travers render for floor collisions and handle    self.cTrav.traverse(render)    #are we on the floor    if self.playerGroundHandler.getNumEntries()>0:        self.playerGroundHandler.sortEntries()        entry=self.playerGroundHandler.getEntry(0)        #set the players z position 3.8 above the collision point ray meets floor        self.player.setZ(entry.getSurfacePoint(render).getZ()+3.8)        #get name of the floor collider to print in the Hud        nameOf=entry.getIntoNodePath().getName()        self.objText['text'] =str(nameOf)        #and set self.qad so the player knows what floor plate they are on        if nameOf=="floorCollider01":            self.qad="qad01"        elif nameOf=="stairCollider01":            self.qad="qad02"        elif nameOf=="floorCollider02":            self.qad="qad03"        elif nameOf=="stairCollider02":            self.qad="qad04"    #the player is off the floor    else:        self.objText['text'] =str("Off floor")        """ Here is the problem, if the player is facing north west and is into the north wall             and moving forward, then they will be sliding down the x axis, but if they are in the             same position but strafing to the right, they will be sliding up the x axis. Furthermore,             if they are in the north west corner moving forward we don't want them going anywhere.            Nor do we want them going anywhere if they are facing north west and are trying to strafe            right while in the north east corner. This is where all that gets done.         """        #flag determines whether the player is at a corner        flag=0        #find out where the player is heading        heading=self.player.getH()        #are they heading north west        if heading>0 and heading<90:            #which keys are pressed (forward, backward, strafe left/right            if self.keyMap["forward"]!=0:                #which wall are they touching, north is maxy, west is minx                floory=self.qads[self.qad].getMaxy()                floorx=self.qads[self.qad].getMinx()                #are they on the north wall                if self.player.getY()>floory:                    #modify their x position only and flag that the player is on the north wall                    modPos[0]=self.player.getX()-0.03                    self.player.setFluidPos(modPos)                    flag=flag+1                #are they on the west wall                if self.player.getX()<floorx:                    #modify they y position only and flag that the player is on the west wall                    modPos[1]=self.player.getY()+0.03                    self.player.setFluidPos(modPos)                    flag=flag+1                #is the player at the corner of the north and west walls, if so, reset them                #to where they were before this cycle of the task                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])            #etc...            if self.keyMap["backward"]!=0:                floory=self.qads[self.qad].getMiny()                floorx=self.qads[self.qad].getMaxx()                                if self.player.getY()<floory:                    modPos[0]=self.player.getX()+0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()>floorx:                    modPos[1]=self.player.getY()-0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])                if self.keyMap["straferight"]!=0:                floory=self.qads[self.qad].getMaxy()                floorx=self.qads[self.qad].getMaxx()                                    if self.player.getY()>floory:                    modPos[0]=self.player.getX()+0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()>floorx:                    modPos[1]=self.player.getY()+0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])            if self.keyMap["strafeleft"]!=0:                floory=self.qads[self.qad].getMiny()                floorx=self.qads[self.qad].getMinx()                                    if self.player.getY()<floory:                    modPos[0]=self.player.getX()-0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()<floorx:                    modPos[1]=self.player.getY()-0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])                                #southwest        elif heading>90 and heading<180:            if self.keyMap["forward"]!=0:                floory=self.qads[self.qad].getMiny()                floorx=self.qads[self.qad].getMinx()                                    if self.player.getY()<floory:                    modPos[0]=self.player.getX()-0.03                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()<floorx:                    modPos[1]=self.player.getY()-0.03                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                        self.player.setPos(floorx, floory, startPos[2])            if self.keyMap["backward"]!=0:                floory=self.qads[self.qad].getMaxy()                floorx=self.qads[self.qad].getMaxx()                                    if self.player.getY()>floory:                    modPos[0]=self.player.getX()+0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()>floorx:                    modPos[1]=self.player.getY()+0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])            if self.keyMap["straferight"]!=0:                floory=self.qads[self.qad].getMaxy()                floorx=self.qads[self.qad].getMinx()                                    if self.player.getY()>floory:                    modPos[0]=self.player.getX()-0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()<floorx:                    modPos[1]=self.player.getY()+0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])            if self.keyMap["strafeleft"]!=0:                floory=self.qads[self.qad].getMiny()                floorx=self.qads[self.qad].getMaxx()                                   if self.player.getY()<floory:                    modPos[0]=self.player.getX()+0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()>floorx:                    modPos[1]=self.player.getY()-0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])        #southeast              elif heading>180 and heading<270:            if self.keyMap["forward"]!=0:                floory=self.qads[self.qad].getMiny()                floorx=self.qads[self.qad].getMaxx()                                    if self.player.getY()<floory:                    modPos[0]=self.player.getX()+0.03                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()>floorx:                    modPos[1]=self.player.getY()-0.03                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])            if self.keyMap["backward"]!=0:                floory=self.qads[self.qad].getMaxy()                floorx=self.qads[self.qad].getMinx()                                    if self.player.getY()>floory:                    modPos[0]=self.player.getX()-0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()<floorx:                    modPos[1]=self.player.getY()+0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])            if self.keyMap["straferight"]!=0:                floory=self.qads[self.qad].getMiny()                floorx=self.qads[self.qad].getMinx()                                    if self.player.getY()<floory:                    modPos[0]=self.player.getX()-0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()<floorx:                    modPos[1]=self.player.getY()-0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])            if self.keyMap["strafeleft"]!=0:                floory=self.qads[self.qad].getMaxy()                floorx=self.qads[self.qad].getMaxx()                                   if self.player.getY()>floory:                    modPos[0]=self.player.getX()+0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()>floorx:                    modPos[1]=self.player.getY()+0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])        #northeast        elif heading>270 and heading<360:            if self.keyMap["forward"]!=0:                floory=self.qads[self.qad].getMaxy()                floorx=self.qads[self.qad].getMaxx()                                    if self.player.getY()>floory:                    modPos[0]=self.player.getX()+0.03                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()>floorx:                    modPos[1]=self.player.getY()+0.03                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])            if self.keyMap["backward"]!=0:                floory=self.qads[self.qad].getMiny()                floorx=self.qads[self.qad].getMinx()                                    if self.player.getY()<floory:                    modPos[0]=self.player.getX()-0.005                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()<floorx:                    modPos[1]=self.player.getY()-0.005                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])            if self.keyMap["straferight"]!=0:                floory=self.qads[self.qad].getMiny()                floorx=self.qads[self.qad].getMaxx()                                    if self.player.getY()<floory:                    modPos[0]=self.player.getX()+0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()>floorx:                    modPos[1]=self.player.getY()-0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])            if self.keyMap["strafeleft"]!=0:                floory=self.qads[self.qad].getMaxy()                floorx=self.qads[self.qad].getMinx()                                    if self.player.getY()>floory:                    modPos[0]=self.player.getX()-0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if self.player.getX()<floorx:                    modPos[1]=self.player.getY()+0.01                    self.player.setFluidPos(modPos)                    flag=flag+1                if flag==2:                    self.player.setPos(floorx, floory, startPos[2])    return task.cont main=Main()run()`

ste3e

Posts: 28
Joined: Sun Apr 25, 2010 7:01 pm
Location: New Zealand

Thanks, this may be useful to something I wanted to try.

silveralex

Posts: 235
Joined: Tue Apr 21, 2009 1:48 pm
Location: New York, NY