Need Help With My Cleanup

When the user exits the game, it is supposed to cleanup everything and
return to the main menu. So that when the user presses play, he enters
the game “with a clean slate”.
My problem is that everything has been cleaned up, except the collision
thing (the red thing that is seen when you uncomment

self.cTrav.showCollisions(render)). The used one is left behind, and a
new one create each time the game starts.
I am not sure, but I believe it has something to do with

self.playerZcol.traverse(render) in this code:

Code:
# Update Player Z (Keep player on Terrain)
def updatePlayerZ(self, time):
if self.page == 1:
startpos = self.Mod1.getPos()
self.playerZcol.traverse(render)

        if self.playerZcolQueue.getNumEntries>0: 
            self.playerZcolQueue.sortEntries() 
            point = self.playerZcolQueue.getEntry 

(0).getSurfacePoint(self.env)
self.Mod1.setZ(point.getZ())
else:
self.Mod1.setPos(startpos)
# End updatePlayerZ

Is there a code I can use to clean this up when I end the game?
Also, is there a code in Panda, that calls “restart game”?
Thanks

:frowning:
Please help. I can’t get anywhere further with my game until this is fixed.
Here is the code: (Just copy and paste, and run. No changes are needed.
Press play. Press escape to return to the menu, then press play again to see the problem.)

import direct.directbase.DirectStart 
from direct.showbase import *
from direct.controls.GravityWalker import GravityWalker 
from direct.actor.Actor import Actor 
from direct.task import Task 
from pandac.PandaModules import *
from direct.gui.DirectGui import *
from direct.interval.IntervalGlobal import *
from direct.showbase.PythonUtil import * # For player rotation 
import random, sys, os, math
from math import pi, sin

class World(DirectObject.DirectObject): 

    def __init__( self ): 
        
        #########################################################
        """SETUP"""
        #########################################################
        #View Framerate
        base.setFrameRateMeter(1)
        
        # Make Background Black for Splash Screen
	base.setBackgroundColor(0, 0, 0)
        
        """VARIABLES"""
        self.page = 0
        self.sceneOne = 0
        self.quitter = 0
        self.fontQuit = loader.loadFont('cmr12.egg') # Using an .egg
        
        self.keyMap = {"left":0, "right":0, "forward":0, "cam-left":0, "cam-right":0}
        self.zoomIn = 0
        self.zoomOut = 0
        self.angledegrees = 0.0
        self.angleradians = self.angledegrees * (math.pi / 180.0)
        
	#**************************************#
	""" Loads up Title Screen Menu       """
	#**************************************#

	#self.menuBack = DirectLabel(image="models/maps/gui/back01.png", scale = 1.4)
        #self.menuBack.setPos(0, 0, 0)

	self.playButton = DirectButton(text = "PLAY", scale = (0.4, 0.4, 0.1), command = self.activate)
	self.playButton.setPos(0, 0, -0.4)

    #**************************************#
    """ Main Menu Functions                
        Running The Game  - Scene 1      """
    #**************************************#

    # This Function Starts the Game
    def activate(self):
        self.page = 1
        self.sceneOne = 1
        self.windowOver = 1
        self.hideMainMenu()

	#**************************************#
	"""             LIGHTING             """
        #**************************************#

        # Light1 - Sunlight
        self.Light1 = PointLight('light1') 
        self.Light1.setColor(VBase4(2.5, 2.5, 2.5, 1)) 
        self.pointLight1 = render.attachNewNode(self.Light1) 
        self.pointLight1.setPos(0, 0, 300) 
        
        # Light2 - Shadow light (ambient)
        self.Light2 = AmbientLight('light2') 
        self.Light2.setColor(VBase4(0.75, 0.75, 0.75, 1)) 
        self.ambientLight1 = render.attachNewNode(self.Light2) 
        
        # Light3 - Spotlight1
        self.Light3 = Spotlight('light3') 
        self.Light3.setColor(VBase4(0.97, 0.98, 0.25, 1)) 
        self.Light3Lens = PerspectiveLens() 
        self.Light3.setLens(self.Light3Lens) 
        self.spotLight1 = render.attachNewNode(self.Light3) 
        self.spotLight1.node().getLens().setFov(18)

	#**************************************#
	"""            KEY INPUT             """
        #**************************************#

        # Accept the control keys for movement and rotation
        # key down
        self.accept("escape", self.endGame)

        taskMgr.add(self.move,"moveTask")
        
        # Game state variables
        self.prevtime = 0
        self.isMoving = False
        
        # Create a floater object.  We use the "floater" as a temporary
        # variable in a variety of calculations.
        self.floater = NodePath(PandaNode("floater"))
        self.floater.reparentTo(render)

        #**************************************#
	"""             MODELS               """
	#**************************************#

        # SCENE
        self.env = loader.loadModel("models/world") 
        self.env.reparentTo( render ) 
        self.env.setPos(0,0,0)

        # MAIN CHARACTER
        self.Mod1 = Actor("models/ralph",{"walk":"models/ralph-walk"}) 
        self.Mod1StartPos = self.env.find("**/start_point").getPos()
        self.Mod1.reparentTo(render)
        self.Mod1.setScale(.2)
        self.Mod1.setPos(self.Mod1StartPos)
        #self.Mod1.loop("walk")

        """CAMERAS"""
        
        # Camera setup
        self.my_cam1 = Camera("cam1") 
        self.my_camera1 = render.attachNewNode(self.my_cam1) 
        self.my_camera1.setName("camera1")
        self.my_camera1.setPos(20*math.sin(self.angleradians),-20.0*math.cos(self.angleradians),3) 
        #self.my_camera1.setPos(0,-20,155)
        self.my_camera1.setHpr(self.angledegrees,0,0)
        self.my_camera1.lookAt(self.Mod1)
        self.my_camera1.node().getLens().setAspectRatio(4.0/2.0)
        
        # Disable the default DisplayRegion, which covers the whole screen. 
        self.dr = base.camNode.getDisplayRegion(0) 
        self.dr.setActive(0) # disable ( use displayRegion 0 )
            
        # DisplayRegions. 
        self.window = self.dr.getWindow() 
        self.dr1 = self.window.makeDisplayRegion(0.026, 0.975, 0.35, 0.94) 
        self.dr1.setSort(self.dr.getSort())
        
        # Setup each camera. 
        self.dr1.setCamera(self.my_camera1)
        
        # Set up the camera
        base.disableMouse()
        self.my_camera1.setPos(self.Mod1.getX(), self.Mod1.getY()+10, 1.5)

        """Lights"""
        # Apply lights
        self.Mod1.setLight(self.pointLight1)
        #self.env.setLight(self.pointLight1)
        self.Mod1.setLight(self.ambientLight1)
        self.env.setLight(self.ambientLight1)
        
        self.spotLight1.setPos(self.Mod1.getX(), self.Mod1.getY()-10, -230)
        self.spotLight1.lookAt(self.Mod1) 
        self.env.setLight(self.spotLight1)
        
        """COLLISIONS"""
        """************************************************************"""
        # We will detect the height of the terrain by creating a collision
        # ray and casting it downward toward the terrain.  One ray will
        # start above mod's head, and the other will start above the camera.
        # A ray may hit the terrain, or it may hit a rock or a tree.  If it
        # hits the terrain, we can detect the height.  If it hits anything
        # else, we rule that the move is illegal.

        self.cTrav = CollisionTraverser()

        self.Mod1GroundRay = CollisionRay()
        self.Mod1GroundRay.setOrigin(0,0,1000)
        self.Mod1GroundRay.setDirection(0,0,-1)
        self.Mod1GroundCol = CollisionNode('ralphRay')
        self.Mod1GroundCol.addSolid(self.Mod1GroundRay)
        self.Mod1GroundCol.setFromCollideMask(BitMask32.bit(0))
        self.Mod1GroundCol.setIntoCollideMask(BitMask32.allOff())
        self.Mod1GroundColNp = self.Mod1.attachNewNode(self.Mod1GroundCol)
        self.Mod1GroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.Mod1GroundColNp, self.Mod1GroundHandler)

        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 = self.my_camera1.attachNewNode(self.camGroundCol)
        self.camGroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler)

        self.playerZcol = CollisionTraverser() 
        self.playerZcolQueue = CollisionHandlerQueue() 
        self.playerZcol.addCollider(self.Mod1GroundColNp, self.playerZcolQueue) 
        self.playerZcol.showCollisions(render) 

        # Uncomment this line to see the collision rays
        #self.ralphGroundColNp.show()
        #self.camGroundColNp.show()
       
        #Uncomment this line to show a visual representation of the 
        #collisions occuring
        #self.cTrav.showCollisions(render)
        """**********************************************************"""
        
        """ Movements by Intervals"""
        #test1
        self.Mod1HprIv = self.Mod1.hprInterval(0.5, Point3(40,0,0))
        self.Mod1PosIv = self.Mod1.posInterval(5, Point3(-80,0,0), startPos=Point3(self.Mod1StartPos))
        self.Mod1ActIv = self.Mod1.actorInterval("walk",loop=1,duration = 5)
        # We create a LerpFunc Interval to correct the Z as we go along 
        self.Mod1Z = LerpFunc(self.updatePlayerZ, duration = 20) 
        
        self.delayMod = Wait(2.5) 
        self.WalkSeq = Sequence(Parallel(self.Mod1HprIv,self.Mod1PosIv,self.Mod1ActIv,self.Mod1Z),self.delayMod)
        self.WalkSeq.start()

    # Update Player Z (Keep player on Terrain)
    def updatePlayerZ(self, time):
        if self.page == 1:
            startpos = self.Mod1.getPos() 
            self.playerZcol.traverse(render) 
    
            if self.playerZcolQueue.getNumEntries>0: 
                self.playerZcolQueue.sortEntries() 
                point = self.playerZcolQueue.getEntry(0).getSurfacePoint(self.env) 
                self.Mod1.setZ(point.getZ()) 
            else: 
                self.Mod1.setPos(startpos) 
        # End updatePlayerZ 

    #Records the state of the arrow keys
    def setKey(self, key, value):
        self.zoomIn = 0
        self.zoomOut = 0
        self.keyMap[key] = value

    # Accepts arrow keys to move either the player or the menu cursor,
    # Also deals with grid checking and collision detection
    def move(self, task):

        elapsed = task.time - self.prevtime

        # save mod's initial position so that we can restore it,
        # in case he falls off the map or runs into something.
        startpos = self.Mod1.getPos()

        # If the camera is too far from mod, move it closer.
        # If the camera is too close to mod, move it farther.
        self.camvec = self.Mod1.getPos() - self.my_camera1.getPos()
        self.camvec.setZ(0)
        self.camdist = self.camvec.length()
        self.camvec.normalize()
        if (self.camdist > 10.0) and self.zoomIn < 1 and self.zoomOut < 1:
            self.my_camera1.setPos(self.my_camera1.getPos() + self.camvec*(self.camdist-10))
            self.camdist = 10.0
        if (self.camdist < 5.0) and self.zoomIn < 1 and self.zoomOut < 1:
            self.my_camera1.setPos(self.my_camera1.getPos() - self.camvec*(5-self.camdist))
            self.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.Mod1GroundHandler.getNumEntries()):
            entry = self.Mod1GroundHandler.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.Mod1.setZ(entries[0].getSurfacePoint(render).getZ())
        else:
            self.Mod1.setPos(startpos)

        # Keep the camera at one foot above the terrain,
        # or two feet above mod, 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"):
            self.my_camera1.setZ(entries[0].getSurfacePoint(render).getZ()+1.5)
        if (self.my_camera1.getZ() < self.Mod1.getZ() + 1.5):
            self.my_camera1.setZ(self.Mod1.getZ() + 1.5)
                
        # The camera should look in mod's direction,
        # but it should also try to stay horizontal, so look at
        # a floater which hovers above mod's head.
        self.floater.setPos(self.Mod1.getPos())
        self.floater.setZ(self.Mod1.getZ() + 1.5)
        self.my_camera1.lookAt(self.floater)

        # Stop the task and return to menu
        if self.quitter > 0:
            self.returnOptions()
            return Task.done

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

    #**************************************#
    """ Setup Menu Functions             """
    #**************************************#

    # This Function Ends the Setup Screen
    def returnOptions(self):
        self.page = 0
        self.quitter = 0
        # Cleanup scene 1
        taskMgr.remove("moveTask")
        # Disable cameras
        self.dr1.setActive(0)
        # remove models from scene graph (don't render)
        self.Mod1.cleanup()
        self.Mod1.removeNode()
        # remove models from game
        loader.unloadModel("models/world")
        self.Mod1.delete()
        self.showMainMenu()
            
    #**************************************#
    """ Functions to Swap Out the Menus  """
    #**************************************#

    # Hides the Main Menu
    def hideMainMenu(self):
        #self.menuBack.hide()
        self.playButton.hide()

    # Shows the Main Menu
    def showMainMenu(self):
        #self.menuBack.show()
        self.playButton.show()

    """Commence Game End"""
    # For ending Game
    def endGame(self):
        if self.page > 0:
            self.quitter = 1
    
world = World( ) 
run( )
        render.find('**/+CollisionVisualizer').removeNode()

OR

        render.find('**/+CollisionVisualizer').node().clear()

No, you have to unroll everything you have done yourself. You can try my crushInstance method in myFinder.py in there :
discourse.panda3d.org/viewtopic.php?t=3875
I haven’t tested it with collision, so you may need to add Collision* destructors to the destructors list.

:smiley:
Thanks ynjh_jo.
I’m quite sure this will work.

EDIT:
I checked it.
The problem is only half fixed.
The collider doesn’t render anymore, but it is still there, since my frame rate decreases every time I enter the game.

Do you know of anything else I might do?
Thanks

The world model is still there. You just unload it from models pool and haven’t removed it from the scenegraph. unloadModel doesn’t do anything to the scenegraph.

:confused:
Huh? I’m lost.
Do you mean that I have not really destroyed or removed the model completely?
Could you show me what you mean, and how I can remove them completely?
Better yet, could you help me cleanup my code?

The idea is simple, you write a program that starts your main program. When it finishes you restart it again. If the error code upon finishing is X then you quit your restarter program too.

Please… please…
It may be simple, but at this stage of my programing, I have an idea what you mean, but I do not have the know-how.
Could someone please show me in my above code?

I found something in the API Reference called , but I do not know how to write the code correctly (I have a lot to learn).
I tried self.cTrav.clearColliders() and others, but get an error. What is the correct way to use this method?

You forgot to remove the world model from the scenegraph. The slowdown is due to collision is done against multiple copies of world model.

self.env.removeNode()

loader.unloadModel only removes model reference in models pool. Calling it is only good when the actual model in file has changed, and you want to load the new one in the next load instead of the old one.
self.cTrav.clearColliders() is a must, I forgot to mention that. What’s the error ?

If you use my crushInstance, all you have to do are :

  1. split the world class and the menu
  2. to cleanup world class instance, call crushInstance(findClass(‘World’)[0], world). It sweeps all instance attributes passed to it, and uses the destructors list to destroy each different type of known objects. Just be sure to clear the visitedInstances list before calling it.
    But it only works for saved objects only, so if you want to be able to crush an object, you have to save it as an attribute somewhere.
  3. add Collision* destructors to the destructors list, eg.
'CollisionTraverser':['clearColliders']

So you don’t have to manually unroll everything by hands.

Thanks for replying.

I probably made a mistake in the spelling of self.cTrav.clearColliders(), because I don’t get the error anymore.

I tried self.env.removeNode(), but I still get a slowdown each time.
I find it very strange and puzzling, that this collision ray thing remains.
It didn’t happen before I used that code:

        self.playerZcol = CollisionTraverser() 
        self.playerZcolQueue = CollisionHandlerQueue() 
        self.playerZcol.addCollider(self.Mod1GroundColNp, self.playerZcolQueue) 
        self.playerZcol.showCollisions(render) 

    # Update Player Z (Keep player on Terrain)
    def updatePlayerZ(self, time):
        if self.page == 1:
            startpos = self.Mod1.getPos() 
            self.playerZcol.traverse(render) 
    
            if self.playerZcolQueue.getNumEntries>0: 
                self.playerZcolQueue.sortEntries() 
                point = self.playerZcolQueue.getEntry(0).getSurfacePoint(self.envPart) 
                self.Mod1.setZ(point.getZ()) 
            else: 
                self.Mod1.setPos(startpos) 
        # End updatePlayerZ 

…to allow for using intervals on the collision mesh.
It’s very distressing.:confused: :cry: I believe strongly, that the problem lies there, because before I used the code, my game worked fine. After cleanup, the framerate always remained stable, even without self.env.removeNode().

I really don’t know what to do at this point, other than to start over from the point where everything ran smoothly, and go from there.
If there are anymore ideas, I’ll still be thankful to hear.

I tried running your IDE_STARTER.py ynjh_jo, but when I opened dyn1.py, the window just disappeared. I waited about half minute, but saw nothing, so I assumed it crashed, or maybe I did something wrong.

Thanks for trying to help.

OK, I took a closer look. The fact is all your lights, cameras, and Z collision traversal are still there. The worst of all is you use 2 coll traverser for adjusting player’s Z position, 1 from Ralph’s code, and 1 from Tiptoe’s code. 2 collision traversal for exactly the same thing are done every frame. Just pick one.

Try this :

    # This Function Ends the Setup Screen
    def returnOptions(self):
        self.page = 0
        self.quitter = 0
        # Cleanup scene 1
        taskMgr.remove("moveTask")
        # Disable cameras
        self.dr1.setActive(0)
        # remove models from scene graph (don't render)
        self.Mod1.cleanup()
        self.Mod1.removeNode()
        self.Mod1.delete()
        self.cTrav.clearColliders()
        self.playerZcol.clearColliders()
        # remove models from game
#         loader.unloadModel("models/world")
        # remove lights
        for l in render.findAllMatches('**/+LightNode').asList():
            l.getParent().setLightOff(l)
            l.removeNode()
        # remove all remaining ordinary children
        render.removeChildren()
        self.showMainMenu()

Simply render.clearLight() doesn’t propagate to the children too, so I use findAllMatches to clear all lights, which maybe not attached directly to render.

About the IDE :
sorry, I forgot to comment this part :

# APP_files=[
# #            r'F:\- new sorting\kcl\30 3 kpoi33 dpf ssd fs234 .py',
#            r'I:\PROGRAMMING\Panda3D\=coba IDE\dyn1.py',
# #            r'I:\PROGRAMMING\Panda3D\=coba IDE\smileyClass.py',
# #            r'I:\PROGRAMMING\Panda3D\=coba IDE\pandaClass.py',
# #            r'I:\PROGRAMMING\Panda3D\=coba IDE\jackClass.py',
#            ]

should be fine now.

I GOT IT!
I had already removed the second collider, but that did nothing. I realized then that the problem lies in the interval. Maybe the LerpFunc is still updating. So I put this code in the move task:

# stop rendering collider if interval is done
            if self.Mod1PosIv.isStopped():
                render.find('**/+CollisionVisualizer').node().clear()

and WOOHOO! It worked. My frame rate is once again stable. :smiley: :smiley: :smiley:

I can now get back to making progress with the game.
Thanks for all your help. I don’t think I could have done it without your input. I learned a few codes that have now become useful to me. Thanks

treeform

Were you refering to something like this:

import direct.directbase.DirectStart
from direct.gui.DirectGUI import *
...
class World(DirectObject):
    # constructor
    def __init__(self):
        base.disableMouse()
        camera.setPosHpr(Vec3(0,-15,7),Vec3(0,-15,0))

        self.loadModels()
        self.loadSounds()
    
        self.setupLighting()
    # end __init__
    
    def start(self):
        self.setupCollisions()
        
        # setup key controls
        self.accept("escape",sys.exit)
        
        self.accept("arrow_up",self.walk)
        self.accept("arrow_left",self.turn,[-1])
        self.accept("arrow_right",self.turn,[1])
        
        # setup picking
        self.picker = Picker()
        self.panda.setTag("event","hit-panda")
        self.accept("mouse1",self.pick)
        self.accept("hit-panda",self.hitPanda)
        
        # start camera task
        taskMgr.add(self.cameraFollowTask,'cameraFollowTask')
        
        # start music
        SoundInterval(self.music).loop()
    # end start

...
# end class World

def startWorld():
    clickToStart.remove()
    world.start()

world = World()
clickToStart = DirectButton(image="clicktostart.tif",command=startWorld)

run()

I had used this when I first started using Panda, but I found it was one-way. At the time I didn’t know how to reverse it.

Thanks again ALL. :smiley: