Plugins + kayn = headache

Background information:

Being the rather incompetent person I am with panda, I was hoping that someone could just (even briefly) help me understand how to implement plugins (plugins, modules, scripts, etc.) and such into my own code. This isn’t really specific to PandaSteer2, either.

Feel kind of stupid for always asking stupid questions, but I guess you have to start somewhere…


The main thing that I was having problems with was how it runs the plugins. This is the main file:

"""
"""
pandaSteer.py -- A plugin-based steering behaviours demo for Panda3D.
"""

# Python imports
import os,math,sys,random,imp
# Panda3D imports
import direct.directbase.DirectStart
from pandac import PandaModules  as P
from direct.showbase.DirectObject import DirectObject
from direct.task.Task import Task
from pandac.PandaModules import loadPrcFileData
loadPrcFileData("", "interpolate-frames 1") # Smooth (interpolated) animations
# Custom imports
from terrain import Terrain
from camera import Camera
from obstacles import SphereObstacle
import character as C
import vehicle as V

from direct.gui.OnscreenText import OnscreenText

base.cTrav = P.CollisionTraverser('Global CollisionTraverser of pandaSteer.py')

class PandaSteer(DirectObject):

    def __init__(self):
        """Initialise the scene, controls and plugins."""

        # Show the framerate
        base.setFrameRateMeter(True)

        # Make some terrain and colour, scale and position it.
        color,scale = (0.6,0.8,0.5,1),3
        trees = 2 # Hack to make sure there are no trees
        t = Terrain(color=color,scale=scale,trees=2.0,pos=P.Point3(-50,-50,0),h=3)
        t.prime.reparentTo(render)

        # Setup a camera, position and orient it to look down at the terrain.
        base.disableMouse()
        self.camera = Camera(P.Vec3(-100,-100,100))
        self.camera.lookAt(0,0,0)

        # Setup ambient and directional lights.
        self.alight = P.AmbientLight('alight')
        self.alight.setColor(P.VBase4(0.35, 0.35, 0.35, 1))
        self.alnp = render.attachNewNode(self.alight)
        render.setLight(self.alnp)

        self.dlight = P.DirectionalLight('dlight')
        self.dlight.setColor(P.VBase4(0.4, 0.4, 0.4, 1))
        self.dlnp = render.attachNewNode(self.dlight)
        self.dlnp.setHpr(45, -45, 0)
        render.setLight(self.dlnp)

        # Setup some scene-wide exponential fog.
        colour = (0.5,0.8,0.8)
        self.expfog = P.Fog("Scene-wide exponential Fog object")
        self.expfog.setColor(*colour)
        self.expfog.setExpDensity(0.0005)
        render.setFog(self.expfog)
        base.setBackgroundColor(*colour)

        # Setup keyboard controls.
        # Accept some keys to move the camera.
        self.accept("a", self.camera.setControl, ["left",1])
        self.accept("a-up", self.camera.setControl, ["left",0])
        self.accept("d", self.camera.setControl, ["right",1])
        self.accept("d-up", self.camera.setControl, ["right",0])
        self.accept("w", self.camera.setControl, ["up",1])
        self.accept("w-up", self.camera.setControl, ["up",0])
        self.accept("s", self.camera.setControl, ["down",1])
        self.accept("s-up", self.camera.setControl, ["down",0])
        self.accept("arrow_up", self.camera.setControl, ["forward",1])
        self.accept("arrow_up-up", self.camera.setControl, ["forward",0])
        self.accept("arrow_down", self.camera.setControl, ["backward",1])
        self.accept("arrow_down-up", self.camera.setControl, ["backward",0])
        self.accept("arrow_left", self.camera.setControl, ["strafe-left",1])
        self.accept("arrow_left-up", self.camera.setControl, ["strafe-left",0])
        self.accept("arrow_right", self.camera.setControl, ["strafe-right",1])
        self.accept("arrow_right-up", self.camera.setControl,["strafe-right",0])
        # Accept the Esc key to exit.
        self.accept("escape", sys.exit)
        # r key restarts the current simulation.
        self.accept("r",self.restart)
        # tab key goes to next demonstration
        self.accept("tab",self.next)
        # c toggles annotation
        self.accept("c",C.toggleAnnotation)
        # h toggles text
        self.accept("h",self.toggleText)

        # Lists to hold the Characters and Obstacles currently in the scene.
        self.characters = []
        self.obstacles = []

        # Find and initialise all demo plugins. Plugins are loaded in order of
        # filename.
        self.plugins = []
        self.plugin = 0
        for file in sorted(os.listdir('plugins')):
            if file.endswith('.py'):
                modulename = file[:-3]
                try:
                    file, filename, description = imp.find_module(
                        modulename, ['plugins'])
                    pluginmodule = imp.load_module(modulename,
                        file, filename, description)
                    if hasattr(pluginmodule, 'Plugin'):
                        print 'Instantiating plugin from %s' % pluginmodule
                        try:
                            self.plugins.append(pluginmodule.Plugin())
                        except Exception, e:
                            print 'Urk! %s' % e
                    else:
                        print 'No plugin in %s' % pluginmodule
                finally:
                    if file:
                        file.close()

        # Create OnscreenText object.
        self.textOn=True
        self.text=''
        self.ostext = OnscreenText(text=self.text,pos=(-1.3, 0.9),scale=0.05,
                                   fg=(0,0,0,1),align=P.TextNode.ALeft)

        # Setup CollisionRay and CollisionHandlerQueue for mouse picking.
        self.pickerQ = P.CollisionHandlerQueue()
        self.picker = camera.attachNewNode(P.CollisionNode('Picker CollisionNode'))
        self.picker.node().addSolid(P.CollisionRay())
        # We want the picker ray to collide with the floor and nothing else.
        self.picker.node().setFromCollideMask(C.floorMASK)
        self.picker.setCollideMask(P.BitMask32.allOff())
        base.cTrav.addCollider(self.picker,self.pickerQ)
        try:
            handler.addCollider(self.picker,camera)
        except:
            pass
        self.accept('mouse1',self.onClick)

        # Start the demo.
        self.restart()
        taskMgr.add(self.wrap,"Wrap task of pandaSteer.py")

    def onClick(self):
        """Handle the mouse-click event."""

        mpos=base.mouseWatcherNode.getMouse()
        # Makes the ray's origin the camera and make the ray point to mpos
        self.picker.node().modifySolid(0).setFromLens(
            base.camNode,mpos.getX(),mpos.getY())
        # We don't want to traverse now, so wait for panda to do it then move.
        taskMgr.doMethodLater(.02,self.setDestination,'setDest')

    def setDestination(self,task):
        """Helper method for onClick.

        Find the position in the 3D scene that was clicked and pass it to the
        click method of the currently active plugin.

        """
        if self.pickerQ.getNumEntries() > 0:
            self.pickerQ.sortEntries()
            self.point=self.pickerQ.getEntry(0).getSurfacePoint(render)
            self.plugins[self.plugin].click(self.point)

    def wrap(self,task):
        """If the current plugin requests it, wrap Character's around if they
        leave the terrain."""

        # FIXME: hardcoded terrain size and position here.
        if self.plugins[self.plugin].wrap:
            for character in self.characters:
                if character.getspeed() > 0:
                    if character._pos.getX()<-50: character._pos.setX(45)
                    if character._pos.getX()>45: character._pos.setX(-50)
                    if character._pos.getY()<-50: character._pos.setY(45)
                    if character._pos.getY()>45: character._pos.setY(-50)
        return Task.cont

    def next(self):
        """Move onto the next plugin, or go back to the first."""

        self.plugin += 1
        if self.plugin >= len(self.plugins): self.plugin = 0
        self.restart()

    def restart(self):
        """Restart the demo environment, its characters, and the current
        plugin."""

        # Destroy the Character objects.
        for character in self.characters:
            character.destroy()
        self.characters = [] # Python will garbage-collect them now

        # Create new Character objects for the plugin.
        for i in range(0,self.plugins[self.plugin].numVehicles):
            character = C.Character(name='Ralph',#FIXME: Give each character a different name!
                                    avoidObstacles=False,avoidVehicles=False)
            self.characters.append(character)

        self.plugins[self.plugin].vehicles = self.characters
        self.plugins[self.plugin].restart()

        # Destroy the Obstacle objects.
        for obstacle in self.obstacles:
            obstacle.destroy()
        self.obstacles = [] # Python will garbage-collect them now

        # Create new Obstacle objects for the plugin.
        if self.plugins[self.plugin].obstaclesOn == True:
            self.obstacles = [SphereObstacle(-20,20,0,15),
                           SphereObstacle(20,10,0,5),
                           SphereObstacle(10,20,0,3),
                           SphereObstacle(20,-15,0,10),
                           SphereObstacle(20,-35,0,3),
                           SphereObstacle(-3,-35,0,10),
                           SphereObstacle(-25,-10,0,7),
                           SphereObstacle(-25,-30,0,2),
                          ]
        C.setContainer(self.plugins[self.plugin].container)

        self.text = self.plugins[self.plugin].text + """

r: restart this demo
Tab: go to next demo
w: look down
s: look up
a: look left
d: look right
Arrow keys: move camera
   forward, backward, left right
c: annotation on/off
h: get rid of this annoying text
Esc: exit"""
        self.toggleText()
        self.toggleText() # :/

    def toggleText(self):
        if self.textOn:
            self.ostext.setText('h: get that helpful text back')
            self.textOn=False
        else:
            self.ostext.setText(self.text)
            self.textOn=True

# Run the test scene.
p = PandaSteer()
run()

And this is what is REALLY throwing me off. I understand that it is running them in the sequence, and I almost understand what it is saying. However, I can’t get it to just run one plugin instead of doing that. Any advice? :

for file in sorted(os.listdir('plugins')):
            if file.endswith('.py'):
                modulename = file[:-3]
                try:
                    file, filename, description = imp.find_module(
                        modulename, ['plugins'])
                    pluginmodule = imp.load_module(modulename,
                        file, filename, description)
                    if hasattr(pluginmodule, 'Plugin'):
                        print 'Instantiating plugin from %s' % pluginmodule
                        try:
                            self.plugins.append(pluginmodule.Plugin())
                        except Exception, e:
                            print 'Urk! %s' % e
                    else:
                        print 'No plugin in %s' % pluginmodule
                finally:
                    if file:
                        file.close()

Still haven’t solved the problem completely, although I am improving. :smiley: However, I keep getting an error saying method not bound… What could be causing this?

Btw, edit wasn’t working so I had to make a new post.

Importing something is very simple. You can use imp.load_*, or use import statement.
What PS2 does is load each found modules and instantiate Plugin class in each module, and then save the instances in plugins, and restart() them in sequence.
Say I have this x.py module :

def out():
    pass

class Plugin:
  me=0
  def __init__(self):
      pass

  class inside:
      def __init__(self):
          pass

If I want to use only “out” method :

from x import out

If I want to use only “Plugin” class :

from x import Plugin

If I want to use only “inside” class :

from x import Plugin
Plugin.inside()

If I want to use all :

from x import *
out()
Plugin()
Plugin.inside()

If you use imp.load_module, it’s similar to :

import x
x.Plugin()

Thank you very much, that’s exactly what I was hoping for. I actually managed to work around that up until now, but now that I finally understand, I think I’ll have a much easier time.

Don’t know how you have the patience to answer my questions personally, half the time I just expect a link to another page (and I wouldn’t mind that) but you just make it so simple. :astonished:

I mostly understood how to import them, but then I was like… How should I call it. Tried reading the python manual but it left me wondering how to call different things (methods, classes, etc.) and I could only call the module itself. Thus, the errors I kept getting. Now I get it. Thank you.

I got it to work. I basically replaced:

for file in sorted(os.listdir('plugins')):
            if file.endswith('.py'):
                modulename = file[:-3]
                try:
                    file, filename, description = imp.find_module(
                        modulename, ['plugins'])
                    pluginmodule = imp.load_module(modulename,
                        file, filename, description)
                    if hasattr(pluginmodule, 'Plugin'):
                        print 'Instantiating plugin from %s' % pluginmodule
                        try:
                            self.plugins.append(pluginmodule.Plugin())
                        except Exception, e:
                            print 'Urk! %s' % e
                    else:
                        print 'No plugin in %s' % pluginmodule
                finally:
                    if file:
                        file.close()

with:

     
        for file in sorted(os.listdir('plugins')):

            modulename = 'follow'
            file, filename, description = imp.find_module(
                modulename, ['plugins'])
            pluginmodule = imp.load_module(modulename,
                        file, filename, description)
            self.plugins.append(pluginmodule.Plugin())

Pretty sure I don’t even have to sort the directory, but this is just a quick solution I came up with once ynjh_jo pointed out that imp.load_module was actually similar to importing. Now that I’ve figured this out, I basically understand PandaSteer2, and thanks to ynjh_jo, how to import modules correctly. (ynjh_jo is now my best friend :laughing: :wink:)

I always like to post my solutions just in case, for future reference, even if the problem was simple. Never know if it can come in handy for someone else. :wink:

:laughing: :laughing: Shouldn’t everyone deserves that ? Where is your Holy Bible ? Is it buried deep under your feet ? :laughing: :laughing: :laughing:

Lol, I’m not Christian. I was just kidding, however. :wink: ( Not to say we can’t be friends.) Unless of course you don’t want to be friends… :cry:

All I meant to say is : “shouldn’t everybody deserves to be the best friend ?”
The point is : God is not picky, so why should we ?

Doesn’t matter to me, how could it be a problem ? I believe the core of all is more or less the same. :smiley:

Hehe, yeah, I understood what you said more or less, I was just pointing out I am not Christian (you said Holy Bible). I like to joke around, so don’t mind me.

Oh, by the way, since we’re on the subject of PD2, I thought I’d make this post count. I tried to change the way that the system is set up (same code as in the original post).

I tried to do the same but it keeps giving me errors. Let’s say I want to check the value of numvehicles from this code (this is the follow plugin):

import random
from steerVec import SteerVec
from pandaSteerPlugin import PandaSteerPlugin
from containers import ContainerSquare

class Plugin(PandaSteerPlugin):
    """PandaSteer plugin demonstrating wander and follow steering behaviors."""

    def __init__(self):
        """Initialise the plugin."""

        PandaSteerPlugin.__init__(self)

        self.numVehicles = 0
        self.container = ContainerSquare(radius=43)
        self.text = """Follow the leader:

One character wanders while another follows.

The follow behaviour is implemented by applying the previous arrival behaviour
to a point just behind the target each frame. The character steers towards this
point and slows down as it catches up to it, never overtaking the point.

Click with the mouse to control the leader using the arrive behaviour.
"""

    def click(self,pos):
        """Respond to mouse-click."""

        self.vehicles[1].arrive(SteerVec(pos.getX(),pos.getY()))

    def restart(self):
        """Start or restart the plugin."""

        # Set first character to random position and velocity.
        self.vehicles[0]._pos = SteerVec( (random.random()-0.5)*100, (random.random()-0.5)*100 )
        self.vehicles[0]._velocity = SteerVec( (random.random()-0.5)*2, (random.random()-0.5)*2 )
        self.vehicles[0]._velocity = self.vehicles[0]._velocity.truncate(self.vehicles[0].maxspeed)

        # Give second character a random start position and push it off towards the origin.
        self.vehicles[1]._pos = SteerVec( (random.random()-0.5)*100, (random.random()-0.5)*100 )
        self.vehicles[1]._velocity = SteerVec(0-self.vehicles[1]._pos.getX(),0-self.vehicles[1]._pos.getY())
        self.vehicles[1]._velocity = self.vehicles[1]._velocity.truncate(self.vehicles[1].maxspeed)

        # Tell the first character to follow the second character, and tell the
        # second character to wander.
         self.vehicles[0].follow(self.vehicles[1])
        self.vehicles[1].wander()

I’ve seen it done before (even in this code) but I get errors when i try it myself… I know the problem but not the solution (lol). :blush:

Edit: My bad, it’s just:

for i in range(0,self.plugins[self.plugin].numVehicles):

The error was caused by me importing another one, instead of just using the entire thing that was added to the range. Hehe. Simple mistake.

Another simple python based question but for some reason i just can’t figure out the problem. It also has to do with modules so I thought I’d put it here.

What I’m trying to do is call the method mouseControl from the Camera class in the camera module. I keep getting this error:

File "main.py", line 521, in gameLoop
    Camera.mouseControl()
TypeError: unbound method mouseControl() must be called with Camera instance as first argument (got nothing instead)

What does this error imply, exactly? I get the feeling that I’m calling the function the wrong way, if so, how is it really done?

you are calling a function as if it’s a static function

you should be calling it with an instance of the class
if the class is named Camera, you should instanciate it like:
cam = Camera()
and then call the mouseControl() function like this:
cam.mouseControl()

Thanks, I just figured it out and came back to change it :smiley:.

self.camera = Camera
self.camera.mouseControl()

However, is there any reason it’s Camera()? I don’t use parentheses and it works. In fact, wouldn’t having Camera () translate to Camera().mouseControl(), or am I mistaken?

Edit: Maybe not since you might have arguments. Ah… I’m going to bed.

I don’t know how your camera class is defined but “Camera” will just give you a reference to the class itself… it will not create an instance for you…

and no, Camera() will not translate to Camera().mousecontrol()… don’t know how you got that idea :stuck_out_tongue:

() signifies the attributes you are giving to the constructor… in this case none

consider the following code:



class Camera:
	def __init__(self):
		self.test = 1


a = Camera
print(type(Camera))
print(type(a))

b = Camera()
print(type(b))

#print(a.test) <- Doesn't work
print(b.test)

output:

X:\>python test.py
<type 'classobj'>
<type 'classobj'>
<type 'instance'>
1

notice the difference between the types of a and b… and that a is the same type as Camera… so…
b = Camera()
is the same as
b = a()

Fixed!