Multipart Actor Inheritence

How can I create a new class that derives from Actors that allows for multipart actors? I’ve been using the code

class Player(Actor.Actor):
    def __init__(self):
        self.loadModel({   "Jaw":"plainshoundJaw.egg",
                           "Body":"plainshoundBody.egg",
                           "Tail":"plainshoundtail.egg"},


                       {   "Jaw":{"Open":"plainshoundJaw-Open.egg"},
                           "Body":{"Idle":"plainshoundBody-Idle.egg",
                                   "WalktoRun":"plainshoundBody-WalktoRun.egg"},
                           "Tail":{"Between":"plainshoundtail-Between.egg",
                                   "Wag":"plainshoundtail-Wag.egg"}})
        self.attach("Body", "Tail", "Tail 1")
        self.attach("Body","Jaw","Jaw")
        self.reparentTo(render)
        self.loop("Idle","Body")
        self.loop("Wag","Tail")

However I get the error

AttributeError: 'Player' object has no attribute '_Actor__subpartDict'

Does anyone have a solution to this?

I think it should be possible.
But, you can’t load an actor using loader.loadModel.

I think this should work. (I have not tested it)

class Player(Actor):
    def __init__(self):
        self.Actor({   "Jaw":"plainshoundJaw.egg",
                           "Body":"plainshoundBody.egg",
                           "Tail":"plainshoundtail.egg"},


                       {   "Jaw":{"Open":"plainshoundJaw-Open.egg"},
                           "Body":{"Idle":"plainshoundBody-Idle.egg",
                                   "WalktoRun":"plainshoundBody-WalktoRun.egg"},
                           "Tail":{"Between":"plainshoundtail-Between.egg",
                                   "Wag":"plainshoundtail-Wag.egg"}}) 
        
        ...and so on...
        

The trick is to remember to upcall to Actor.init() when you override init().

This is one of those Python quirks that trips everyone up. Unlike C++, Python does not automatically generate upcalls to the constructor when you define a new constructor. So if you redefine init(), you are completely replacing the Actor’s own constructor, and unless you remember to upcall, the Actor will not get properly constructed.

Write your method like this:

    def __init__(self):
        Actor.Actor.__init__(self)
        self.loadModel({   "Jaw":"plainshoundJaw.egg",
                           "Body":"plainshoundBody.egg",
                           "Tail":"plainshoundtail.egg"},
        (etc)

David

I now get a different error when I type the code drwr gave me.

D:\Thertor>ppython main.py
DirectStart: Starting the game.
Warning: DirectNotify: category 'Interval' already exists
Known pipe types:
  wglGraphicsPipe
(3 aux display modules not yet loaded.)
:display:gsg:glgsg(warning): BlendEquation advertised as supported by OpenGL run
time, but could not get pointers to extension function.
:display:gsg:glgsg(warning): BlendColor advertised as supported by OpenGL runtim
e, but could not get pointers to extension function.
:util(warning): Adjusting global clock's real time by 0.131903 seconds.
:loader(error): Couldn't load file Body: not found on model path (which is curre
ntly: ".;/c/Panda3D-1.3.0/etc/..;/c/Panda3D-1.3.0/etc/../models")
:loader(error): Couldn't load file Jaw: not found on model path (which is curren
tly: ".;/c/Panda3D-1.3.0/etc/..;/c/Panda3D-1.3.0/etc/../models")
:loader(error): Couldn't load file Tail: not found on model path (which is curre
ntly: ".;/c/Panda3D-1.3.0/etc/..;/c/Panda3D-1.3.0/etc/../models")
Traceback (most recent call last):
  File "main.py", line 71, in ?
    Hound = Player()
  File "main.py", line 22, in __init__
    self.loadModel({   "Jaw":"plainshoundJaw.egg",
  File "C:\Panda3D-1.3.0\direct\src\actor\Actor.py", line 1360, in loadModel
    bundle = model.find("**/+PartBundleNode")
AttributeError: 'list' object has no attribute 'find'

Care to lend your expertise again?

Oh, sorry. Right, loadModel() doesn’t take a dictionary. If you want to load your multiple parts, you have to call loadModel once for each part:

self.loadModel("plainshoundJaw.egg", partName = "Jaw")
self.loadModel("plainshoundBody.egg", partName = "Body")
self.loadModel("plainshoundtail.egg", partName = "Tail")

Or, now that you are calling the Actor constructor, you can alternatively pass your dictionary up to that (since the constructor will take a dictionary):

Actor.Actor.__init__(self, {   "Jaw":"plainshoundJaw.egg",
                           "Body":"plainshoundBody.egg",
                           "Tail":"plainshoundtail.egg"}, 
                    )

David

Got it working Woot! Thanks drwr for your help. :slight_smile: