Changing the environment

I’ve been working on making a basic game based on the RoamingRalph demo to learn Panda3D, but I’ve run into an issue - when I try to change the environment to a new one to make it seem like the player has entered a new level, the player does not interact with the terrain of the new environment properly, and instead starts high up in the air and runs into invisible terrain collision all over, and even runs up non-existent hills.

I’m sure it’s something simple I forgot to set in the player’s collision detection that tells it to use the new environment as the terrain to collide into, but I don’t know how to fix it myself.

Here is my code for initializing the player and his collision detection, and the environment:

self.environ = loader.loadModel("models/world.egg")
        self.environ.reparentTo(render)
        self.environ.setPos(0,0,0)

        ralphStartPos = self.environ.find("**/start_point").getPos()
        self.ralph = Actor("models/ralph",
                                 {"run":"models/ralph-run",
                                  "walk":"models/ralph-walk"})
                                  
        self.ralph.reparentTo(render)
        self.ralph.setScale(.2)
        self.ralph.setPos( -50,-50,2 )

        base.camera.setPos(self.ralph.getX(),self.ralph.getY()+10,2)
        self.cTrav = CollisionTraverser()

        self.ralphGroundRay = CollisionRay()
        self.ralphGroundRay.setOrigin(0,0,1000)
        self.ralphGroundRay.setDirection(0,0,-1)
        self.ralphGroundCol = CollisionNode('ralphRay')
        self.ralphGroundCol.addSolid(self.ralphGroundRay)
        self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0))
        self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff())
        self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol)
        self.ralphGroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler)

        self.camGroundRay = CollisionRay()
        self.camGroundRay.setOrigin(0,0,1000)
        self.camGroundRay.setDirection(0,0,-1)
        self.camGroundCol = CollisionNode('camRay')
        self.camGroundCol.addSolid(self.camGroundRay)
        self.camGroundCol.setFromCollideMask(BitMask32.bit(0))
        self.camGroundCol.setIntoCollideMask(BitMask32.allOff())
        self.camGroundColNp = base.camera.attachNewNode(self.camGroundCol)
        self.camGroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler)

And here is my code for changing the environment, which either sets it to a new instance of the same one or a different model altogether:

    def nextLevel( self, task ):
        self.level += 1
        self.environ.hide()
        
        if ( self.level % 2 == 0 ):
            self.environ = loader.loadModel( "models/Environment.egg" )
            self.environ.reparentTo(render)
            self.environ.setScale(0.25,0.25,0.25)
            self.environ.setPos(-8,42,0)
        else:
            self.environ = loader.loadModel( "models/world" )

        self.ralph.setPos( 30, 30, 1 )

And here is my code for when the player moves, which is the default Ralph code:

def move(self, task):
        if ( self.isGameOver ):
            if self.isMoving:
                self.ralph.stop()
                self.ralph.pose("walk",5)
                self.isMoving = False
            return Task.cont

        elapsed = task.time - self.prevtime

        # If the camera-left key is pressed, move camera left.
        # If the camera-right key is pressed, move camera right.

        base.camera.lookAt(self.ralph)
        camright = base.camera.getNetTransform().getMat().getRow3(0)
        camright.normalize()
        if (self.keyMap["cam-left"]!=0):
            base.camera.setPos(base.camera.getPos() - camright*(elapsed*20))
        if (self.keyMap["cam-right"]!=0):
            base.camera.setPos(base.camera.getPos() + camright*(elapsed*20))

        # save ralph's initial position so that we can restore it,
        # in case he falls off the map or runs into something.

        startpos = self.ralph.getPos()

        # If a move-key is pressed, move ralph in the specified direction.

        if (self.keyMap["left"]!=0):
            self.ralph.setH(self.ralph.getH() + elapsed*300)
        if (self.keyMap["right"]!=0):
            self.ralph.setH(self.ralph.getH() - elapsed*300)
        if (self.keyMap["forward"]!=0):
            backward = self.ralph.getNetTransform().getMat().getRow3(1)
            backward.setZ(0)
            backward.normalize()
            self.ralph.setPos(self.ralph.getPos() - backward*(elapsed*5))

        # If ralph is moving, loop the run animation.
        # If he is standing still, stop the animation.

        if (self.keyMap["forward"]!=0) or (self.keyMap["left"]!=0) or (self.keyMap["right"]!=0):
            if self.isMoving is False:
                self.ralph.loop("run")
                self.isMoving = True
        else:
            if self.isMoving:
                self.ralph.stop()
                self.ralph.pose("walk",5)
                self.isMoving = False

        # If the camera is too far from ralph, move it closer.
        # If the camera is too close to ralph, move it farther.

        camvec = self.ralph.getPos() - base.camera.getPos()
        camvec.setZ(0)
        camdist = camvec.length()
        camvec.normalize()
        if (camdist > 10.0):
            base.camera.setPos(base.camera.getPos() + camvec*(camdist-10))
            camdist = 10.0
        if (camdist < 5.0):
            base.camera.setPos(base.camera.getPos() - camvec*(5-camdist))
            camdist = 5.0

        # Now check for collisions.

        self.cTrav.traverse(render)

        # Adjust ralph's Z coordinate.  If ralph's ray hit terrain,
        # update his Z. If it hit anything else, or didn't hit anything, put
        # him back where he was last frame.

        entries = []
        for i in range(self.ralphGroundHandler.getNumEntries()):
            entry = self.ralphGroundHandler.getEntry(i)
            entries.append(entry)
        entries.sort(lambda x,y: cmp(y.getSurfacePoint(render).getZ(),
                                     x.getSurfacePoint(render).getZ()))
        if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain"):
            self.ralph.setZ(entries[0].getSurfacePoint(render).getZ())
        else:
            self.ralph.setPos(startpos)

        # Keep the camera at one foot above the terrain,
        # or two feet above ralph, whichever is greater.

        entries = []
        for i in range(self.camGroundHandler.getNumEntries()):
            entry = self.camGroundHandler.getEntry(i)
            entries.append(entry)
        entries.sort(lambda x,y: cmp(y.getSurfacePoint(render).getZ(),
                                     x.getSurfacePoint(render).getZ()))
        if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain"):
            base.camera.setZ(entries[0].getSurfacePoint(render).getZ()+1.0)
        if (base.camera.getZ() < self.ralph.getZ() + 2.0):
            base.camera.setZ(self.ralph.getZ() + 2.0)

        # The camera should look in ralph's direction,
        # but it should also try to stay horizontal, so look at
        # a floater which hovers above ralph's head.

        self.floater.setPos(self.ralph.getPos())
        self.floater.setZ(self.ralph.getZ() + 2.0)
        base.camera.lookAt(self.floater)

        # Store the task time and continue.
        self.prevtime = task.time
        return Task.cont

I would appreciate any help as to why my player follows strange terrain collision after the map changes!

Oh, and one more question - is it possible to get the width and height of the environment model? I would like to be able to do something along the lines of … player.setPos( environ.width(), environ.height() ) to start the player in the far corner.

Thanks!

Only hide()ing the old env model doesn’t automatically exclude it from collision traversal. Perhaps all you want is removing it with removeNode(), or stash() it if you want to keep it lying around in the scenegraph, but upon switch don’t load and attach it again, just unstash() it.

To get the bounds, use getTightBounds(). The result is in its parent’s coord system. So, to put the avatar, you should set it relative to environment’s parent.

player.setPos(self.environ.getParent(),.......)

This fixed it perfectly!

Thanks!