Mouse Input Tutorial

Hi, I’m really, really desperate for a tutorial that can teach me how to use the mouse for point-and-click style actor movement.

I’ve followed the BVW tutorial, and it was great for teaching me how to setup a game for keyboard controls, but I have no idea how to translate it to use the mouse for actor movement instead of the keyboard.

What I want to do, is have a cursor on the screen, when I move the cursor over a spot on the ground/environment and left click, my actor moves to that spot.

If I click a spot on the ground to the left or right of my actor, then I want my actor to turn and walk in the direction of the click, and even turn full about in the opposite direction if I happen to click a spot behind them (hope that makes sense).

The BVW tutorial uses this code:

def start(self):
        self.setupCollisions()
        
        #self.pandaIdle.loop()
        
        # setup key controls
        self.acceptOnce("arrow_up",self.walk)
        self.acceptOnce("arrow_left",self.turn,[-1])
        self.acceptOnce("arrow_right",self.turn,[1])

        self.accept("arrow_up-up",self.stopWalk)
        self.accept("arrow_left-up",self.stopTurn)
        self.accept("arrow_right-up",self.stopTurn)
        
        # setup picking
        self.picker = Picker()
        self.panda.setTag("event","hit-panda")
        self.accept("mouse1",self.pick)
        self.accept("hit-panda",self.hitPanda)
        
        # start camera task
        taskMgr.add(self.cameraFollowTask,'cameraFollowTask')
        
        # start music
        SoundInterval(self.music).loop()
    # end start

Does anybody know how to change it so that the actor’s movement is controlled by mouse clicks and not by the keyboard? How do you get the location of the point clicked on and then make the actor move to it?

I know it’s a lot to ask, but if somebody could write an example or tutorial for this, I’d be eternally grateful :smiley:.

Thanks

Hi,

The problem you are having is not that simple. As i never used panda i dont know how to do this. But i can give you some hints:

  • First you need to figure out where your click collides with the ground/object. This gives you the target position.
  • You game logic then needs to turn the character in this direction. Best you calculate the angle between the view direction and the target position. Turn into until this angle is low (<2°).
  • Then let you character move forward until he reaches the point or the angle is bigger then (>5°), repeat step 2 if this happens.

This however will not work if you have objects in the way which prevent the character from moving forward. In this case you would need some more complicated algorithm to figure out a path. For further informations look for pathfinding algorithms. This is part of AI and not easy at all. (specially not in 3d environments)

Thanks very much for trying to help me Tschaess. Now I understand why it’s so rare to find mouse controlled movement in most 3D game engines these days. Oddly, people seem to refer to ‘mouse point-and-click’ style games as simple, but it looks like there is nothing simple about them at all.

Anyway, I’ve been searching the Internet, trying to find information on how to implement this, and I found something called the A Star Algorithm which looks like it can take care of the pathfinding for me (I just need to find a python example of it :unamused:).

I also came across an excellent tutorial titled ‘Mouse Ray Picking Explained’ written by Brian Hook: indiegamedev.tucows.com/blog/_ar … 75114.html

At the moment, it’s WAY over my head. But I really, really want mouse controls, so I’ll keep trying to understand it and make it work in Panda.

Please, if anybody has already managed to do something like this in Panda, could you please share an example of your code with a poor confused noob.

In fact, any help at all would be wonderful, cause I’m not sure I even know where to begin :blush:.

In the end, I’d really love to have something like a GameControls.py module that includes both mouse and keyboard controls, that you can toggle between ‘in game’ with the press of a button (I think this is the best way to cater to different playing styles). I would then import and re-use this module in every game I ever made.

Of course, it goes without saying, that I’d more than willingly share it with the Panda community (specially if they help write it hint-hint :laughing:).

Thanks everyone

Hi Pesky,

maybe you should have a look at the “picking tutorial” that’s included in the Panda3D release. It explains how to use the mouse to identify and drag 3D objects.

From my point of view you will have to do this:

  • set up a picking algorithm like this in the tutorial
  • let the collision ray collide only with the floor
  • on mouse click get the 3D point from the collision between ray and floor (search this forum for “collision point”)

Then you should have the position where your player has to go to.
You will still need a path finding algorithm though.

Hope this helps. :slight_smile:

Thanks Jadawin, yes that does help. I just need to keep experimenting with this until I figure it out :laughing:.

Of course, I would have to pick one of the hardest things to implement in a 3d game :unamused:. Ah well, I 'spose it’s good for me (I’m certainly learning more about programming than I ever thought I could).

Here is a link to the picker.py module, just incase you didnt have it yet:

etc.cmu.edu/bvw/scripting/do … /Picker.py

It helped me alot, and you could just import in into your project…

Good luck

Thanks for that link Yellow. Yep, every little bit helps :smiley:. I think that I may have found another example of the picker.py code in the Panda manual itself, called ‘Example for Clicking On 3D Objects’.

I think it’s a new addition to the manual, cause I never noticed it before :blush:.

# This is a small example program for clicking on 3D objects. A panda, a teapot, 
# and a cube will be on screen. When you click on the screen the console will 
# tell you the nodePath of what you have clicked on. Its basically a watered down 
# version of the tutorial included with Panda 3D 1.0.4. However, all the functionality
# for picking 3D objects is encapsulated into the Picker class which you may feel 
# free to use in your own code.

import direct.directbase.DirectStart
#for the events
from direct.showbase import DirectObject
#for collision stuff
from pandac.PandaModules import *


class Picker(DirectObject.DirectObject):
   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.pickerNode, self.queue)

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

      self.accept('mouse1', self.printMe)

   #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()
         self.pickedObj=self.queue.getEntry(0).getIntoNodePath()

         parent=self.pickedObj.getParent()
         self.pickedObj=None

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

   def getPickedObj(self):
         return self.pickedObj

   def printMe(self):
         self.getObjectHit( base.mouseWatcherNode.getMouse())
         print self.pickedObj

mousePicker=Picker()

#load these models
panda=loader.loadModel('panda')
teapot=loader.loadModel('teapot')
box=loader.loadModel('box')

#put them in the world
panda.reparentTo(render)
panda.setPos(camera, 0, 100,0)

teapot.reparentTo(render)
teapot.setPos(panda, -30, 0, 0)

box.reparentTo(render)
box.setPos(panda, 30,0,0)

mousePicker.makePickable(panda)
mousePicker.makePickable(teapot)
mousePicker.makePickable(box)

run() 

Anyway, I’m going to try loading an environment model, instead of the panda, teapot…etc. With luck, when I click on the environment, the console should tell me the nodePath of the model I clicked on (ie: environment).

If that works, then I’m going to try making the console tell me the world ‘coordinates’ of the position clicked on by the mouse. For example, if I click a point on the environment, then I want the console to print out (0, 0, 0) or something, which will give me the X, Y, Z world coordinates of the point I clicked on.

If I click on the sky or somewhere else that is not part of the environment model, then nothing at all should happen, because only the environment model is designated as ‘pickable’.

I don’t know if this makes sense, or even if it’s possible, but I’ll play around with it and see what happens.

So now I need to figure out some way to tell the code to return the ‘world position’ of the mouse click, instead of the nodePath of the model and then print that info to the console.

At the moment though, I’m not really sure how to do this, so if anybody is willing to help me out by telling me which lines of code I need to change, I’d be really grateful.

Thanks

If you use the module I linked to above, just set up the picker according to the manual and make it print cEntry
That displays a xyz coordinate to the console. Not sure if this is what you’re looking for, only been toying with panda for a few days…

def pick(self):
cEntry = self.picker.pick()
print cEntry # the only line you need to add to the tutorial code

Thanks heaps Yellow, I’ll try that and see if it works.

Thanks a lot