Ok, I’ve been working on this for the past couple of days. So far, I’ve managed to write a program that returns the position of a mouse click and prints the results to the command console. It looks like this:
import direct.directbase.DirectStart # Start Panda
from pandac.PandaModules import* # Import the Panda Modules
from direct.showbase.DirectObject import DirectObject # To listen for 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
import math # To use math (sin, cos..etc)
import sys
class Picker(DirectObject):
def __init__(self):
base.disableMouse() # Disable default camera.
self.loadModels()
# Setup collision stuff
self.position = None
self.picker = CollisionTraverser() # Make a traverser
self.queue = CollisionHandlerQueue() # Make a handler
#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)
self.pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask())
self.pickerRay = CollisionRay() # Make our ray
self.pickerNode.addSolid(self.pickerRay) # Add it to the collision node
#Register the ray as something that can cause collisions
self.picker.addCollider(self.pickerNode, self.queue)
self.accept("mouse1", self.destination)
def loadModels(self):
# Load the player and its animations
self.player = Actor.Actor("MODELS/ralph",{"walk":"MODELS/ralph-walk"})
self.player.reparentTo(render) # Make it display/render on the screen.
self.player.setScale(.005)
self.player.setPos(0, 0, 0) # Position it at the center of the world.
# Load an environment
self.environ = loader.loadModel("MODELS/env")
self.environ.reparentTo(render)
self.environ.setPos(0, 0, 0)
#Set the model to be collideable with the ray.
self.makePickable(self.environ)
#Create a camera dummy node
self.camera_dummy_node = render.attachNewNode("camera_dummy_node")
#Position the camera dummy node.
self.camera_dummy_node.setPos( 0, 0, 0)
# Make it view from behind the player.
self.camera_dummy_node.setHpr(180, 0, 0)
# Attach the camera dummy node to the player.
self.camera_dummy_node.reparentTo(self.player)
# Attach the camera to the dummy node.
camera.reparentTo(self.camera_dummy_node)
# Position the camera
camera.setPos(0, -30, 9) # X = left & right, Y = zoom, Z = Up & down.
camera.setHpr(0, -25, 0) # Heading, pitch, roll.
camera.lookAt(self.player) # Make the camera follow the player.
# end loadModels
# This function is meant to flag an object as something we can pick.
def makePickable(self, newObj):
newObj.setTag("pickable","true")
# This function finds the closest object to the camera that has been hit
# by our ray.
def getPosition(self, mousepos):
self.pickerRay.setFromLens(base.camNode, mousepos.getX(),mousepos.getY())
self.picker.traverse(render)
if self.queue.getNumEntries() > 0:
self.queue.sortEntries() # Get the closest object.
self.position = self.queue.getEntry(0).getIntoNodePath()
parent = self.position.getParent()
self.position = None
while parent != render:
if parent.getTag("pickable") == "true":
self.position = parent
return parent
else:
parent = parent.getParent()
return None
def destination(self):
self.getPosition(base.mouseWatcherNode.getMouse())
print self.position
mousePicker = Picker()
run()
It seems to do what I want, and prints these results(which I’m hoping are the actual world coordinates of the mouse clicks on the environment model):
[NodePath at: 99425648]
render/env.egg
[NodePath at: 99425624]
render/env.egg
[NodePath at: 99425648]
render/env.egg
[NodePath at: 98257056]
render/env.egg
[NodePath at: 99425624]
render/env.egg
[NodePath at: 99425648]
render/env.egg
Anyway, what I’m now trying to do, is to get my player model to move to the coordinates of those mouse clicks. This is what I’ve come up with (I’m working from the tutorials and Sandman’s ‘BoardWalker’ example):
import direct.directbase.DirectStart # Start Panda
from pandac.PandaModules import* # Import the Panda Modules
from direct.showbase.DirectObject import DirectObject # To listen for 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
import math # To use math (sin, cos..etc)
import sys
#A handy little function for getting the proper position for a given point
def getPos(position):
return Point3((i%8) - 3.5, int(i/8) - 3.5, 0)
class Picker(DirectObject):
def __init__(self):
base.disableMouse() # Disable default camera.
self.loadModels()
self.rotationOffset = 0
self.playerMovement = None
self.movementSpeed = 0.5
# Setup collision stuff
self.position = None
self.picker = CollisionTraverser() # Make a traverser
self.queue = CollisionHandlerQueue() # Make a handler
#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)
self.pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask())
self.pickerRay = CollisionRay() # Make our ray
self.pickerNode.addSolid(self.pickerRay) # Add it to the collision node
#Register the ray as something that can cause collisions
self.picker.addCollider(self.pickerNode, self.queue)
self.accept("player-stopped", self.stopWalkAnim)
self.accept("mouse1", self.destination)
def loadModels(self):
# Load the player and its animations
self.player = Actor.Actor("MODELS/ralph",{"walk":"MODELS/ralph-walk"})
self.player.reparentTo(render) # Make it display/render on the screen.
self.player.setScale(.005)
self.player.setPos(0, 0, 0) # Position it at the center of the world.
# Load an environment
self.environ = loader.loadModel("MODELS/env")
self.environ.reparentTo(render)
self.environ.setPos(0, 0, 0)
#Set the model to be collideable with the ray.
self.makePickable(self.environ)
#Create a camera dummy node
self.camera_dummy_node = render.attachNewNode("camera_dummy_node")
#Position the camera dummy node.
self.camera_dummy_node.setPos( 0, 0, 0)
# Make it view from behind the player.
self.camera_dummy_node.setHpr(180, 0, 0)
# Attach the camera dummy node to the player.
self.camera_dummy_node.reparentTo(self.player)
# Attach the camera to the dummy node.
camera.reparentTo(self.camera_dummy_node)
# Position the camera
camera.setPos(0, -30, 9) # X = left & right, Y = zoom, Z = Up & down.
camera.setHpr(0, -25, 0) # Heading, pitch, roll.
camera.lookAt(self.player) # Make the camera follow the player.
# end loadModels
# This function is meant to flag an object as something we can pick.
def makePickable(self, newObj):
newObj.setTag("pickable","true")
# This function finds the closest object to the camera that has been hit
# by our ray.
def getPosition(self, mousepos):
self.pickerRay.setFromLens(base.camNode, mousepos.getX(),mousepos.getY())
self.picker.traverse(render)
if self.queue.getNumEntries() > 0:
self.queue.sortEntries() # Get the closest object.
self.position = self.queue.getEntry(0).getIntoNodePath()
parent = self.position.getParent()
self.position = None
while parent != render:
if parent.getTag("pickable") == "true":
self.position = parent
return parent
else:
parent = parent.getParent()
return None
def moveTo(self, position):
if self.playerMovement != None:
self.playerMovement.pause()
self.stopWalkAnim()
# This is the "start" position
sPos = self.player.getPos()
# Calculate the new hpr
# Create a tmp NodePath and set it to the start position
# Then make it "lookAt" the position we want to be at.
# It will then create the hpr that we can turn to.
a = NodePath('tmp')
a.setPos(sPos)
a.lookAt(position)
newHpr = a.getHpr()
# Create a turn animation from current hpr to the calculated new hpr.
playerTurn = self.player.hprInterval(1,Point3(newHpr[0] + self.rotationOffset,newHpr[1],newHpr[2]),startHpr = self.player.getHpr())
# 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
if sPos[0] > position[0]:
distanceX = sPos[0] - position[0]
else:
distanceX = position[0] - sPos[0]
if sPos[1] > position[1]:
distanceY = sPos[1] - position[1]
else:
distanceY = position[1] - sPos[1]
distance = sqrt((distanceX * distanceX) + (distanceY * distanceY))
# Create a movement animation using the 2 positions
playerMove = self.player.posInterval( (distance / self.movementSpeed), position, startPos = sPos)
# Put the animations into a sequence and create an event that will be
# called when its finished.
self.playerMovement = Sequence(playerTurn, playerMove, name = "playerMove")
self.playerMovement.setDoneEvent("player-stopped")
# startFrame, stopFrame, playRate, loop
#self.actorMovement.setupPlay(-1, -1, 5, 0)
# Start the walking animation, then start the turn+move anims
self.player.loop("walk")
self.playerMovement.start()
def stopWalkAnim(self):
# This is called when the turn+move anim has finished.
# We can then stop the walk anim.
self.player.stop("walk")
self.playerMovement = None
def destination(self):
self.getPosition(base.mouseWatcherNode.getMouse())
if self.position != None:
self.player.moveTo(getPos(self.position))
mousePicker = Picker()
run()
But unfortunately, when I run it, I get this error message whenever I click the mouse:
I’m not really sure how to solve this. So here I am again, pestering you all for advice . You see, I don’t really have any idea what I’m doing, I’m just sort of making it up as I go along and trying out different things. So if anybody can help me with this I’d be eternally grateful .
Cheers