Kaweh I can’t thankyou enough for this, this is exactly what I needed. Thankyou very, very much.
I’ve just added your code to my script as follows:
# Left click on the ground to move.
# Rotate the camera by moving the mouse pointer to the edges of the screen or
# with the left & right arrow keys.
# Zoom the camera with the mouse wheel or the up & down arrow keys.
import direct.directbase.DirectStart # Start Panda
from pandac.PandaModules import * # Import the Panda Modules
from direct.showbase.DirectObject import DirectObject # To handle Events
from direct.task import Task # To use Tasks
from direct.actor import Actor # To use animated Actors
from direct.interval.IntervalGlobal import * # To use Intervals
from direct.showbase.PythonUtil import closestDestAngle # For player rotation
import sys
class Controls(DirectObject):
def __init__(self):
base.disableMouse()
self.loadModels()
self.setupCollisions()
# Declare variables.
self.position = None
self.playerMovement = None
self.movementSpeed = 6.0 # Controls how long it takes the player to
# move to the clicked destination.
self.speed = .10 # Controls the speed of the camera rotation and zoom.
# Setup controls
self.accept("escape", sys.exit)
self.accept("player-stopped", self.stopWalkAnim)
self.accept('mouse1', self.moveToPosition)
self.accept("arrow_left", self.cameraTurn,[-1])
self.accept("arrow_right", self.cameraTurn,[1])
self.accept("arrow_up", self.cameraZoom,[-1])
self.accept("arrow_down", self.cameraZoom,[1])
self.accept("wheel_up", self.cameraZoom,[-1])
self.accept("wheel_down", self.cameraZoom,[1])
taskMgr.add(self.edgeScreenTracking, "edgeScreenTracking")
# End __init__
def loadModels(self):
# Load an environment
self.environ = loader.loadModel("models/bumpy_grass")
self.environ.reparentTo(render) # Place it in the scene.
self.environ.setPos(0, 0, 0)
self.environ.setHpr(0, 0, 0)
# Create a player dummy node
self.player_dummy_node = render.attachNewNode("player_dummy_node")
# Position it at the center of the world
self.player_dummy_node.setPos(0, 0, 0)
self.player_dummy_node.setHpr(0, 0, 0)
# Load the player and its animations
self.player = Actor.Actor("models/ralph",{"walk":"models/ralph-walk"})
# Attach the player to the player dummy node
self.player.reparentTo(self.player_dummy_node)
self.player.setScale(.5)
self.player.setPos(0, 0, 0)
# Create a dummy node for the player turn function
self.npLook = render.attachNewNode("npLook")
#Create a camera dummy node
self.camera_dummy_node = render.attachNewNode("camera_dummy_node")
# Attach the camera dummy node to the player dummy node.
self.camera_dummy_node.reparentTo(self.player_dummy_node)
# Attach the camera to the camera dummy node.
camera.reparentTo(self.camera_dummy_node)
# Position the camera
camera.setPos(0, -35, 18) # X = left & right, Y = zoom, Z = Up & down.
camera.setHpr(0, -25, 0) # Heading, pitch, roll.
# End loadModels
# Define a function to setup collision detection. We need two rays, one
# attached to the camera for mouse picking and one attached to the player
# for collision with the terrain. The rays must only cause collisions and
# not collide with each other so their Into bitMasks are set to allOff().
def setupCollisions(self):
# The terrain model was edited by hand to include the following tag:
# <Collide> Plane01 { Polyset keep descend }.
#Once we have the collision tags in the model, we can get to them using
# the NodePath's find command.
self.ground = self.environ.find("**/Plane01")
# Set the model's Into collide mask to bit (0).
self.ground.node().setIntoCollideMask(BitMask32.bit(0))
# Create a CollisionTraverser. CollisionTraversers are what do the
# job of calculating collisions.
self.picker = CollisionTraverser()
# Create a handler for the picker ray
self.queue = CollisionHandlerQueue()
#Make a collision node for our picker ray
self.pickerNode = CollisionNode('mouseRay')
#Attach that node to the camera since the ray will need to be positioned
#relative to it.
self.pickerNP = camera.attachNewNode(self.pickerNode)
# Set the collision node's From collide mask. Now the ray can only cause
# collisions with objects that have bitMask(0)
self.pickerNode.setFromCollideMask(BitMask32.bit(0))
# Set the collision node's Into collide mask. The ray cannot be collided
# into.
self.pickerNode.setIntoCollideMask(BitMask32.allOff())
# Make our ray
self.pickerRay = CollisionRay()
# Add it to the collision node
self.pickerNode.addSolid(self.pickerRay)
#Register the ray as something that can cause collisions with the traverser
self.picker.addCollider(self.pickerNP, self.queue)
# Setup collision stuff to handle the player's collision with the terrain.
# Make a collision node for the player's ray
self.groundCol = CollisionNode('playerRay')
# Make a collision ray for the player
self.groundRay = CollisionRay()
# Set the height of the ray (7 units above the player's head)
self.groundRay.setOrigin(0,0,7)
# Set the rays direction (pointing down on the Z axis)
self.groundRay.setDirection(0,0,-1)
# Add the collision node to the collision ray
self.groundCol.addSolid(self.groundRay)
# Set the collision node's From collide mask. The ray can only collide
# with objects that have bitMask(0).
self.groundCol.setFromCollideMask(BitMask32.bit(0))
# Set the collision node's Into collide mask to allOff so that nothing
# can collide into the ray.
self.groundCol.setIntoCollideMask(BitMask32.allOff())
# Attach the collision node to the player dummy node
self.groundColNp = self.player_dummy_node.attachNewNode(self.groundCol)
# Make a handler for the ground ray
self.floorHandler = CollisionHandlerFloor()
# Associate it with the player dummy node
self.floorHandler.addCollider(self.groundColNp, self.player_dummy_node)
# Register the ray as something that can cause collisions with the traverser
self.picker.addCollider(self.groundColNp, self.floorHandler)
# Uncomment this line to see the collision rays
self.groundColNp.show()
#Uncomment this line to show a visual representation of the
#collisions occuring
self.picker.showCollisions(render)
# End setupCollisions
# Define a task to monitor the position of the mouse pointer & rotate
# the camera when the mouse pointer moves to the edges of the screen.
def edgeScreenTracking(self,task):
# Check if the mouse is available
if not base.mouseWatcherNode.hasMouse():
return Task.cont
# Get the relative mouse position, its always between 1 and -1
mpos = base.mouseWatcherNode.getMouse()
if mpos.getX() > 0.99:
self.cameraTurn(1)
elif mpos.getX() < -0.99:
self.cameraTurn(-1)
return Task.cont
# End edgeScreenTracking
# Define the CameraTurn function.
def cameraTurn(self,dir):
self.camTurn = LerpHprInterval(self.camera_dummy_node, self.speed, Point3(self.camera_dummy_node.getH()-(10*dir), 0, 0))
self.camTurn.start()
# End cameraTurn
# Define the cameraZoom function.
def cameraZoom(self,dir):
self.camZoom = LerpPosInterval(camera, self.speed, Point3(camera.getX(), camera.getY()-(2*dir), camera.getZ()))
self.camZoom.start()
# End cameraZoom
# Define a function to get the position of the mouse click.
def getPosition(self, mousepos):
startpos = self.player.getPos()
self.pickerRay.setFromLens(base.camNode, mousepos.getX(),mousepos.getY())
# Now check for collisions.
self.picker.traverse(render)
if self.queue.getNumEntries() > 0:
self.queue.sortEntries()
# This is the clicked position.
self.position = self.queue.getEntry(0).getSurfacePoint(self.environ)
return None
# End getPosition
# Define a function to make the player turn towards the clicked position
# and then move to that position.
def moveToPosition(self):
# Get the clicked position
self.getPosition(base.mouseWatcherNode.getMouse())
# Calculate the new hpr
self.npLook.setPos(self.player.getPos(render))
# Look at the clicked position.
self.npLook.lookAt(self.position)
# Prevent overturning or 'wrapping'
reducedH = self.player.getH()%360.0
self.player.setH(reducedH)
currHpr = self.player.getHpr()
newHpr = self.npLook.getHpr()
newH = closestDestAngle(currHpr[0], newHpr[0])
# Create a turn animation from current hpr to the calculated new hpr.
playerTurn = self.player.hprInterval(.1, Point3(newH, newHpr[1], newHpr[2]))
# Calculate the distance between the start and finish positions.
# This is then used to calculate the duration it should take to
# travel to the new coordinates based on self.movementSpeed
travelVec = self.position - self.player_dummy_node.getPos()
distance = travelVec.length()
# Create an animation to make the player move to the clicked position
playerMove = self.player_dummy_node.posInterval((distance / self.movementSpeed), self.position)
# We create a LerpFunc Interval to correct the Z as we go along
playerPositionZ = LerpFunc(self.correctPlayerZ, duration=(distance / self.movementSpeed))
# Put both animations into a sequence
self.playerMovement = Parallel(Sequence(playerTurn, playerMove), playerPositionZ)
self.playerMovement.setDoneEvent("player-stopped")
self.player.loop("walk")
self.playerMovement.start()
# End moveToPosition
def correctPlayerZ(self, time):
startpos = self.player_dummy_node.getPos()
self.picker.traverse(render)
if self.floorHandler.getNumEntries() > 0:
point = self.queue.getEntry(0).getSurfacePoint(self.environ)
self.player.setZ(point.getZ())
else:
self.player.setPos(startpos)
# End correctPlayerZ
def stopWalkAnim(self):
# This is called when the movement animation has finished.
# We can then stop the walk animation.
self.player.stop("walk")
self.player.pose("walk",17)
self.playerMove = None
c = Controls()
run()
But when I ran it I got this error message:
if self.floorHandler.getNumEntries() > 0:
AttributeError: 'libpanda.CollisionHandlerFloor' object has no attribute 'getNumEntries'
I’ve stuffed something up again sigh. Can you see what I did wrong?
Thanks heaps