Point & Click Player Movement

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 :unamused:. 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 :smiley:.

Cheers

Try:

    def destination(self):
        self.getPosition(base.mouseWatcherNode.getMouse())
        if self.position != None:
            self.player.setPos(getPos(self.position))

Ah drat! Nope, that didn’t work either :frowning:. Thanks for the suggestion though. I just seem to have a knack for choosing the most difficult programming tasks don’t I?

So I’ll take any help that I can get. So any and all suggestions are most gratefully welcomed. Thanks again.

as far as I can see, moveTo is a member of your Picker class, not of the player object. use: self.moveTo(blah)

Thanks Yellow, that’s stopped it crashing. But frustratingly, when I now click on the environment nothing at all happens. I used this code:

   def destination(self): 
        self.getPosition(base.mouseWatcherNode.getMouse()) 
        if self.position != None:
            self.moveTo

I’m pretty sure that I’m missing something here, I tried using self.moveTo(self.position) and a few other combinations, but all resulted in a crash and error message. Any ideas?

As always, thanks for your help.

Can you post the error log?

anyway, I’ll paste you a picker / game example I’m using:

Picker:


#for collision stuff 
from pandac.PandaModules import * 


class Picker:
    def __init__(self): 
        #setup collision stuff 

        self.picker = CollisionTraverser() 
        self.queue  = CollisionHandlerQueue() 

        self.pickerNode = CollisionNode('mouseRay') 
        self.pickerNP = camera.attachNewNode(self.pickerNode) 
        self.pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) 
        self.pickerRay = CollisionRay() 
        self.pickerNode.addSolid(self.pickerRay) 
        self.picker.addCollider(self.pickerNP, self.queue) 

        #this holds the object that has been picked 
        self.pickedObj=None 

   #this function is meant to flag an object as being somthing 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 getObjectHit(self, mpos): #mpos is the position of the mouse on the screen 
        self.pickedObj=None #be sure to reset this 
        self.pickerRay.setFromLens(base.camNode, mpos.getX(),mpos.getY()) 
        self.picker.traverse(render)
        if self.queue.getNumEntries() > 0: 
            self.queue.sortEntries()

            cEntry = self.queue.getEntry(0)
            obj = cEntry.getIntoNodePath().getParent()

            while obj != render:
                if obj.getTag('pickable')=='true':
                    self.pickedObj = obj
                    return obj, cEntry
                else:
                    obj = obj.getParent()
                    
        return None, None


    def getPickedObj(self): 
        return self.pickedObj       

    def pick(self):
        obj, cEntry = self.getObjectHit( base.mouseWatcherNode.getMouse())
        return cEntry, obj

Game:


from picker import Picker

class Game(DirectObject):

    def __init__(self):
        self.picker = Picker()
        # make pickable stuff etc....
        self.accept('mouse1',self.select)

    def select(self):
        cEntry, obj = self.picker.pick()

        if not cEntry:
            return
        
        dpos = cEntry.getSurfacePoint(obj)
        self.player.moveTo(dpos)
        return

Oh that’s marvelous Yellow! Thanks very much for sharing with me, I think this will be a big help.

I’ll go do some more experimenting with it and let you know how I get on :unamused:.

Cheers

Sigh I still can’t get it to work. The code runs just fine and there are no error messages, but the player simply refuses to move to the clicked position (in fact, no animation plays at all, he just stands there).

Anyway, following your examples, this is what I came up with (first the mypicker module, I used your example for this :smiley:):


#for collision stuff
from pandac.PandaModules import* # Import the Panda Modules 

class Picker: 
    def __init__(self):
        # Setup collision stuff 
        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)
        #this holds the object that has been picked
        self.pickedObj = None
        
    # 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 getObjectHit(self, mpos):
        self.pickedObj = None #be sure to reset this
        self.pickerRay.setFromLens(base.camNode, mpos.getX(),mpos.getY()) 
        self.picker.traverse(render) 
        if self.queue.getNumEntries() > 0: 
            self.queue.sortEntries() # Get the closest object.
            
            cEntry = self.queue.getEntry(0)
            obj = cEntry.getIntoNodePath().getParent()
            
            while obj != render:
                if obj.getTag('pickable')=='true':
                    self.pickedObj = obj
                    return obj, cEntry
                else:
                    obj = obj.getParent()
                    return None, None

    def getPickedObj(self):
        return self.pickedObj       

    def pick(self):
        obj, cEntry = self.getObjectHit( base.mouseWatcherNode.getMouse())
        return cEntry, obj 

Next the main game code:


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
from mypicker import Picker # Import the mypicker.py module 

class Game(DirectObject): 
    def __init__(self):
        self.picker = Picker() # Module's class name
        base.disableMouse() # Disable default camera.
        self.loadModels()
        self.rotationOffset = 0
        self.playerMovement = None
        self.movementSpeed = 0.5
        self.accept("player-stopped", self.stopWalkAnim) 
        self.accept("mouse1", self.select)
        
    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")
        
    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 select(self):
        cEntry, obj = self.picker.pick()

        if not cEntry:
            return
       
        dpos = cEntry.getSurfacePoint(obj)
        self.player.moveTo(dpos)
        return
        
m = Game() 
    
run()
        

Now, I’m not sure if this has anything to do with it, but I had to define ‘makePickable’ in both the module and the main game code, because I got an error when I first ran the code without it in the main game code.

But that aside, to my inexperienced eyes, it looks like it ‘should’ work. I really hope that you can see where I’ve gone wrong and point me in the right direction. Thanks a lot.

I fixed up the picker class (see below) I also noticed the your code in moveTo wouldnt work. your first line tells it to skip everything if the playerMovement is not None. Means it would never start. Just keep the first 2 lines under that if statement.


#for collision stuff 
from pandac.PandaModules import* # Import the Panda Modules 

class Picker: 
    def __init__(self): 
        # Setup collision stuff 
        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.pickerNP, self.queue) 
        #this holds the object that has been picked 
        self.pickedObj = None 
        
    # 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 getObjectHit(self, mpos): 
        self.pickedObj = None #be sure to reset this 
        self.pickerRay.setFromLens(base.camNode, mpos.getX(),mpos.getY()) 
        self.picker.traverse(render) 
        if self.queue.getNumEntries() > 0: 
            self.queue.sortEntries() # Get the closest object. 
            
            cEntry = self.queue.getEntry(0) 
            obj = cEntry.getIntoNodePath().getParent()
            
            while obj != render:
                if obj.getTag('pickable')=='true': 
                    self.pickedObj = obj 
                    return obj, cEntry
                obj = obj.getParent()
                  
            return None, None 

    def getPickedObj(self): 
        return self.pickedObj        

    def pick(self): 
        obj, cEntry = self.getObjectHit( base.mouseWatcherNode.getMouse())
        return cEntry, obj 

Drat! This is gonna drive me crazy. Still no joy. I’ve changed the code as you suggested, to this:

def moveTo(self, position):
        if self.playerMovement.pause():
            self.stopWalkAnim()

instead of:

def moveTo(self, position):
    if self.actorMovement != None:
        self.actorMovement.pause()
        self.stopWalkAnim() 

But, sadly, it still won’t work. I have a horrible feeling that I’m going to have to start all over from scratch :imp:. Well, this time, I’m going to try to follow Sandman’s examples (I hope he doesn’t mind me posting this, but here is his code):

BoardWalker.py

from math import sqrt
from direct.interval.IntervalGlobal import *
from direct.actor import Actor

class BoardWalker(DirectObject):

  def __init__(self, modelPath, walkingModelPath, pos, hpr, scale,
               rotationOffset = 0, movementSpeed = 0.5):
    self.actor = Actor.Actor(modelPath, {'walk':walkingModelPath})
    self.actor.setScale(scale, scale, scale)
    self.actor.reparentTo(render)
    self.actor.setPos(pos)
    self.actor.setHpr(hpr[0], hpr[1], hpr[2])
    self.rotationOffset = rotationOffset
    self.accept('actor-stopped', self.__stopWalkAnim)
    self.actorMovement = None
    self.movementSpeed = movementSpeed
   
  def moveTo(self, position):
    if self.actorMovement != None:
        self.actorMovement.pause()
        self.__stopWalkAnim()
       
    # This is the "start" position
    sPos = self.actor.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.
    actorTurn = self.actor.hprInterval(1,Point3(newHpr[0] + self.rotationOffset,newHpr[1],newHpr[2]),startHpr = self.actor.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 coordinate base 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
    actorMove = self.actor.posInterval( (distance / self.movementSpeed), position, startPos = sPos)

    # Put the animations into a sequnce and create an event that will be
    # called when its finished.
    self.actorMovement = Sequence(actorTurn, actorMove, name = 'actorMove')
    self.actorMovement.setDoneEvent('actor-stopped')

    # startFrame, stopFrame, playRate, loop
    #self.actorMovement.setupPlay(-1, -1, 5, 0)

    # Start the walking animation, then start the turn+move anims
    self.actor.loop('walk')
    self.actorMovement.start()

  def __stopWalkAnim(self):
    # This is called when the turn+move anim has finished.
    # We can then stop the walk anim.
    self.actor.stop('walk')
    self.actorMovement = None 

Board.py

import direct.directbase.DirectStart
from pandac.PandaModules import *
from direct.interval.IntervalGlobal import *
from direct.gui.DirectGui import *
from direct.actor import Actor

from boardWalker import *

#First we define some contants for the colors
BLACK = Vec4(0,0,0,1)
WHITE = Vec4(1,1,1,1)
HIGHLIGHT = Vec4(0,1,1,1)
PIECEBLACK = Vec4(.15, .15, .15, 1)

#This is derived from the mathmatical of a plane, solved for a given point
def PointAtZ(z, point, vec):
  return point + vec * ((z-point.getZ()) / vec.getZ())

#A handy little function for getting the proper position for a given square
def SquarePos(i):
  return Point3((i%8) - 3.5, int(i/8) - 3.5, 0)

#Helper function for determining wheter a square should be white or black
#The modulo operations (%) generate the every-other pattern of a chess-board
def SquareColor(i):
  if (i + ((i/8)%2))%2: return BLACK
  else: return WHITE

class Board(DirectObject):

  def __init__(self):
    self.accept('escape', sys.exit)              #Escape quits
    base.disableMouse()                          #Disble mouse camera control
    camera.setPosHpr(0, -13.75, 6, 0, -25, 0)    #Set the camera
    self.setupLights()                           #Setup default lighting

    #Since we are using collision detection to do picking, we set it up like
    #any other collision detection system with a traverser and a handler
    self.picker = CollisionTraverser()            #Make a traverser
    self.pq     = 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)
    #Everything to be picked will use bit 1. This way if we were doing other
    #collision we could seperate it
    self.pickerNode.setFromCollideMask(BitMask32.bit(1))
    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.pq)
    #self.picker.showCollisions(render)

    #Now we create the chess board and its pieces

    #We will attach all of the squares to their own root. This way we can do the
    #collision pass just on the sqaures and save the time of checking the rest
    #of the scene
    self.squareRoot = render.attachNewNode("squareRoot")
   
    #For each square
    self.squares = [None for i in range(64)]
    for i in range(64):
      #Load, parent, color, and position the model (a single square polygon)
      self.squares[i] = loader.loadModelCopy("MODELS/square")
      self.squares[i].reparentTo(self.squareRoot)
      self.squares[i].setPos(SquarePos(i))
      self.squares[i].setColor(SquareColor(i))
      #Set the model itself to be collideable with the ray. If this model was
      #any more complex than a single polygon, you should set up a collision
      #sphere around it instead. But for single polygons this works fine.
      self.squares[i].find("**/polygon").node().setIntoCollideMask(BitMask32.bit(1))
      #Set a tag on the square's node so we can look up what square this is
      #later during the collision pass
      self.squares[i].find("**/polygon").node().setTag('square', str(i))
      #We will use this variable as a pointer to whatever piece is currently
      #in this square
      self.squares[i].piece = None
      # Load in the actor
      self.boardWalker = BoardWalker('MODELS/panda-model','MODELS/panda-walk4',SquarePos(1),[0, 0, 0], 0.001, 180)
      #This will represent the index of the currently highlited square
      self.hiSq = False
      #This wil represent the index of the square where currently dragged piece
      #was grabbed from
      self.dragging = False
      #Start the task that handles the picking
      self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask')
      self.accept('mouse1', self.selectSquare)       #left-click grabs a piece

  def mouseTask(self, task):
    #This task deals with the highlighting and dragging based on the mouse
   
    #First, clear the current highlight
    if self.hiSq is not False:
      self.squares[self.hiSq].setColor(SquareColor(self.hiSq))
      self.hiSq = False
     
    #Check to see if we can access the mouse. We need it to do anything else
    if base.mouseWatcherNode.hasMouse():
      #get the mouse position
      mpos = base.mouseWatcherNode.getMouse()
     
      #Set the position of the ray based on the mouse position
      self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
     
      #Do the actual collision pass (Do it only on the squares for
      #efficiency purposes)
      self.picker.traverse(self.squareRoot)
      if self.pq.getNumEntries() > 0:
        #if we have hit something, sort the hits so that the closest
        #is first, and highlight that node
        self.pq.sortEntries()
        i = int(self.pq.getEntry(0).getIntoNode().getTag('square'))
        #Set the highlight on the picked square
        self.squares[i].setColor(HIGHLIGHT)
        self.hiSq = i
         
    return Task.cont

  def selectSquare(self):
    if self.hiSq != None:
      self.boardWalker.moveTo(SquarePos(self.hiSq))
   
  def setupLights(self):    #This function sets up some default lighting
    lAttrib = LightAttrib.makeAllOff()
    ambientLight = AmbientLight( "ambientLight" )
    ambientLight.setColor( Vec4(.8, .8, .8, 1) )
    lAttrib = lAttrib.addLight( ambientLight )
    directionalLight = DirectionalLight( "directionalLight" )
    directionalLight.setDirection( Vec3( 0, 45, -45 ) )
    directionalLight.setColor( Vec4( 0.2, 0.2, 0.2, 1 ) )
    lAttrib = lAttrib.addLight( directionalLight )
    render.attachNewNode( directionalLight.upcastToPandaNode() )
    render.attachNewNode( ambientLight.upcastToPandaNode() )
    render.node().setAttrib( lAttrib )


#Do the main initialization and start 3D rendering
w = Board()
run() 

I’ve tested this code and it works, but the trouble with this is (apart from a small rotation bug) this code uses the chess board from the Picker tutorial which is made up of several individual square models.

Most game levels are composed of just one single environment model and not lots of individual squares, so I’m finding it really difficult to figure out how to make the player move to a clicked position ON the environment model, instead of moving to an individually clicked model.

Anyway, thanks for all your help, if you (or anybody else for that matter) think of anything else to try, please post.

Cheers

I might aswell hand you an old test project of mine that contains point and click movement. I supose you’ll have to twist your mind a little to follow my coding style though :stuck_out_tongue:

Point 'n click example

For anyone interested, it also contains an alternative to the FMOD sound lib. (audiere with python bindings) I havent documentated the bindings yet though, and havent added positional sound yet either.
Its compiled for windows, so when using linux, switch it off in soundmgr.py (set AUDIERE = True to False)

This is wonderful Yellow! I really, really appreciate this. I knew that implementing mouse controls was going to be much harder than implementing keyboard controls, but sheez I didn’t think it was going to be this hard :unamused:.

Well, I’m really going to try my best to get this working, but it may simply be beyond my abilities at this point. I’ll try to keep you all posted on my progress. Again thanks for all your help with this.

Cheers

Oh! I am so, so close to making this work. I just have a couple of small problems to work out.

First off, there is something wrong with my playerTurn animation, when I click on a position, he turns full circle in the opposite direction and then walks backwards to it (the camera also moves backwards with him).

I am pretty sure that it has something to do with the way the camera_dummy_node is positioned. I have the Hpr of the camera_dummy_node set to (180, 0, 0) so that I can look at the player from behind.

If I change the Hpr of the camera_dummy_node to (0, 0, 0) it improves things, but now I’m looking at the player from the font, and when I click on a position, he turns and then moves in the right direction, but he still walks backwards toward it (the camera now moves forward with him).

It would be almost right, if I could just figure out some way to turn the player around to face in the other direction, so that he can walk forward to the clicked position instead of backwards. I’ve tried setting the players Hpr, but it has no effect.

So any advice here would be most welcome. Anyway, here is the code:

I’ve highlighted what I think is the offending code. Everything else is working beautifully, I’m just stuck on this problem :unamused:.

Cheers

might be that the model is made facing backward. you might try to change


playerTurn = self.player.hprInterval(1,Point3(newHpr[0] + self.rotationOffset,newHpr[1],newHpr[2]),startHpr = self.player.getHpr()) 

to


playerTurn = self.player.hprInterval(1,Point3(newHpr[0] *-1,newHpr[1],newHpr[2]),startHpr = self.player.getHpr()) 

As far as I can see, the roatation offset has no real use, it only adds .2 to the eventual heading. I´ve multiplied the heading (H) with -1 to reverse the facing.

Thanks for the suggestion Yellow. I’ve just tried it, but no luck, the silly chap still walks backwards.

I’m really wracking my brain on this one. If I don’t use ‘playerTurn’ at all, and just use ‘playerMove’ it works fine. I click a position on the ground and the player plays his walk animation and moves to the clicked position (it looks a bit funny if you click to the left or right of him, because he doesn’t change his heading, he just sort of slides across the screen to the clicked position. But at least it works :smiley:).

But now I need some way to make my player turn (change his heading) to face in the direction of the clicked position and then move to that position. It’s a real pain in the neck to figure out :imp:.

Thanks heaps for trying to help.