Switching between levels

What is the best way to switch between levels in a game in Panda3d? I am using python! Some sample code is certainly appreciated

I would suggest encapsulating your levels into a “Level class”, responsible for both building and cleaning up a level. That way you can simply instantiate a “level object” to load a level (presumably passing in data to indicate which level to load or what objects to include), and then clean it up once you’re done, whether that’s because of a game-over or because the player is moving on to the next level.

[edit] I don’t claim, please note, that this is the best way. Indeed, I’m not sure that there is a single “best way”, as different methods may work for different people. Rather, I suggest it merely as a method of handling levels, and leave it to you to determine whether it works for you.

My option in this post.

Well, I seem to have got a notification that brought me here, hmm, well, the solution to the topic serega-kkz has linked uses txt files to store and load information for panda to tear down both actors and scenes and rebuild new ones in their place to achieve loading new areas.

If you are fine with this, I will post the simplest form of sample code of the method discovered in the linked topic.

# this sample is a couple of functions that use a text file's information to
# tear down what is on the render currently and rebuild elements using info
# from a text file.
    def Load(self, task):
        global lpuase
        global load
        global rlf
# lpuase is a global boolean variable that can be applied to code dependent on
# what is affected in this code, in this case it should be applied to anything
# relating to the scene or actors.
        if not lpause:
# load is "stage" variable for the entire "load process"            
            if load is 0:
               lpause = True
        else:
            if load is 0:
# rlf is a global empty list in which the piece of code below will take the lines
# from a text file in a file folder named "room", which the file itself is named
# "room" with the number that is the variable "room", input each line as a
# string into it's own item within the "rlf" list to fill it. 
                rlf = []               
                txt = open(str(os.path.join(str(base.mainDir),'room', "room"+str(room)+'.txt')))
                rlf = txt.readlines()
                txt.close()
                txt = None
                load = 1
                   
        return Task.cont

    def Scene(self, task):
        global lpause
        global txtdur
        global rlf
        global scene
# this function will wait on the load function, until it is finshed.        
        if not lpause:
            if load is 1:
                lpause = True
        else:         
            if load is 1:
                if txtset is 0:
                    sp = []
# because the rlf list is a duplicate of the text file it is read from just
# organized into a string list, this code below will search out the word
# [Scene] which is the header of the section where you will store the information
# about objects loaded from loader.loadModel or I will call scene objects.
                    sp = [rlf.index(i) for i in rlf if "[Scene]" in i]
                    ep = []
# this code line grabs the index value of the line that contains "[ScnEnd]"
# which is the footer of the section where you store the information about
# scene objects, of course everything in between the header and footer
# are infomration about objects stored in seperate lines.
                    ep = [rlf.index(i) for i in rlf if "[ScnEnd]" in i]
# txtdur is a global variable that determines what line in terms of index value
# will be read, this code below checks if txtdur is below the index value of
# the line that contains "[Scene]" or the section header. if it is below
# then it sets it up to be above it by 1.
                    if not txtdur >= sp[0]:
# so "scene" will be our global list of objects loaded from the loader.loadModel, and
# when this list has anything, it will begain to tear down anything object in
# this list until it is empty.
                        while len(scene) > 0:
# this is a while loop, and a temperary variable which takes the lentgh of the
# list and subtracts by 1 in order obtain the index number of the last item of
# the list, using this, it cleans the node and deletes the item afterward, one
# by one until there is nothing left.
                            cul = int(len(scene)) - 1
                            if scene[cul].isStashed():
                                scene[cul].unstash()
                            scene[cul].setScale(1.0)     
                            scene[cul].removeNode()
                            scene[cul] = None
                            del scene[cul]
# once everyting is clean, then textdur is set to past the section header.                            
                        txtdur = sp[0] + 1
                    else:
# so another while loop plays while txtdur is under that of the index value
# of [ScnEnd] or basically the footer of the section, what basiclly happens is
# a line is read using txtdur as the index value, then txtdur at the end is
# updated with 1 more value, this repeats until txtdur is no longer lesser
# then the value that is the footer, so bascilly every line in between
# is read and turned into a scene object, using the information of the line.
                        while txtdur < ep[0]:
# this line takes the string line of the line with a index value that is the
# same as txtdur and breaks it down into another temprary list becuase the
# format of the text file is each line would contain all the aspects of the
# object you want to load in all in one line but each seperated by stars *
                            rlfdef = rlf[txtdur].split("*")
# this will take your filepath which would be first in the line and load in
# that model into a item of our scene list.
                            scene.extend([loader.loadModel(rlfdef[0])])
# this temperary variable would take length of the scene list and subtract it
# by 1 in order to obtain the last item of the list which is the newly loaded
# model from the code line above.
                            scnin = int(len(scene)) - 1
# this of course adds said model into the render for everyone to see.                            
                            scene[scnin].reparentTo(render)
# so the next two aspects of the line would be the location in which the model
# is loaded to, first a name of another object or just "-" dash, then actual
# XYZ coordinates, now the point of the a object's name is to grab that object's
# XYZ coordinates to give to this newly loaded object, now this checks if a name
# of another object is present or not (which would be stated as dash)
                            if not "-" in rlfdef[1]:
# if a name is present, as stated before, the location of the item that is named
# is obtained then combined with the aspect after the name which is just XYZ
# coordinates, if you did not want to state XYZ coordinates, just put (0, 0, 0)
                                scnloc = scene[0].find("**/"+rlfdef[1]).getPos()
                                scene[scnin].setPos(scnloc + eval(rlfdef[2]))
                                scnloc = None
                            else:
# if a name is not present, then a set of XYZ coordinates after the dash is used
# to set the location of the newly added object.
                                scene[scnin].setPos(eval(rlfdef[2]))
# so the last aspect would be the transparency of the said object, useful for
# water and the like, and it would be simple float number, if said number is
# below 1.0, basiclly trasnparent in panda3d terms, then transparency will be
# set based on that number.
                            if float(rlfdef[3]) < 1.0:    
                               scene[scnin].setTransparency(TransparencyAttrib.M_dual, 1)
                               scene[scnin].setAlphaScale(float(rlfdef[3]))
                               scene[scnin].setTwoSided(True)
# txtdur is updated by 1 so the loop can repeat using the next line on it's next
# run.                               
                            txtdur += 1
# so once txtdur reaches the index value of line that contains "[ScnEnd]", every
# variable used in this process is cleaned and reset back thier original
# values before this function was used, except for load which is set to 2.
                        if txtdur >= ep[0]:
                            sp = []
                            rlfdef = []
                            ep = []
                            scnin = 0
                            txtdur = 0
                            load = 2
                        
        return Task.cont

    def Actor(self, task):
        global lpause
        global txtdur
        global rlf
        global actor
# this function will wait on the Scene function, until it is finshed.        
        if not lpause:
            if load is 2:
                lpause = True
        else:         
            if load is 2:
                if txtset is 0:
                    sp = []
                    sp = [rlf.index(i) for i in rlf if "[Actor]" in i]
                    ep = []
                    ep = [rlf.index(i) for i in rlf if "[ActEnd]" in i]
                    if not txtdur >= sp[0]:
                        while len(actor) > 0:
                            cul = int(len(actor)) - 1
                            if actor[cul].isStashed():
                                actor[cul].unstash()    
                            actor[cul].removeNode()
                            actor[cul] = None
                            del actor[cul]                           
                        txtdur = sp[0] + 1
                    else:
                        while txtdur < ep[0]:
                            rlfdef = rlf[txtdur].split("*")
# so this function is basiclly the same as the last, except it accomendates
# the 3 more line aspects for file names of 3 animation files for the actor.
                            actor.extend([Actor(rlfdef[0], {"Act1": rlfdef[1],
                            "Act2": rlfdef[2], "Act3": rlfdef[3]])
                            scnin = int(len(actor)) - 1                           
                            actor[scnin].reparentTo(render)
                            if not "-" in rlfdef[4]:
                                scnloc = actor[0].find("**/"+rlfdef[4]).getPos()
                                actor[scnin].setPos(scnloc + eval(rlfdef[5]))
                                scnloc = None
                            else:
                                actor[scnin].setPos(eval(rlfdef[5]))
                            if float(rlfdef[6]) < 1.0:    
                               actor[scnin].setTransparency(TransparencyAttrib.M_dual, 1)
                               actor[scnin].setAlphaScale(float(rlfdef[6]))
                               actor[scnin].setTwoSided(True)                              
                            txtdur += 1
                        if txtdur >= ep[0]:
                            sp = []
                            rlfdef = []
                            ep = []
                            scnin = 0
                            txtdur = 0
                            load = 3
                        
        return Task.cont

And here is a sample text file the code above would draw information from, you can download it here testroom.zip (657 Bytes)

I,m not a good python programmer, as you can see the use of global variables, so I wouldn’t recommend using this example directly, maybe the pros can chime in and recreate it into something more compatible to python though.

To load a prefabricated object type, such as an actor or car. I’d rather make a constructor class for the character I want. And in a text file I would simply store the name, position and the rest of the tronformation.

1 Like