having trouble playing an animation using nodePath.getParent

I’m using a loop to expose joints in a level and place rocks and collision sphere’s at the joints, which works great. I want the rocks to break (play an animation) when you hit them, and then detach the rock node from the scene graph. I have problems when calling the animation on the parent of the collision sphere. Here’s my code:


# for each rock, we need to expose the joint, and create a collision sphere at the pos of the joint

for i in range (1, 114):
    self.rockJoint = self.tube.exposeJoint(None,"modelRoot","r" + str(i) ) 

    self.rockModel = Actor.Actor("models/Ice2.egg",
                                             {'shatter' : "models/Ice2-Shatter"} )
    self.rockModel.reparentTo(self.rockJoint)
                        
    self.rockSphere = CollisionSphere(0, 0, 0, 1.5) 
    self.rockSphereNodePath = self.rockModel.attachNewNode(CollisionNode('rColNode' + str(i) )) 
    self.rockSphereNodePath.node().addSolid(self.rockSphere)

    self.rockSphereNodePath.show()

I’m not including my collision checking code, because the collisions work. But I’ll include my collision function that gets called on a collision



def collide(self, entry):

        if(self.alreadyCollided):
            None
        else:
            self.alreadyCollided = 1

            # get the parent of the collision sphere node path, which is the rock/spike geometry
            self.sphereHit = entry.getIntoNodePath()
            self.geomHit = self.sphereHit.getParent()
            self.breakAnim = self.geomHit.actorInterval('shatter')

            print self.sphereHit.getParent()

            self.shatterSeq = Sequence( self.breakAnim, Func(self.GeomHit.detachNode) )
                        
            Sequence( Func(self.pauseAll), self.shatterSeq, Func(self.resumeAll), Wait(1), Func(self.resetCollision) ).start()

The error I get is: NodePath instance has no attribute ‘actorInterval’

I think my problem is that every time I expose a joint, I overwrite the self.rockModel object, but I figured if I could get the parent of the collisionNode that was hit, that would be the geometry that I needed to animate, but panda doesn’t like this.

Any suggestions?

Right, this is a classic problem with Panda’s C++/Python grafting. The Actor you created is a Python object that wraps a C++ object (a NodePath). When you store that NodePath in the scene graph, you’re actually only storing the C++ part of it there, and when you get that NodePath out of the scene graph again (e.g. from a getParent() call), you get a new Python object that wraps the same C++ object, instead of the original Actor.

So the bottom line: you have to save the Actor in Python as you create it, and not overwrite it each time. Then you need some way to look up the Actor associated with each rock when you get the collision event.

There are lots of ways to do this. One way is to store the Actor in a dictionary by rock name, and store the same string on a tag, that you can extract from the collision event, like this:


self.rocks = {}
# for each rock, we need to expose the joint, and create a collision sphere at the pos of the joint

for i in range (1, 114):
    rockName = "r" + str(i)
    self.rockJoint = self.tube.exposeJoint(None,"modelRoot",rockName )
    
    rockModel = Actor.Actor("models/Ice2.egg",
                                             {'shatter' : "models/Ice2-Shatter"} )
    rockModel.reparentTo(self.rockJoint)
    rockModel.setTag('rockName', rockName)
    self.rocks[rockName] = rockModel
                       
    self.rockSphere = CollisionSphere(0, 0, 0, 1.5)
    self.rockSphereNodePath = rockModel.attachNewNode(CollisionNode('rColNode' + str(i) ))
    self.rockSphereNodePath.node().addSolid(self.rockSphere)

    self.rockSphereNodePath.show() 

Then when you get the collision event, look up the actor in your dictionary based on the tag name:

def collide(self, entry):

        if(self.alreadyCollided):
            None
        else:
            self.alreadyCollided = 1

            # get the parent of the collision sphere node path, which is the rock/spike geometry
            sphereHit = entry.getIntoNodePath()
            rockName = sphereHit.getNetTag('rockName')
            geomHit = self.rocks[rockName]
            breakAnim = geomHit.actorInterval('shatter')

            shatterSeq = Sequence( breakAnim, Func(geomHit.detachNode) )
                       
            Sequence( Func(self.pauseAll), shatterSeq, Func(self.resumeAll), Wait(1), Func(self.resetCollision) ).start() 

David

Thanks so much david, you are really saving my ass on this project =)

Another related question…we have both rocks and stalagtites in the tunnel, both with different animations. So the animation played on the object depends on the type of object hit. Would you suggest using a rock class and a stalagtite class to differentiate between collision types?

edit: or is there a way to use regular expressions to check if the object name contains a “s” or an “r” to differentiate between a rock being hit and a stalagtite being hit?

nevermind…I just used the same dictionary for all the objects and it worked perfectly =)

Ok more on this issue:

I’m using classes now, and I’m having the same problem with getting the right position of the joint in the level. Here’s my expose joints code:


# for each rock, we need to expose the joint, and create a collision sphere at the pos of the joint
        for i in range (1, 400):
            rockName = "Rocks:r" + str(i)
            self.rockJoint = self.tube.exposeJoint(None,"modelRoot",rockName ) 

            self.rock = Rock(self.rockJoint, self.rockModel, rockName, self)

            self.rocks[i] = self.rock

        # expose joints for stalagtites
        for i in range (1, 57):
            spikeName = "s" + str(i)
            self.spikeJoint = self.tube.exposeJoint(None,"modelRoot",spikeName ) 

            self.spike = Spike(self.spikeJoint, self.spikeModel, spikeName, self)

            self.spikes[i] = self.spike

And here’s my rock class


class Rock(DirectObject):

    def __init__(self, joint, model, name, w):

        self.model = model
        self.name = name
        self.joint = joint

        self.model.reparentTo(render)
        self.model.setPos(self.joint.getPos() )

        self.rockSphere = CollisionSphere(0, 0, 0, 1.5) 
        self.rockSphereNodePath = self.model.attachNewNode(CollisionNode(self.name)) 
        self.rockSphereNodePath.node().addSolid(self.rockSphere)

It seems like its placing all the rock models at (0,0,0), but the joints arent at (0,0,0). Is my joint.getPos not working correctly?

lol im answering my own questions here. I got them to place correctly by changing my code. I’m having trouble with the animations now though, but I’ll wait a bit to post again since I seem to be fixing my own problems.

Thanks =)