|
|
|
Return to Scripting Issues
by Tiptoe » Tue Apr 18, 2006 1:50 am
Hi All, I hope you'll forgive me for starting a new thread. I was going to add this to my original thread, but that one's getting a bit long and I thought that this new post might get overlooked.
Anyway, I've made some progress on my point & click player controls. I think I've solved the problem of the model moving backwards to a clicked position. It seems that to work correctly, the model's orientation must be set in your modeling package to face away from the camera (when I used a model that had been modeled and exported facing backwards, it worked a treat  ).
But now I have a new problem. My code to make the player turn towards a clicked position, seems to have a bug in it.
This is what I want to happen. I click a position on the ground (the red X represents the clicked position).
The player should make a half turn to the right and face in the direction of the clicked position:
But this is what actually happens. I click this position on the ground and the player makes a full turn in the opposite direction and then faces in the right direction.
If I click in the opposite corner the same thing happens:
The strange thing is, this only happens when I click in the bottom corners of the map. When I click in the top corners of the map it works just fine.
Anyhow, this is the code:
- Code: Select all
# 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 # 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)) # 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()) # 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(self.position) # Look at the clicked position. newHpr = a.getHpr() # Create a turn animation from current hpr to the calculated new hpr. playerTurn = self.player.hprInterval(.2,Point3(newHpr[0],newHpr[1],newHpr[2])) playerTurn.start()
p = Picker()
run()
I'm really baffled by this one, so if anybody has any suggestions please advise me.
Cheers
 Tiptoe
-
Tiptoe
-
- Posts: 127
- Joined: Fri Mar 03, 2006 11:57 pm
by russ » Tue Apr 18, 2006 8:55 am
You can import a function from direct.showbase.PythonUtil called closestDestAngle. It should help.
- Code: Select all
def closestDestAngle(src, dest): # The function above didn't seem to do what I wanted. So I hacked # this one together. I can't really say I understand it. It's more # from impirical observation... GRW 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
Also, creating a temp nodepath every move might cause some extra memory usage. Its possible Panda will keep the nodepath around in the scene graph even though your function has lost its scope. It would be better to create a single nodepath and parent it to your player. Then you can call lookAt() on that single nodepath each move instead of creating a new one each time.
-
russ
-
- Posts: 223
- Joined: Wed May 04, 2005 5:32 pm
by Tiptoe » Tue Apr 18, 2006 11:32 am
Thanks Russ. I've tried to implement your suggestion, but I just don't have the knowledge or experience to make it work properly. This is what I've done (don't laugh, I really tried my best  ):
- Code: Select all
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()) # This is the "start" position sPos = self.player.getPos() diff = sPos - self.position # Start position - the clicked position. if diff > 180: # if the difference is greater that 180 it's shorter to go the other way return sPos - (diff - 360) elif diff < -180: # or perhaps the OTHER other way... return sPos - (360 + diff) else: # otherwise just go to the original destination return self.position playerTurn = self.player.hprInterval(.5,self.position) playerTurn.start()
p = Picker()
run()
Sadly, this code doesn't work. But it looks very similar to the code I'm using to MOVE the player to the clicked position (this particular piece of code works nicely): - Code: Select all
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() self.position = self.queue.getEntry(0).getSurfacePoint(self.environ) return None
def moveToPosition(self): # Get the clicked position self.getPosition(base.mouseWatcherNode.getMouse()) # This is the "start" position sPos = self.player.getPos() # 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] > self.position[0]: distanceX = sPos[0] - self.position[0] else: distanceX = self.position[0] - sPos[0] if sPos[1] > self.position[1]: distanceY = sPos[1] - self.position[1] else: distanceY = self.position[1] - sPos[1] distance = sqrt((distanceX * distanceX) + (distanceY * distanceY)) playerMove = self.player.posInterval((distance / self.movementSpeed), self.position, startPos = sPos) playerMove.start()
If I can get this code to work without the need for a dummy node, that'd be great, because I already have a camera_dummy_node attached to the player (so that I can rotate the camera around him).
Sorry to be such a pest. Can you see what I've done wrong?
Thanks very much.
 Tiptoe
-
Tiptoe
-
- Posts: 127
- Joined: Fri Mar 03, 2006 11:57 pm
by Tiptoe » Tue Apr 18, 2006 11:47 am
Oops, I forgot to mention, I eventually plan to combine both playerTurn and playerMove into the one function and then play them in sequence. Hopefully this will make the player turn in the direction of the mouse click and then move to that position.
Something like the following code (this is Sandman's example  ):
- Code: Select all
def moveToPosition(self): 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 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 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')
# 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
Cheers
 Tiptoe
-
Tiptoe
-
- Posts: 127
- Joined: Fri Mar 03, 2006 11:57 pm
by russ » Tue Apr 18, 2006 2:12 pm
So here is more what I was talking about. The periods represent the code already in there. Hope its not too confusing.
- Code: Select all
#imports . . . from direct.showbase.PythonUtil import closestDestAngle . . . class Picker(DirectObject): def __init__(self): . . . #after self.player has been set up self.npLook = self.player.attachNewNode("npLook") . . . 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, startPos = sPos)
self.playerMovement = Sequence(playerTurn, playerMove) self.playerMovement.setDoneEvent('player-stopped') # Start the walking animation, then start the turn+move anims self.player.loop('walk') self.playerMovement.start() . . .
-
russ
-
- Posts: 223
- Joined: Wed May 04, 2005 5:32 pm
by Tiptoe » Wed Apr 19, 2006 1:19 am
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  .
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  ).
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:
http://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):
- Code: Select all
# 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.
 Tiptoe
-
Tiptoe
-
- Posts: 127
- Joined: Fri Mar 03, 2006 11:57 pm
by russ » Wed Apr 19, 2006 9:34 am
I didn't really test the code before I posted.
change newH to:
- Code: Select all
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.
-
russ
-
- Posts: 223
- Joined: Wed May 04, 2005 5:32 pm
by russ » Wed Apr 19, 2006 11:03 am
nevermind that last post, it won't work either. Let me actually think about it and I will post something new
-
russ
-
- Posts: 223
- Joined: Wed May 04, 2005 5:32 pm
by russ » Wed Apr 19, 2006 12:10 pm
so this should work:
- Code: Select all
. . . #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]) . . .
-
russ
-
- Posts: 223
- Joined: Wed May 04, 2005 5:32 pm
by Tiptoe » Wed Apr 19, 2006 1:32 pm
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
 Tiptoe
-
Tiptoe
-
- Posts: 127
- Joined: Fri Mar 03, 2006 11:57 pm
by russ » Wed Apr 19, 2006 2:57 pm
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)
-
russ
-
- Posts: 223
- Joined: Wed May 04, 2005 5:32 pm
by Tiptoe » Thu Apr 20, 2006 2:12 am
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  .
I've also discovered that swearing and threatening it with dire consequences doesn't work either  . Do you have any ideas for what I should try next?
Thanks heaps
 Tiptoe
-
Tiptoe
-
- Posts: 127
- Joined: Fri Mar 03, 2006 11:57 pm
by Fixer » Thu Apr 20, 2006 9:55 am
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
- Code: Select all
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 Tiptoe wrote: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  . I've also discovered that swearing and threatening it with dire consequences doesn't work either  . Do you have any ideas for what I should try next? Thanks heaps
[/code]
-
Fixer
-
- Posts: 190
- Joined: Tue May 17, 2005 7:03 pm
- Location: Pittsburgh, PA
by Tiptoe » Thu Apr 20, 2006 11:20 am
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  (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:
- Code: Select all
# 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
 Tiptoe
-
Tiptoe
-
- Posts: 127
- Joined: Fri Mar 03, 2006 11:57 pm
by russ » Thu Apr 20, 2006 3:22 pm
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.
- Code: Select all
#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: - Code: Select all
# 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])
-
russ
-
- Posts: 223
- Joined: Wed May 04, 2005 5:32 pm
by Tiptoe » Fri Apr 21, 2006 3:14 am
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  ) 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:
C:\TUTS>ppython getrotation7.py DirectStart: Starting the game. Warning: DirectNotify: category 'Interval' already exists Known pipe types: wglGraphicsPipe (3 aux display modules not yet loaded.) Traceback (most recent call last): File "getrotation7.py", line 97, in ? p = Picker() File "getrotation7.py", line 36, in __init__ self.reducePlayerH() File "getrotation7.py", line 65, in reducePlayerH self.player.setH(reducedH) NameError: 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):
- Code: Select all
# 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
 Tiptoe
-
Tiptoe
-
- Posts: 127
- Joined: Fri Mar 03, 2006 11:57 pm
by russ » Fri Apr 21, 2006 9:32 am
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
-
russ
-
- Posts: 223
- Joined: Wed May 04, 2005 5:32 pm
by Tiptoe » Fri Apr 21, 2006 10:14 am
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?).
Microsoft Windows XP [Version 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp.
C:\Documents and Settings\Gigabyte User>cd C:\TUTS\
C:\TUTS>ppython getrotation7.py DirectStart: Starting the game. Warning: DirectNotify: category 'Interval' already exists Known pipe types: wglGraphicsPipe (3 aux display modules not yet loaded.) :express(warning): Adjusting global clock's real time by -3.6672 seconds. Traceback (most recent call last): File "getrotation7.py", line 99, in ? run() File "C:\Panda3D-1.0.5\direct\src\showbase\ShowBase.py", line 1603, in run self.taskMgr.run() File "C:\Panda3D-1.0.5\direct\src\task\Task.py", line 781, in run self.step() File "C:\Panda3D-1.0.5\direct\src\task\Task.py", line 728, in step self.__stepThroughList(taskPriList) File "C:\Panda3D-1.0.5\direct\src\task\Task.py", line 671, in __stepThroughLis t ret = self.__executeTask(task) File "C:\Panda3D-1.0.5\direct\src\task\Task.py", line 602, in __executeTask ret = task(task) File "C:\Panda3D-1.0.5\direct\src\showbase\EventManager.py", line 32, in event LoopTask self.doEvents() File "C:\Panda3D-1.0.5\direct\src\showbase\EventManager.py", line 26, in doEve nts self.processEvent(self.eventQueue.dequeueEvent()) File "C:\Panda3D-1.0.5\direct\src\showbase\EventManager.py", line 71, in proce ssEvent messenger.send(eventName) File "C:\Panda3D-1.0.5\direct\src\showbase\Messenger.py", line 175, in send apply(method, (extraArgs + sentArgs)) File "getrotation7.py", line 94, in moveToPosition 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)) AssertionError
And the new code:
- Code: Select all
# 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  .
Thanks again
Last edited by Tiptoe on Fri Apr 21, 2006 10:30 am, edited 1 time in total.
 Tiptoe
-
Tiptoe
-
- Posts: 127
- Joined: Fri Mar 03, 2006 11:57 pm
by Martin » Fri Apr 21, 2006 10:22 am
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:
- Code: Select all
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
-
Martin
-
- Posts: 275
- Joined: Wed Jul 27, 2005 4:59 pm
- Location: Vienna, Austria - EU
-
by Tiptoe » Fri Apr 21, 2006 11:15 am
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  .
Again HUGE thanks to all of you.
Last edited by Tiptoe on Sat Apr 22, 2006 1:21 am, edited 1 time in total.
 Tiptoe
-
Tiptoe
-
- Posts: 127
- Joined: Fri Mar 03, 2006 11:57 pm
by russ » Fri Apr 21, 2006 12:25 pm
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.
- Code: Select all
# 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()
-
russ
-
- Posts: 223
- Joined: Wed May 04, 2005 5:32 pm
by Tiptoe » Sat Apr 22, 2006 1:47 am
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  ).
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.
 Tiptoe
-
Tiptoe
-
- Posts: 127
- Joined: Fri Mar 03, 2006 11:57 pm
by Tiptoe » Sat Apr 22, 2006 12:35 pm
Hi again. I've been playing around with this code all day, it's just wonderful  . 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  ?
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?
- Code: Select all
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
 Tiptoe
-
Tiptoe
-
- Posts: 127
- Joined: Fri Mar 03, 2006 11:57 pm
by Martin » Sat Apr 22, 2006 3:05 pm
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
-
Martin
-
- Posts: 275
- Joined: Wed Jul 27, 2005 4:59 pm
- Location: Vienna, Austria - EU
-
by Tiptoe » Sun Apr 23, 2006 12:56 am
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  .
Cheers
 Tiptoe
-
Tiptoe
-
- Posts: 127
- Joined: Fri Mar 03, 2006 11:57 pm
Return to Scripting Issues
Who is online
Users browsing this forum: No registered users and 0 guests
| | |