Point & Click Turning Bug!


#6

Thankyou so much for this Russ. This time I was able to get the code working. But sadly, it’s still not doing what I want it to do :imp:.

The player now makes a small quarter turn in the direction of the click, which results in him sometimes moving sideways or even backwards to the clicked position (sometimes he turns and moves correctly too, it just depends on where you click :unamused:).

It’s kind of difficult to explain. So it’s probably better to show you. I don’t know if this is of any use, or even if you’d have the time to test it, but I’ve uploaded the models that I’m using to Rapidshare. So if you want to, you could take a look at it with the same models:

rapidshare.de/files/18379082/Tes … s.rar.html

And this is the new code (Note: The Fleet model isn’t animated, so I removed the animation references):

# This program turns a model to the position (point 3) of a left mouse click 
# on a 3d surface and then moves it to that position.

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)
from math import sqrt
from direct.showbase.PythonUtil import closestDestAngle
import sys  

class Picker(DirectObject): 
    def __init__(self): 
        base.disableMouse()
        # Position the camera
        camera.setPos(0, -35, 18) # X = left & right, Y = zoom, Z = Up & down.
        camera.setHpr(0, -25, 0) # Heading, pitch, roll.
        # Declare variables
        self.position = None
        self.playerMovement = None
        self.movementSpeed = 8.0 # Controls how fast the player moves.
        # Load an environment
        self.environ = loader.loadModel("MODELS/grass")
        self.environ.reparentTo(render)
        self.environ.setPos(0, 0, 0)
        self.player = loader.loadModel("MODELS/fleet")
        self.player.reparentTo(render)
        self.player.setPos(0, 0, 0)
        self.player.setHpr(0, 0, 0)
        self.player.setColor(Vec4(0, 148, 213, 1))
        self.npLook = self.player.attachNewNode("npLook")
        # 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)
        # Setup controls
        self.accept("escape", sys.exit) 
        self.accept('mouse1', self.moveToPosition)

    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()
            # This is the clicked position. 
            self.position = self.queue.getEntry(0).getSurfacePoint(self.environ) 
            return None
        
    def moveToPosition(self):
        # Get the clicked position 
        self.getPosition(base.mouseWatcherNode.getMouse())
        # Calculate the new hpr
        self.npLook.lookAt(self.position) # Look at the clicked position.
        currHpr = self.player.getHpr()
        newHpr = self.npLook.getHpr(render)
        newH = closestDestAngle(currHpr[0], newHpr[0])
        # Create a turn animation from current hpr to the calculated new hpr.
        playerTurn = self.player.hprInterval(.2, 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.getPos()
        distance = travelVec.length()
       
        playerMove = self.player.posInterval((distance / self.movementSpeed), self.position)

        self.playerMovement = Sequence(playerTurn, playerMove)
        self.playerMovement.start() 
        
p = Picker() 

run()

This has got to be one of the hardest and most frustrating things I’ve ever tried to do. I can’t tell you how much I appreciate your help with this. My respect and awe for game programmer’s has increased enormously.

Thanks heaps.


#7

I didn’t really test the code before I posted.

change newH to:


newH = currHpr[0] + closestDestAngle(currHpr[0], newHpr[0]) 

I think that should fix it, but I haven’t tested this either.
If it still acts funny, use some print statements to help figure out what is going on.


#8

nevermind that last post, it won’t work either. Let me actually think about it and I will post something new


#9

so this should work:

.
.
.
        #in __init__
        self.npLook = render.attachNewNode("npLook")
    .
    .
    .
        #in moveToPosition
        # Calculate the new hpr 
        self.npLook.setPos(self.player.getPos())
        self.npLook.lookAt(self.position) # Look at the clicked position.
        currHpr = self.player.getHpr()
        newHpr = self.npLook.getHpr()
        newH = closestDestAngle(currHpr[0], newHpr[0]) 
    .
    .
    .

#10

Oh Wow!!! This is fantastic!!!

Russ, I think you’ve done it. I’ve just tested it, and it works almost perfectly. It’s a HUGE improvement over the original code.

But there’s just one little hiccup, that weird ‘spin around in the opposite direction’ bug is still there, but not as bad as before. This time it only occurs in 3 places.

The first time is when traveling in a straight line ‘north to south’ through the middle of the map, if I first click on a point in the middle of the map, the player moves nicely to that point. But if I click again to resume traveling (in the same direction), the player spins a full circle before he moves to the next click.

The next two occurences are when traveling clockwise and anti-clockwise around the edges of the map (by clicking in each corner). I’ve made a quick image to show you what I mean (this nasty little bug is consistent and repeatable, it happens in the same spots over and over).

Anyway, bugs or no bugs, I can’t thankyou enough for taking the time to work on this for me. This thing has had me almost in tears. I didn’t think I’d ever solve it. Without you’re help, I think I’d have given up on it. Thankyou a thousand times over.

Cheers


#11

Those extra spins have to do with wrapping of the heading angle. Unfortunately, closestDestAngle doesn’t account for that. One way to fix it would be to correct for the wrap yourself some time after the player hits its final heading (after the turn interval or after the enitre movement sequence). You could do this with a call like:

reducedH = self.player.getH()%360.0
self.player.setH(reducedH)


#12

Thanks Russ. I’ve just tried it. I tried putting it after the turn interval and then after the movement sequence, but in both cases, all it seemed to do was make the player spin in each one of the corners, instead of just the one :unamused:.

I’ve also discovered that swearing and threatening it with dire consequences doesn’t work either :smiley:. Do you have any ideas for what I should try next?

Thanks heaps


#13

Given how many other things Python and Panda3d handle automatically, I almost wouldn’t be surprised if there were a “swearing and threatening dire consequences” detector. Obviously, someone will have to build one in :wink:

try:
  myFunction(defaultOptions)
except SwearingAndThreateningDireConsequences:
  myFunction(-defaultOptions)

If you find that it’s spinning the wrong direction in all cases now, that’s good news: it probably means something in the equation is negative that should be positive. Maybe change an addition to a subtraction and try again?

It might also be helpful to walk through the calculation on paper. Write down the starting heading and your desired heading, then see if you can guess how the lerp function will go from one to the other. Doing this can help you isolate what about the lerp’s behavior isn’t matching up with your expectations. Even if it doesn’t solve the problem directly, you may gain a deeper insight to how the lerps work; that will almost always be helpful down the road.

Best of luck!
-Mark

[/code]


#14

Bummer! I’ve tried everything I can think of, I even tried defining ‘closestDestAngle’ as a function in my own code so that I could play with its values, but all to no effect.

I’m at my wit’s end here, it just seems to be an impossible task to get this working properly. I’m beginning to suspect that Panda3D might simply not be capable of implementing point & click game controls :cry: (sadly, a lot of engines can’t).

I’ve tried searching the internet for some type of point & click example codes (even ones written in another language, just to give me some idea of what to do) but I couldn’t find anything of use. Grrr I just don’t know what else to do.

Anyway, here is my ‘almost’ working code:

# This program turns a model to the position (point 3) of a left mouse click 
# on a 3d surface.

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)
from math import sqrt
import sys  

class Picker(DirectObject): 
    def __init__(self): 
        base.disableMouse()
        # Position the camera
        camera.setPos(0, -35, 18) # X = left & right, Y = zoom, Z = Up & down.
        camera.setHpr(0, -25, 0) # Heading, pitch, roll.
        # Declare variables
        self.position = None
        self.playerMovement = None
        self.movementSpeed = 8.0 # Controls how fast the player moves.
        # Load an environment
        self.environ = loader.loadModel("MODELS/grass")
        self.environ.reparentTo(render)
        self.environ.setPos(0, 0, 0)
        self.player = loader.loadModel("MODELS/fleet")
        self.player.reparentTo(render)
        self.player.setPos(0, 0, 0)
        self.player.setHpr(0, 0, 0)
        self.player.setColor(Vec4(0, 148, 213, 1))
        self.npLook = render.attachNewNode("npLook") 
        # 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)
        # Setup controls
        self.accept("escape", sys.exit) 
        self.accept('mouse1', self.moveToPosition)
        
    def closestDestAngle(src, dest):
        diff = src - dest
        if diff > 180:
            # if the difference is greater that 180 it's shorter to go the other way
            return src - (diff - 360)
        elif diff < -180:
            # or perhaps the OTHER other way...
            return src - (360 + diff)
        else:
            # otherwise just go to the original destination
            return dest

    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()
            # This is the clicked position. 
            self.position = self.queue.getEntry(0).getSurfacePoint(self.environ) 
            return None
        
    def moveToPosition(self):
        # Get the clicked position 
        self.getPosition(base.mouseWatcherNode.getMouse())
        # Calculate the new hpr
        self.npLook.setPos(self.player.getPos())
        self.npLook.lookAt(self.position) # Look at the clicked position.
        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(.2, Point3(newHpr[0], 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.getPos()
        distance = travelVec.length()
        # Put the animations into a sequence.
        playerMove = self.player.posInterval((distance / self.movementSpeed), self.position)
        self.playerMovement = Sequence(playerTurn, playerMove)
        reducedH = self.player.getH()%360.0
        self.player.setH(reducedH)
        self.playerMovement.start()
        
p = Picker() 

run()

Cheers


#15

The way it is now, the heading is being reduced before the movement sequence starts. This is why the player is spinning each time. The reduction needs to happen after the turn interval has finished.

    #define this somewhere
    def reducePlayerH(self):
        """reduce the player heading to eliminate wrap-around"""
        reducedH = self.player.getH()%360.0
        self.player.setH(reducedH) 
        .
        .
        .
        
        #in moveToPosition
        self.playerMovement = Sequence(playerTurn, Func(self.reduceH), playerMove) 
        

Alternatively, you can reduce the player heading before you calculate the closesDestAngle:


        # Calculate the new hpr
        self.npLook.setPos(self.player.getPos())
        self.npLook.lookAt(self.position) # Look at the clicked position.

        reducedH = self.player.getH()%360.0
        self.player.setH(reducedH) 

        currHpr = self.player.getHpr()
        newHpr = self.npLook.getHpr()
        newH = closestDestAngle(currHpr[0], newHpr[0]) 

#16

Thankyou for your infinite patience Russ, I am so incredibly grateful for all your help.

Anyway, I tried your second solution first (cause it looked easier :wink:) but sadly, it didn’t seem to make any difference to the spin bug.

So onto the next one, I defined a reducePlayerH() function and ran the code, but I’m getting a strange error message telling me that global name ‘reducedH’ is not defined:

I’ve stuffed something up again haven’t I? This is how I updated the code (I tried declaring a variable ‘self.reducedH = None’ but that doesn’t seem to help):

# This program turns a model to the position (point 3) of a left mouse click 
# on a 3d surface.

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)
from math import sqrt
import sys  

class Picker(DirectObject): 
    def __init__(self): 
        base.disableMouse()
        # Position the camera
        camera.setPos(0, -35, 18) # X = left & right, Y = zoom, Z = Up & down.
        camera.setHpr(0, -25, 0) # Heading, pitch, roll.
        # Declare variables
        self.position = None
        self.playerMovement = None
        self.movementSpeed = 8.0 # Controls how fast the player moves.
        self.reducedH = None
        # Load an environment
        self.environ = loader.loadModel("MODELS/grass")
        self.environ.reparentTo(render)
        self.environ.setPos(0, 0, 0)
        self.player = loader.loadModel("MODELS/fleet")
        self.player.reparentTo(render)
        self.player.setPos(0, 0, 0)
        self.player.setHpr(0, 0, 0)
        self.player.setColor(Vec4(0, 148, 213, 1))
        self.npLook = render.attachNewNode("npLook")
        # Declare functions.
        self.reducePlayerH()
        # 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)
        # Setup controls
        self.accept("escape", sys.exit) 
        self.accept('mouse1', self.moveToPosition)
        
    def closestDestAngle(src, dest):
        diff = src - dest
        if diff > 180:
            # if the difference is greater that 180 it's shorter to go the other way
            return src - (diff - 360)
        elif diff < -180:
            # or perhaps the OTHER other way...
            return src - (360 + diff)
        else:
            # otherwise just go to the original destination
            return dest

    def reducePlayerH(self):
        # Reduce the players heading to eliminate wrap-around
        self.reducedH = self.player.getH()%360.0
        self.player.setH(reducedH) 

    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()
            # This is the clicked position. 
            self.position = self.queue.getEntry(0).getSurfacePoint(self.environ) 
            return None
        
    def moveToPosition(self):
        # Get the clicked position 
        self.getPosition(base.mouseWatcherNode.getMouse())
        # Calculate the new hpr
        self.npLook.setPos(self.player.getPos())
        self.npLook.lookAt(self.position) # Look at the clicked position.
        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(.2, Point3(newHpr[0], 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.getPos()
        distance = travelVec.length()
        # Put the animations into a sequence.
        playerMove = self.player.posInterval((distance / self.movementSpeed), self.position)
        self.playerMovement = Sequence(playerTurn, Func(self.reducedH), playerMove)
        self.playerMovement.start()
        
p = Picker() 

run()

What have I done wrong?

Thanks heaps


#17

you define self.reducedH but reference reducedH which python thinks is a global variable

change reducedH to a local variable in the function so instead of

self.reducedH = player.getH()%360.0

use

reducedH = player.getH()%360


#18

Thanks Russ, I did as you suggested, but now I’m getting a new error message (:evil: it’s enough to make you scream, isn’t it?).

And the new code:

# This program turns a model to the position (point 3) of a left mouse click 
# on a 3d surface.

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)
from math import sqrt
import sys  

class Picker(DirectObject): 
    def __init__(self): 
        base.disableMouse()
        # Position the camera
        camera.setPos(0, -35, 18) # X = left & right, Y = zoom, Z = Up & down.
        camera.setHpr(0, -25, 0) # Heading, pitch, roll.
        # Declare variables
        self.position = None
        self.playerMovement = None
        self.movementSpeed = 8.0 # Controls how fast the player moves.
        self.reducedH = None
        # Load an environment
        self.environ = loader.loadModel("MODELS/grass")
        self.environ.reparentTo(render)
        self.environ.setPos(0, 0, 0)
        self.player = loader.loadModel("MODELS/fleet")
        self.player.reparentTo(render)
        self.player.setPos(0, 0, 0)
        self.player.setHpr(0, 0, 0)
        self.player.setColor(Vec4(0, 148, 213, 1))
        self.npLook = render.attachNewNode("npLook")
        # Declare functions.
        self.reducePlayerH()
        # 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)
        # Setup controls
        self.accept("escape", sys.exit) 
        self.accept('mouse1', self.moveToPosition)
        
    def closestDestAngle(src, dest):
        diff = src - dest
        if diff > 180:
            # if the difference is greater that 180 it's shorter to go the other way
            return src - (diff - 360)
        elif diff < -180:
            # or perhaps the OTHER other way...
            return src - (360 + diff)
        else:
            # otherwise just go to the original destination
            return dest

    def reducePlayerH(self):
        # Reduce the players heading to eliminate wrap-around
        reducedH = self.player.getH()%360.0
        self.player.setH(reducedH) 

    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()
            # This is the clicked position. 
            self.position = self.queue.getEntry(0).getSurfacePoint(self.environ) 
            return None
        
    def moveToPosition(self):
        # Get the clicked position 
        self.getPosition(base.mouseWatcherNode.getMouse())
        # Calculate the new hpr
        self.npLook.setPos(self.player.getPos())
        self.npLook.lookAt(self.position) # Look at the clicked position.
        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(.2, Point3(newHpr[0], 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.getPos()
        distance = travelVec.length()
        # Put the animations into a sequence.
        playerMove = self.player.posInterval((distance / self.movementSpeed), self.position)
        self.playerMovement = Sequence(playerTurn, Func(self.reducedH), playerMove)
        self.playerMovement.start()
        
p = Picker() 

run()

I dunno, maybe I’m just not cut out for this programming business :cry:.

Thanks again


#19

As russ told you above: self.reduceH is a virable
Func( … ) needs a callable object
I guess you should use Func( self.reducePlayerH ) instead
Just for the future:

self.playerMovement = Sequence(playerTurn, Func(self.reducedH), playerMove)
File "C:\Panda3D-1.0.5\direct\src\interval\FunctionInterval.py", line 275, in
__init__
assert(callable(function)) 

assert is a functions that checks a logical expression.
You see there is something that needs to be a function but is not a function
Then look at the line bove. There you can see the Func( … ) and then try to find whats the mistake
Hope this helps
Martin


#20

Thanks Martin, that fixed the AssertionError. But after all that trouble, that hateful spin bug is still there. The new function doesn’t seem to have made any difference at all :frowning:.

Again HUGE thanks to all of you.


#21

this worked for me. I used some default models so anyone could try it. The panda is modeled backwards, so when he turns, he will face opposite to the direction he is moving.

# This program turns a model to the position (point 3) of a left mouse click
# on a 3d surface and then moves it to that position.

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
from direct.showbase.PythonUtil import closestDestAngle
import sys 

class Picker(DirectObject):
    def __init__(self):
        base.disableMouse()
        # Position the camera
        camera.setPos(0, -0, 100) # X = left & right, Y = zoom, Z = Up & down.
        camera.setHpr(0, -90, 0) # Heading, pitch, roll.
        # Declare variables
        self.position = None
        self.playerMovement = None
        self.movementSpeed = 8.0 # Controls how fast the player moves.
        # Load an environment
        self.environ = loader.loadModel("environment")
        self.environ.reparentTo(render)
        self.environ.setPos(0, 0, 0)
        self.player = loader.loadModel("panda")
        self.player.reparentTo(render)
        self.player.setPos(0, 0, 0)
        self.player.setHpr(0, 0, 0)
        self.player.setColor(Vec4(0, 148, 213, 1))
        self.npLook = render.attachNewNode("npLook")
        # 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)
        # Setup controls
        self.accept("escape", sys.exit)
        self.accept('mouse1', self.moveToPosition)
        self.accept('r', self.reset)

    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()
            # This is the clicked position.
            self.position = self.queue.getEntry(0).getSurfacePoint(self.environ)
            print "position", self.position
            return None
       
    def moveToPosition(self):
        # Get the clicked position
        self.getPosition(base.mouseWatcherNode.getMouse())
        
        # Calculate the new hpr
        self.npLook.setPos(self.player.getPos())
        self.npLook.lookAt(self.position) # Look at the clicked position.
        reducedH = self.player.getH()%360.0
        self.player.setH(reducedH) 
        currHpr = self.player.getHpr()
        #print "curr", currHpr
        newHpr = self.npLook.getHpr()
        #print "new", newHpr
        #print self.npLook.getPos(render)
        newH = closestDestAngle(currHpr[0], newHpr[0])
        #print newH
        # Create a turn animation from current hpr to the calculated new hpr.
        playerTurn = self.player.hprInterval(.2, 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.getPos()
        distance = travelVec.length()
       
        playerMove = self.player.posInterval((distance / self.movementSpeed), self.position)

        self.playerMovement = Sequence(playerTurn, playerMove)
        self.playerMovement.start()
        #playerTurn.start()

    def reset(self):
        self.player.setHpr(0,0,0)
p = Picker()

run() 

#22

Russ!!! You did it!!! You solved it!!! Bless you a thousand times over!!! You are amazing!!!

Thankyou from the bottom of my heart. Last night I was feeling depressed and fed up and ready to chuck the whole thing in (I’ve edited my last post, cause it’s just embarrassing now :blush:).

I didn’t think this would ever work, and like a poor workman blaming his tools, I was blaming Panda (I thought it was missing some essential function that should make this work) when in reality it was my own lack at fault, not Panda’s (sorry Panda, never again, I promise).

Russ, I don’t know how to thankyou enough for this. Without your tremendous patience and help this would never have gotten done.

Thankyou so much.


#23

Hi again. I’ve been playing around with this code all day, it’s just wonderful :smiley:. I’ve also been studying it to try and learn what I did wrong.

However, I’m a little bit puzzled, I don’t know why it works and this is something that I’d really love to understand. Anybody in the mood to give a quick programming lesson :smiley:?

You see, in the function below, ‘reducedH = self.player.getH()%360.0’ and ‘self.player.setH(reducedH)’ seem to be called/referenced (sorry, I don’t know what the right term is) before any player movement has even taken place.

To my newbie mind, it seems like they’re setting the player’s heading before he’s even started to move. It seems to me that they should be called after the player’s movement, or at least after ‘playerTurn’, but obviously that’s not the correct approach. How come it works when it’s done this way?

def moveToPosition(self):
        # Get the clicked position
        self.getPosition(base.mouseWatcherNode.getMouse())
       
        # Calculate the new hpr
        self.npLook.setPos(self.player.getPos())
        self.npLook.lookAt(self.position) # Look at the clicked position.
        reducedH = self.player.getH()%360.0
        self.player.setH(reducedH)
        currHpr = self.player.getHpr()
        #print "curr", currHpr
        newHpr = self.npLook.getHpr()
        #print "new", newHpr
        #print self.npLook.getPos(render)
        newH = closestDestAngle(currHpr[0], newHpr[0])
        #print newH
        # Create a turn animation from current hpr to the calculated new hpr.
        playerTurn = self.player.hprInterval(.2, 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.getPos()
        distance = travelVec.length()
       
        playerMove = self.player.posInterval((distance / self.movementSpeed), self.position)

        self.playerMovement = Sequence(playerTurn, playerMove)
        self.playerMovement.start()
        #playerTurn.start() 

Sorry to be a such a pest, I’m just really trying to learn and understand all this programming stuff.

Cheers


#24

In the case above called is right: Because: self.player.setH() is a function and you call it.
It doesn’t matter if heading is set bevore player moved because H is initialy set to 0 if i’m right
0 % 360= 0 so there is no change on H.
What kind of programming lesson do you mean?
Martin


#25

Thanks for the reply Martin, I’m really just trying to understand why ‘calling’ these functions (thanks for the carification) before the player moves stops the spin bug, but calling them after playerTurn or the movement sequence doesn’t seem to have any effect at all.

Logically, it seems to me, that you should tell the player to turn and then add these functions to stop him turning too far. But obviously that’s not the way it’s done.

Ah well, no matter, the lovely thing works and that’s all that’s important :smiley:.

Cheers