This modification changes player controls (gives walk-run option, for example; although animations are the same at the moment, but it can be easily changed), camera controls, and some other tweaks.
# This code was created with Crimsonland-like shooter concept in my head.
# This is why I placed camera so high and removed camera collisions. They
# can be easily restored, having the original Roaming Ralph sample
# and this code.
# Thanks to all who helped to make this code! I have just started
# to learn Panda, so, all working code belongs to them,
# all bugs are my own :)
# Version 0.1
import direct.directbase.DirectStart
from direct.actor.Actor import Actor
from direct.showbase.DirectObject import DirectObject
from direct.task.Task import Task
from pandac.PandaModules import WindowProperties
from pandac.PandaModules import PandaNode,NodePath,Camera,Vec3,Filename
from pandac.PandaModules import CollisionTraverser,CollisionNode,BitMask32
from pandac.PandaModules import CollisionHandlerQueue,CollisionRay
import sys
# Relative path to Roaming Ralph's models directory
modelsDir = "models/"
# Ralph's speed, units per second
speed = 2
# Set floater and camera position (relative to self.actorPlace).
# The camera will look at the floater, and not at Ralph himself.
# Right(-) or left(+), forward (-) or backward (+), down(-) or up(+)
setFloaterPos = (0, -2, 0.5)
setCameraPos = (0, 15, 8)
class World(DirectObject):
def __init__(self):
# Disable defaul camera controls
base.disableMouse()
# Initial setup
props = WindowProperties()
props.setCursorHidden(True)
base.win.requestProperties(props)
base.setBackgroundColor(0, 0, 0)
# Find the center of the screen
self.screenCenterX = base.win.getProperties().getXSize()/2
self.screenCenterY = base.win.getProperties().getYSize()/2
# Move mouse to the middle of the screen
base.win.movePointer(0, self.screenCenterX, self.screenCenterY)
# Game state variables
# Previous recorded time in milliseconds (will be used for movements)
self.prevtime = 0
# Is Ralph moving now? (will be used to animate Ralph)
self.isMoving = False
# Initial orientation of Ralph, floater, and camera
# (i.e. self.actorPlace) in degrees
self.angle = 0
# Set floater and camera position (relative to self.actorPlace).
# The camera will look at the floater, and not at Ralph himself.
# Right(-) or left(+), forward (-) or backward (+), down(-) or up(+)
self.floaterPos = setFloaterPos
self.cameraPos = setCameraPos
# When we scale nodes, their local coordinate system is scaled too.
# Also, some models use Y as top-down axis, while the engine uses Z
# for it. Therefore, in order to avoid confusion, it's better to
# create an empty node (here it's called "actorPlace"), and reparent
# Ralph to it. In this case Ralph can be scaled as he needs,
# and have Y as his top-down axis, still, we can move, rotate or use
# him in any other way with ease
# Create Actor's placeholder
self.actorPlace = NodePath(PandaNode("actorPlace"))
self.actorPlace.reparentTo(render)
self.actorPlace.setPos(0, 0, 0)
self.actorPlace.setHpr(self.angle, 0, 0)
# Create the main character, Ralph, with two animations
# and reparent him to the self.actorPlace
self.ralph = Actor(modelsDir+"ralph",
{"run":modelsDir+"ralph-run",
"walk":modelsDir+"ralph-walk"})
self.ralph.reparentTo(self.actorPlace)
self.ralph.setScale(.2)
self.ralph.setPos(0, 0, 0)
self.ralph.setHpr(0, 0, 0)
# Create the floater and reparent it to the self.actorPlace
self.floater = NodePath(PandaNode("floater"))
self.floater.reparentTo(self.actorPlace)
self.floater.setPos(self.floaterPos[0], self.floaterPos[1], self.floaterPos[2])
# Create the camera, reparent it to the self.actorPlace
# and make it look at the floater
base.camera.reparentTo(self.actorPlace)
base.camera.setPos(self.cameraPos[0], self.cameraPos[1], self.cameraPos[2])
base.camera.lookAt(self.floater)
# Load world
self.environ = loader.loadModel(modelsDir+"world")
self.environ.reparentTo(render)
self.environ.setPos(0, 0, 0)
# Create key map
# (primFire and secFire are not used, but reserved for future use)
self.keyMap = {"left":0,
"right":0,
"forward":0,
"backward":0,
"run":0,
"changeView":0,
"primFire":0,
"secFire":0}
# Accept the control keys for movement and rotation
# (primFire and secFire are not used, but reserved for future use)
self.accept("escape", sys.exit)
self.accept("w", self.setKey, ["forward", 1])
self.accept("a", self.setKey, ["left", 1])
self.accept("s", self.setKey, ["backward", 1])
self.accept("d", self.setKey, ["right", 1])
self.accept("w-up", self.setKey, ["forward", 0])
self.accept("a-up", self.setKey, ["left", 0])
self.accept("s-up", self.setKey, ["backward", 0])
self.accept("d-up", self.setKey, ["right", 0])
self.accept("shift", self.setKey, ["run", 1])
self.accept("shift-up", self.setKey, ["run", 0])
self.accept("shift-w", self.setKey, ["forward", 1])
self.accept("shift-a", self.setKey, ["left", 1])
self.accept("shift-s", self.setKey, ["backward", 1])
self.accept("shift-d", self.setKey, ["right", 1])
self.accept("shift-w-up", self.setKey, ["forward", 0])
self.accept("shift-a-up", self.setKey, ["left", 0])
self.accept("shift-s-up", self.setKey, ["backward", 0])
self.accept("shift-d-up", self.setKey, ["right", 0])
self.accept("mouse2", self.setKey, ["changeView", 1])
self.accept("mouse2-up", self.setKey, ["changeView", 0])
self.accept("mouse1", self.setKey, ["primFire", 1])
self.accept("mouse1-up", self.setKey, ["primFire", 0])
self.accept("mouse3", self.setKey, ["secFire", 1])
self.accept("mouse3-up", self.setKey, ["secFire", 0])
# We repeat self.move every frame
taskMgr.add(self.move,"moveTask")
# Collision detection
self.ralphGroundRay = CollisionRay()
self.ralphGroundRay.setOrigin(0, 0, 1000)
self.ralphGroundRay.setDirection(0, 0, -1)
self.ralphGroundCol = CollisionNode('ralphRay')
self.ralphGroundCol.addSolid(self.ralphGroundRay)
self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0))
self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff())
self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol)
self.ralphGroundHandler = CollisionHandlerQueue()
# base.cTrav doesn't need to be called every frame,
# it happens automatically.
base.cTrav = CollisionTraverser()
base.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler)
# Records the state of the arrow keys
def setKey(self, key, value):
self.keyMap[key] = value
def move(self, task):
# This method is called every frame, since it is attached to taskMgr.
# The elapsed time is the current time minus the last saved time
elapsed = task.time - self.prevtime
# Save Ralph's initial position so that we can restore it,
# in case he falls off the map or runs into something.
startpos = self.actorPlace.getPos()
# If a move key is pressed, turn ralph in the specified direction.
if (self.keyMap["left"]!=0) or (self.keyMap["right"]!=0) or (self.keyMap["backward"]!=0) or (self.keyMap["forward"]!=0):
rotDirections = (self.keyMap["left"], self.keyMap["right"], self.keyMap["backward"], self.keyMap["forward"])
rotValues = (90, 270, 180, (0, 360))
rotateBy = 0
divideBy = 0
for direction in rotDirections:
divideBy += direction
for value in range(0, 4):
if value != 3:
rotateBy += rotDirections[value]*rotValues[value]
if value == 3:
if self.keyMap["left"]!=0:
rotateBy += rotDirections[value]*rotValues[value][0]
else:
rotateBy += rotDirections[value]*rotValues[value][1]
rotateBy = rotateBy / divideBy
self.ralph.setH(rotateBy)
# If a move-key is pressed, move ralph in the specified direction.
if (self.keyMap["left"]!=0):
if (self.keyMap["run"]!=0):
self.actorPlace.setPos(self.actorPlace, Vec3(1, 0, 0)*(elapsed*speed*2))
else:
self.actorPlace.setPos(self.actorPlace, Vec3(1, 0, 0)*(elapsed*speed))
if (self.keyMap["right"]!=0):
if (self.keyMap["run"]!=0):
self.actorPlace.setPos(self.actorPlace, Vec3(-1, 0, 0)*(elapsed*speed*2))
else:
self.actorPlace.setPos(self.actorPlace, Vec3(-1, 0, 0)*(elapsed*speed))
if (self.keyMap["forward"]!=0):
if (self.keyMap["run"]!=0):
self.actorPlace.setPos(self.actorPlace, Vec3(0, -1, 0)*(elapsed*speed*2))
else:
self.actorPlace.setPos(self.actorPlace, Vec3(0, -1, 0)*(elapsed*speed))
if (self.keyMap["backward"]!=0):
if (self.keyMap["run"]!=0):
self.actorPlace.setPos(self.actorPlace, Vec3(0, 1, 0)*(elapsed*speed*2))
else:
self.actorPlace.setPos(self.actorPlace, Vec3(0, 1, 0)*(elapsed*speed))
# If ralph is moving, loop "run" animation.
# If he is standing still, stop the animation.
if (self.keyMap["forward"]!=0) or (self.keyMap["left"]!=0) or (self.keyMap["right"]!=0) or (self.keyMap["backward"]!=0):
if not self.isMoving:
self.ralph.loop("run")
self.isMoving = True
else:
if self.isMoving:
self.ralph.stop()
self.ralph.pose("walk",5)
self.isMoving = False
# Mouse controls
if self.keyMap["changeView"]!=0: # If changeView button is pressed, change view when moving mouse.
mouseX = base.win.getPointer(0).getX()
# Update ratation
if base.win.movePointer(0, self.screenCenterX, self.screenCenterY):
self.angle -= (mouseX - self.screenCenterX)
if self.angle < 0:
self.angle += 360
if self.angle >= 360:
self.angle -= 360
# Otherwise, just keep cursor position at the center of the screen
else:
base.win.movePointer(0, self.screenCenterX, self.screenCenterY)
# Adjust ralph's Z coordinate. If ralph'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.
entries = []
for i in range(self.ralphGroundHandler.getNumEntries()):
entry = self.ralphGroundHandler.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.actorPlace.setZ(entries[0].getSurfacePoint(render).getZ())
else:
self.actorPlace.setPos(startpos)
# Update actorPlace orientation
self.actorPlace.setH(self.angle)
# Store the task time and continue
self.prevtime = task.time
return Task.cont
# Create world
World()
#render.analyze()
run()