Isn’t that the thing keeps the world spin ? Keep it on !
Heads up, David, your link is dead !

Damn, IPKnightly! You beat me to it.

I’m also working on a steering behaviors demo for Panda. I have seek, flee, pursue, evade, arrive, wander, and am working on obstacle avoidance. I guess I should make a release.

From the looks of it your vehicles steer in 3 dimensions. I’ve done mine a little differently, my vehicles steer in two dimensions and stay on the XY plane. That happens to be what I need for my application. The difference is fairly trivial for the most part though.

P.S. I had some difficulty running your example:

s0094060@marley-20:~/Desktop/boidsSeekArrive$ python boids.py 
Warning: DirectNotify: category 'Interval' already exists
DirectStart: Starting the game.
Known pipe types:
(all display modules loaded.)
:util(warning): Adjusting global clock's real time by 0.475743 seconds.
:util(warning): Adjusting global clock's real time by -0.129731 seconds.
Assertion failed: !pos.is_nan() at line 367 of panda/src/pgraph/transformState.cxx
Traceback (most recent call last):
  File "boids.py", line 193, in ?
    run( ) # if this module is not being imported run the game
  File "debtmp/usr/share/panda3d/direct/src/showbase/ShowBase.py", line 2028, in run
  File "debtmp/usr/share/panda3d/direct/src/task/Task.py", line 839, in run
  File "debtmp/usr/share/panda3d/direct/src/task/Task.py", line 787, in step
  File "debtmp/usr/share/panda3d/direct/src/task/Task.py", line 721, in __stepThroughList
  File "debtmp/usr/share/panda3d/direct/src/task/Task.py", line 644, in __executeTask
  File "boids.py", line 169, in steer
    self.redBoid.run( )
  File "boids.py", line 119, in run
    self.update( ) # run update method
  File "boids.py", line 73, in update
    self.boidModel.setPos( self.location )
AssertionError: !pos.is_nan() at line 367 of panda/src/pgraph/transformState.cxx

I don’t know specifically what’s going wrong in this instance, but in general, you’re most likely end up with NaN’s in a position value by applying an invalid matrix. And nine times out of ten, you get an invalid matrix by inverting a singular matrix, which is to say, a matrix that involves a scale to 0 in one of its axes.

Another way to end up with NaN’s is to divide by zero (which is the same thing in a different context).


Ok, first, query the window size and aspect ratio :

self.winAspRatio = base.camLens.getAspectRatio()
self.xSizeHalf = base.win.getXSize()*.5
self.ySizeHalf = base.win.getYSize()*.5

and replace your code in moveTarget :

        x = ( base.win.getPointer(0).getX()-self.xSizeHalf ) / self.xSizeHalf
        y = ( 2*self.ySizeHalf-base.win.getPointer(0).getY()-self.ySizeHalf ) / self.ySizeHalf
        self.target.setPos( Vec3( x*self.winAspRatio*10.8, y*10.8, 0.0 ) )

Do you know that the value of mouse pos returned by “base.win.getPointer” has the origin at the upper left corner ?

x = ( base.win.getPointer( 0 ).getX( ) - self.xSizeHalf ) / self.xSizeHalf

is to bring the x pos origin to the window’s center, and the division is to get value range -1…1

y = ( 2 * self.ySizeHalf - base.win.getPointer( 0 ).getY( ) - self.ySizeHalf ) / self.ySizeHalf

is to bring the y pos origin to the window’s center and flip it, and the division is to get value range -1…1
sorry…, I should have used the following straightforward way :

y = - ( base.win.getPointer( 0 ).getY( ) - self.ySizeHalf ) / self.ySizeHalf
  1. the x & y results now in range -1…1, so you have to multiply x by aspect ratio, or else it would be squashed. And the 10.8 is only to match the results to your scene size and your camera elevation.

I hope it’s clear enough.

OR, for shorter code, without querying aspectRatio, you can do this directly :

        x = ( base.win.getPointer(0).getX()-self.xSizeHalf ) / self.ySizeHalf
        y = -( base.win.getPointer(0).getY()-self.ySizeHalf ) / self.ySizeHalf
        self.target.setPos( Vec3( x*10.8, y*10.8, 0.0 ) )