Floor ray, picker ray, physics walls code with FPS

Here is some stuff I have been grappling with and sorted . The code loads a model containing placeholders and collider objects, sets up the player and camera, and adds a floor ray to check floor type and empty space, a picker ray to pick objects looked at, and physics to keep the player off walls. To run the code you will need a model with:
a placeholder named playerStart01
a floor collide
a wall collide named wallColl01
a box collide named atm01
a PNG image of some crosshairs
Either that or change the names in the code. Below the code I have given a summary of how to create placeholders and colliders in Blender.

To move the player use:
mouse for direction
e: forward
f: back
a: strafe left
space: strafe right (it where my hands comfortably sits on the keyboard)… or change the code’s key bindings.

The code is in 2 classes Player and Scene1. Player follows:

from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import *
from direct.gui.DirectGui import * 
import sys

from Scene1 import Scene1


class Player(DirectObject):
    def __init__(self):
        #variables
        self.floorMask=BitMask32.bit(0) 
        self.wallMask=BitMask32.bit(1)
        self.pickMask=BitMask32.bit(2)
        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
        self.bullets=[]
        #classes
        self.scene1=Scene1()
        """code begins. Sort out the mouse. Then set up the world in which the player will roam
        and retrieve the player start position which is a placeholder in the model."""
        self.props=WindowProperties()
        self.props.setCursorHidden(True) 
        base.win.requestProperties(self.props) 
        base.disableMouse()
        #load the model, create the player, and setup text to relay info
        self.playerPos=self.scene1.loadScene1(self.floorMask, self.wallMask, self.pickMask)
        self.createPlayer()
        self.initUI() 
        
#Thanks RocMonkey
    def initUI(self): 
        """Setup the players UI""" 

        self.objText = OnscreenText(text = '', pos = (0,0,0), scale = 0.07) 
        image = OnscreenImage(image = 'textures/crosshairs.png') 
        image.setPos((0,0,0)) 
        image.setScale((0.08,1,0.08)) 
        image.setTransparency(TransparencyAttrib.MAlpha)
        self.frame = None 
        
    def createPlayer(self):
        #create the player... no model included
        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,0) 
        self.player.reparentTo(self.playerPos)        

        #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)#make sure player hasn't sneaked passed collision surface

        """Floor. This code comes from the Roaming Ralph tut. Here a ray is created that
        points from above the players head to below his feet colliding with the ground. I could
        have used the physics handler (below) to deal with floor collisions, but I want to
        be able to read surface type, stairs etc, and deal with these by altering speeds"""
        self.playerGroundRay = CollisionRay()
        self.playerGroundRay.setOrigin(0,0,10)
        self.playerGroundRay.setDirection(0,0,-1)
        self.playerGroundCol = CollisionNode('playerRay')
        self.playerGroundCol.addSolid(self.playerGroundRay)
        self.playerGroundCol.setFromCollideMask(self.floorMask)
        self.playerGroundCol.setIntoCollideMask(BitMask32.allOff())
        self.playerGroundColNp = self.player.attachNewNode(self.playerGroundCol)
        self.playerGroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.playerGroundColNp, self.playerGroundHandler)

        """Picker. This code sets up a ray that will shine out of the camera, so to speak,
        and will collide with objects looked at and give a read back as to what they are. 
        Note that the ray itself is not positioned until the task is called."""
        self.pickerHandler = CollisionHandlerQueue()
        self.pickerNode=CollisionNode('pickerRay')
        self.pickerNodeNp=base.camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(self.pickMask)
        self.pickerNode.setIntoCollideMask(BitMask32.allOff())
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.cTrav.addCollider(self.pickerNodeNp, self.pickerHandler)
        #self.pickerNodeNp.show()
        #self.pickTraverser.showCollisions(render)

        """Walls. This code sets up physics on the player which handles wall collisions. I
        also intend using it as a push-back when pushing heavy objects.""" 
        base.enableParticles()
        base.physicsMgr.attachPhysicalNode(self.player.node())
        self.player.node().getPhysicsObject().setMass(10)
        self.physicsHandler=PhysicsCollisionHandler()
        self.physicsHandler.setStaticFrictionCoef(0.0)
        self.physicsHandler.setDynamicFrictionCoef(1.0)
        self.playerCollideNp=self.player.attachNewNode(CollisionNode('collNode'))
        self.playerCollideNp.node().addSolid(CollisionSphere(0,1.8,2,2))
        self.playerCollideNp.node().setFromCollideMask(self.wallMask)
        self.playerCollideNp.node().setIntoCollideMask(BitMask32.allOff())
        self.physicsHandler.addCollider(self.playerCollideNp, self.player)
        self.cTrav.addCollider(self.playerCollideNp, self.physicsHandler)
        

          
        #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

        #get mouse pos if valid... base.win gives a different result to base.mouseWatcherNode... 
        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)

            """Setup and then handle picker ray collisions. We get the mouse coords, set the picker
            ray in position, then call the traverser to traverse from the render node through the
            scene-graph to see if collisions have occurred. If they have occurred, get their name
            and print it on screen using the UI method above."""
            mpos=base.mouseWatcherNode.getMouse()
            self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
            self.cTrav.traverse(render)
            if self.pickerHandler.getNumEntries()>0:
                self.pickerHandler.sortEntries()
                item=self.pickerHandler.getEntry(0).getIntoNodePath().getName()
                self.objText['text'] =item

            """The traverser has been called just above, and the pickerHandler que has been checked.
`           Now we check on the playerGroundHandler que to see if the ground ray has collided
            with the floor. My model is set up with two floor colliders, floorCollider01, and
            stairCollider01. If the player is on the floor I set the forwardspeed to default,
            if it is on the stairs I decrease the forwardspeed. If the que is empty the player has
            wandered into freespace so I make them fall and print a message saying they are falling."""
            if self.playerGroundHandler.getNumEntries()>0:
                self.playerGroundHandler.sortEntries()
                entry=self.playerGroundHandler.getEntry(0)

                nameOf=entry.getIntoNodePath().getName()
            
                if nameOf=="floorCollider01":
                    self.forwardspeed=15.0
                    self.player.setZ(entry.getSurfacePoint(render).getZ())
                if nameOf=="stairCollider01":
                    self.forwardspeed=5.0
                    self.player.setZ(entry.getSurfacePoint(render).getZ())
            else:
                self.objText['text'] = "Player is falling"
                self.player.setZ(self.player.getZ()-0.98)

        #move player ford, back, and strafe
        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)
        
       

        return task.cont

player=Player()
run()

Here is the code that loads the model. Note that it is called Scene1 because if you rename it you will need to change the import details in the above code.

from direct.directbase import DirectStart 
from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import *

class Scene1(DirectObject):
  def __init__(self):
    """My thinking is that loading the models is going to be the most time consuming part of
    getting the game running. In the 'constructor', therefore, I only load the model but do
    not parent it render... that way the time consuming stuff will be done and the loadScene
    method below will just have to reparent the node (rendered to hidden presumeably) to render.
    This means the scene classes could be instantiated by a seperate thread set a low priority."""
    self.world=loader.loadModel("./models/hellsKitchen")
    self.playerPos=self.world.find("**/playerStart01")

  #reparent the scene to render, set the Into masks, and return the player position placeholder
  def loadScene1(self, floorMask, wallMask, pickMask):
    self.floorMask=floorMask
    self.wallMask=wallMask
    self.pickMask=pickMask
    self.world.reparentTo(render)
    self.world.setPos(0,0,0)
    self.world.setScale(1)
    self.createColliders()
    
    return self.playerPos

  def createColliders(self):
    #Wall collision 
    cWall = self.world.find("**/wallColl01") 
    
    #picker collision
    self.world.find("**/atm01").node().setIntoCollideMask(self.pickMask)
    self.world.find("**/atm01").node().setTag('atm01', 'atm01')
    


  
    

In Blender:
Colliders:
Delete the start cube. With your mouse in the 3d window press ‘space’ and add a mesh plane. Split the 3d window and open the outliner window in the smaller section. Ctrl LMB on ‘Plane’ in the outliner window and rename the plane floor01. In the 3d win with the plane selected go shift d and immediately press the LMB so the duplicate just created doesn’t get dragged around. In the outliner rename this floorCollide01. Place your mouse in the buttons window and hit f4 to open the logic panel. Add property. Change the float value to a string. Type Collide into the name field, and Polyset descend into the adjacent field. Now when you export the file it will contain a floor colider that can be got using the code above. Repeat the exercise to create some stairs with a sloping plane beneath called stairCollide01, a wall called wallColl01, and a box called atm01. You will need to either create these things or remove mention of them in the Scene1 class otherwise Panda will complain.

Placeholders:
In the 3d window RMB where you want to place your player… you can always select and move this latter, or better yet, hit the N key to access the text field position etc settings. In the outliner window rename Empty to playerPos01. Done.