I made some little modification to RootDwarf’s code.
Here is the code with some comments (I renamed the class in FirstPersonCamera and saved it in a module called FirstPersonCamera.py). To create the camera controller you need to pass a nodepath containing the camera and if a refNode is specified, heading and up/down are performed wrt this node; so if it is the root node of scene (i.e. base.render) “looking around” is done in a more acceptable way, moreover with shift pressed it goes faster.
from direct.showbase import DirectObject
#from pandac.PandaModules import WindowProperties
#from panda3d.core import CollisionHandlerPusher, CollisionNode, \
# CollisionSphere
from direct.task import Task
## First person camera controller, "free view"/"FPS" style.
#
# Simple camera mouse look and WASD key controller
# shift to go faster,
# r and f keys move camera up/down,
# q and e keys rotate camera,
# hit enter to start/stop controls.
# If a refNode is specified, heading and up/down are performed wrt the
# reference node (usually the root node of scene, i.e. base.render)
# and camera behaves more similarly to an "FPS" camera.
class FirstPersonCamera(DirectObject.DirectObject):
'''
First person camera controller.
'''
## Constructor
# @param gameaApp: the game application to which this controller
# applies, that should be ShowBase derived.
# @param camera: the camera to which this controller applies
# @param refNode: reference node wrt heading and up/down are performed
def __init__(self, gameApp, camera, refNode=None,
collisionHandler=None):
'''
Constructor
'''
self.gameApp = gameApp
self.camera = camera
if refNode != None:
self.refNode = refNode
else:
self.refNode = self.camera
self.running = False
self.time = 0
self.centX = self.gameApp.win.getProperties().getXSize() / 2
self.centY = self.gameApp.win.getProperties().getYSize() / 2
# key controls
self.forward = False
self.backward = False
self.fast = 1.0
self.left = False
self.right = False
self.up = False
self.down = False
self.up = False
self.down = False
self.rollLeft = False
self.rollRight = False
# sensitivity settings
self.movSens = 2
self.movSensFast = self.movSens * 5
self.rollSens = 50
self.sensX = self.sensY = 0.2
self.collisionHandler = collisionHandler
self.collideMask = BitMask32(0x10)
#press enter to get this camera controller
self.accept("enter", self.toggle)
## Get camera collide mask
def getCollideMask(self):
return self.collideMask
## Camera rotation task
def cameraTask(self, task):
dt = task.time - self.time
# handle mouse look
md = self.gameApp.win.getPointer(0)
x = md.getX()
y = md.getY()
if self.gameApp.win.movePointer(0, self.centX, self.centY):
self.camera.setH(self.refNode, self.camera.getH(self.refNode)
- (x - self.centX) * self.sensX)
self.camera.setP(self.camera, self.camera.getP(self.camera)
- (y - self.centY) * self.sensY)
# handle keys:
if self.forward == True:
self.camera.setY(self.camera, self.camera.getY(self.camera)
+ self.movSens * self.fast * dt)
if self.backward == True:
self.camera.setY(self.camera, self.camera.getY(self.camera)
- self.movSens * self.fast * dt)
if self.left == True:
self.camera.setX(self.camera, self.camera.getX(self.camera)
- self.movSens * self.fast * dt)
if self.right == True:
self.camera.setX(self.camera, self.camera.getX(self.camera)
+ self.movSens * self.fast * dt)
if self.up == True:
self.camera.setZ(self.refNode, self.camera.getZ(self.refNode)
+ self.movSens * self.fast * dt)
if self.down == True:
self.camera.setZ(self.refNode, self.camera.getZ(self.refNode)
- self.movSens * self.fast * dt)
if self.rollLeft == True:
self.camera.setR(self.camera, self.camera.getR(self.camera)
- self.rollSens * dt)
if self.rollRight == True:
self.camera.setR(self.camera, self.camera.getR(self.camera)
+ self.rollSens * dt)
self.time = task.time
return Task.cont
## Start to control the camera
def start(self):
self.gameApp.disableMouse()
self.camera.setP(self.refNode, 0)
self.camera.setR(self.refNode, 0)
# hide mouse cursor, comment these 3 lines to see the cursor
props = WindowProperties()
props.setCursorHidden(True)
self.gameApp.win.requestProperties(props)
# reset mouse to start position:
self.gameApp.win.movePointer(0, self.centX, self.centY)
self.gameApp.taskMgr.add(self.cameraTask, 'HxMouseLook::cameraTask')
#Task for changing direction/position
self.accept("w", setattr, [self, "forward", True])
self.accept("shift-w", setattr, [self, "forward", True])
self.accept("w-up", setattr, [self, "forward", False])
self.accept("s", setattr, [self, "backward", True])
self.accept("shift-s", setattr, [self, "backward", True])
self.accept("s-up", setattr, [self, "backward", False])
self.accept("a", setattr, [self, "left", True])
self.accept("shift-a", setattr, [self, "left", True])
self.accept("a-up", setattr, [self, "left", False])
self.accept("d", setattr, [self, "right", True])
self.accept("shift-d", setattr, [self, "right", True])
self.accept("d-up", setattr, [self, "right", False])
self.accept("r", setattr, [self, "up", True])
self.accept("shift-r", setattr, [self, "up", True])
self.accept("r-up", setattr, [self, "up", False])
self.accept("f", setattr, [self, "down", True])
self.accept("shift-f", setattr, [self, "down", True])
self.accept("f-up", setattr, [self, "down", False])
self.accept("q", setattr, [self, "rollLeft", True])
self.accept("q-up", setattr, [self, "rollLeft", False])
self.accept("e", setattr, [self, "rollRight", True])
self.accept("e-up", setattr, [self, "rollRight", False])
self.accept("shift", setattr, [self, "fast", 10.0])
self.accept("shift-up", setattr, [self, "fast", 1.0])
# setup collisions
# setup collisions
if self.collisionHandler != None:
#setup collisions
nearDist = self.camera.node().getLens().getNear()
# Create a collision node for this camera.
# and attach it to the camera.
self.collisionNP = self.camera.attachNewNode(CollisionNode("firstPersonCamera"))
# Attach a collision sphere solid to the collision node.
self.collisionNP.node().addSolid(CollisionSphere(0, 0, 0, nearDist * 1.1))
# self.collisionNP.show()
# setup camera "from" bit-mask
self.collisionNP.node().setFromCollideMask(self.collideMask)
# add to collisionHandler (Pusher)
self.collisionHandler.addCollider(self.collisionNP, self.camera)
#add camera to collision system
self.gameApp.cTrav.addCollider(self.collisionNP, self.collisionHandler)
## Stop to control the camera
def stop(self):
self.gameApp.taskMgr.remove("HxMouseLook::cameraTask")
mat = LMatrix4f(self.camera.getTransform(self.refNode).getMat())
mat.invertInPlace()
self.camera.setMat(LMatrix4f.identMat())
self.gameApp.mouseInterfaceNode.setMat(mat)
self.gameApp.enableMouse()
props = WindowProperties()
props.setCursorHidden(False)
self.gameApp.win.requestProperties(props)
self.forward = False
self.backward = False
self.left = False
self.right = False
self.up = False
self.down = False
self.rollLeft = False
self.ignore("w")
self.ignore("shift-w")
self.ignore("w-up")
self.ignore("s")
self.ignore("shift-s")
self.ignore("s-up")
self.ignore("a")
self.ignore("shift-a")
self.ignore("a-up")
self.ignore("d")
self.ignore("shift-d")
self.ignore("d-up")
self.ignore("r")
self.ignore("shift-r")
self.ignore("r-up")
self.ignore("f")
self.ignore("shift-f")
self.ignore("f-up")
self.ignore("q")
self.ignore("q-up")
self.ignore("e")
self.ignore("e-up")
self.ignore("shift")
self.ignore("shift-up")
# un-setup collisions
if self.collisionHandler != None:
# remove camera from the collision system
self.gameApp.cTrav.removeCollider(self.collisionNP)
# remove from collisionHandler (Pusher)
self.collisionHandler.removeCollider(self.collisionNP)
# remove the collision node
self.collisionNP.removeNode()
## Call to start/stop control system
def toggle(self):
if(self.running):
self.stop()
self.running = False
else:
self.start()
self.running = True
and here an example on how to use it
from direct.showbase.ShowBase import ShowBase
#import from FirstPersonCamera.py
from FirstPersonCamera import FirstPersonCamera
## Main game class.
class TestCamera(ShowBase):
'''
Main TestCamera class.
'''
def __init__(self):
'''
Constructor
'''
ShowBase.__init__(self)
# Load the environment model.
self.environ = self.loader.loadModel("models/environment")
# Reparent the model to render.
self.environ.reparentTo(self.render)
# Apply scale and position transforms on the model.
self.environ.setScale(0.25, 0.25, 0.25)
self.environ.setPos(-8, 42, 0)
self.mouseLook = FirstPersonCamera(self, self.cam, self.render)
if __name__ == '__main__':
gameApp = TestCamera()
gameApp.run()