Python Initialization Examples

The Game Engine forum has taught me to initialize code like this:

if not 'init' in own:
    #Insert code to be run once here.
    own['init'] = True

Though this has always bothered me.

It seems like a ‘hack’ for lack of a better word.

I vaguely remember a thread where similar things were discussed, and I know there are other ways to accomplish this, but I can’t for the life of me find them.

Cheers!

Edit: Error 40 inside the code tags : 3

i only do it like that in small examples to keep complexity down, in bigger projects i would have a separate init script that is only run once.

Typically I do either:


def init(cont):
    if 'GAMENAME' not in cont.owner:
        cont.owner['GAMENAME'] = GameMainClass()
    cont.owner['GAMENAME'].update()


class GameMainClass():
    def __init__(self):
        ''' Init code should go in here '''
    
    def update(self):
        ''' Update code goes here '''

Or if I want to save that ‘if’ statment, you can do:


def init(cont):
    cont.owner['GAMENAME'] = GameMainClass()
    cont.script = __name__ + '.run'   # Change the script run from the controller. __name__ evaluates to the scripts name.

def run(cont):
    cont.owner['GAMENAME'].update()


class GameMainClass():
    def __init__(self):
        ''' Init code should go in here '''
    
    def update(self):
        ''' Update code goes here '''

Once you’re inside your main game object, you can do whatever you like with regards to loading etc. Obviously, name your classes better than that example.

I run init in my GUI code and my world code…just because it gets messy to have it all in one script(and they are separate scenes ofc)…at least for me…if I cannot track something down it gets frustrating… I do not like it, but it it can be a necessity…

That’s basically why I’m asking this, I feel like I’m starting to get to the point where I could complete a larger project, and not just a vertical slice of a prototype : P

Wait, wait, wait, hold on a minute, what just happened there? That’s basically the method I learned, but with a class slapped in there, which has the nifty initialization/constructor method built into it?
Man that’s really cool though, I never even knew that a property could be am object like that. Mind = BLOWN

What the!? How did you even find/come up with this stuff? xD



Yup, we use the game object classes initialiser for the initialisation code. I mean, it makes sense doesn’t it?

I use that system for all my projects, but for bigger ones there is another layer. I mean, what happens if your initilization code adds a scene (takes one frame to occur), or is something that takes a long time (eg libload). You need a loading screen. So how about this:


import bge
import functools

def init(cont):
    cont.owner['GAMENAME'] = GameMainClass()
    cont.script = __name__ + '.run'   # Change the script run from the controller. __name__ evaluates to the scripts name.


def run(cont):
    cont.owner['GAMENAME'].update()




class GameMainClass():
    def __init__(self):
        ''' Init code should go in here '''
        self.load_screen = LoadingScreen()

        self.load_screen.functions += [
            some_long_function,
            other_long_function,
        ]

        self.update = self.load  # Make the update function run the loader

    def load(self):
        '''Update the load screen'''
        self.load_screen.update()
        if self.load_screen.done:
            self.update = self.play_game  # Start the game playing
    
    def play_game(self):
        ''' Update code goes here '''


class LoadingScreen:  # Note: this can be split into two classes: a loader and it's visuals
    def __init__(self):
        self.functions = [
            functools.partial(bge.logic.addScene, 'Loading')  # Add the loading screen as the first function
            start_load  # Then continue setting up the loader. Other functions should be appended to this list.
        ]  
        self.scene = None
        self._max_len = 0
        self._max_logic_frames = bge.logic.getMaxLogicFrame()

    def start_load(self):
        '''Sets things up for the loader (ie finds the loading screen)'''
        self.scene = next(s for s in bge.logic.getSceneList() if s.name == 'Loading')
        self.functions.append(self.remove_load_scene)  # Make sure the loader removes the load screen as the last function it runs
        self._max_len = len(self.functions)  # Store total number of functions for computing percentage
        bge.logic.setMaxLogicFrame(1)  # Ensure that long-running functions don't cause BGE to jump render steps

    def update(self):
        '''Runs the next function'''
        function = self.functions.pop(0)  # Grab first item from list (removing it from list)
        function()
        
        if self.scene is not None and not self.scene.invalid:  # Prevent against execution without loading screen present - such as first frame
            percent = len(self.functions) / self._max_len
            self.scene.objects['LoadingText'].text = "Loading: {}% ".format(int(percent*100))

    @property
    def done(self):
        # Note bool of a list is True unless the list is empty. So this Not means that it is 'Done' whenever the list is empty.
        return not bool(self.functions)  

    def remove_load_scene(self):
        # Restore previous max_logic_frames so that logic stays in sync with real time even if frame rate drops
        bge.logic.setMaxLogicFrame(self._max_logic_frames)  

        # Remove loading screen
        self.scene.end()
        


Again, you can go more complex than this if needed, but this is a good starting point for a load screen. When do you need more complex? When you start needing resource management, multiple language support etc. etc.

Some things are a little less clear than I’d like such as:

  • Changing the function pointed at by GameMainClass.update
  • The loader starts with a full list and runs the left-most, decreasing list length until it reaches zero
  • The loader adds functions to it’s own function list to handle scene creation and finding

You can also set up an on_load_complete callback instead of polling the load_scene.done but I think reading load_screen.done is more understandable when glancing at the code.


Also, I haven’t seen that coffee-cup-foot meme. It made me chuckle. Good find.

When I used the game engine, I’d have an “Always” without true pulse to the init module and an “Always” with true pulse on the main module. That way I don’t have unnecessarily code (init is done literally once in a game, why waste an if statement on it if 99.9999999% of the time it’ll be false?).

Basically what sdfgeoff is doing.

You can also use a module controller and call a function.


# import bge get owner and so on.

#Initialize your variables here.
own['init'] = True

#Call the run function with the module controller
def run(cont):
    #Insert code here.
    own = cont.owner

This is wrong when you want a function to initialize once per game object.

Executing a function at module level is good to initialize a module, but once the module is initialized you can’t do it again (you could reload the module, but this brings more problems)

The best way is still to have an init function, and a main/update, and trigger them when needed in the object, unless its to setup a module of course.

I think there is no “best” method. I always depends on your needs, your style (and your knowledge).

I talk about initializing game object setup, not initializing other things like module data, data or scene data (these can has other but similar options).

Property “init”

You basically store in the object if the object was initialized or not.


if not 'init' in owner:
    owner['init'] = True
    initialize()

else:
   performBusiness()


Benefits:

  • all code in one file
  • all code pretty close to each other
  • one property that holds the initialization status, rather than checking the outcome of initialization
  • guarantied correct processing order due to static code
  • fire and forget logic brick setup (trigger a single python controller, rather than multiple)
  • business code can assume certain conditions without fail safe checks

Drawbacks:

  • constant check for initialization (wast of time)
  • mixing initialization processing with supposed business (can confuse the reader)
  • the property can incorrect (is set, but initialization is not correct)
  • becomes complex on large initialization blocks
  • needs to be repeated when other code needs initialization too

Initialization method - init()

This expects you to split the code into an initialization part and one or mode business parts. These code does not need to bee together, not even in the same file.


def initialize():
    ...


def performBusiness():
    ...

Benefits:

  • can be in one file or different files
  • explicitly separates initialization from business
  • you explicitly setup when to run initialization and when to run business
  • good on complex initialization
  • no code replication
  • business code can assume certain conditions without fail safe checks

Drawback:

  • process order not guarantied (as it depends on your setup)
  • code of the same topic distributed over several code blocks (Yes, it is a benefit too ;)).
  • more complex setup as you need at least two Python controllers (one for initialization and one or more for business)

Fallback operation

This basically means you do not initialize at all. You check for “non-initialized” conditions when necessary = no assumption about the object state. In most cases you want to know if a property is set.

Benefits:

  • guarantied processing order
  • small validation overhead
  • check initialization status (rather than an aggregated cache)
  • only done when needed
  • when encapsulated you will not disturb the business code
  • no additional property holding the initialization status (cache)

Drawbacks:

  • can cause code replication
  • additional processing each time the business code gets executed (check if present)
  • can become complex when there are many checks to perform

Fallback Value


def doBusinessWithFallbackValue():
    businessValue = owner.get("property", fallbackValue)

This method definition shows how to deal with missing value and provides a valid one if it is not present.

Additional benefits:

  • part of the API
  • minimal impact on the readability of the business code
  • fire and forget logic brick setup (trigger a single python controller, rather than multiple) is possible

Additional drawbacks:

  • the property will not be set
  • additional fallback operation when the proerty is still not set
  • code replications when you need to get the value from object again
  • complex fallback value calculation will occur each time this method is used

I often use this on optional properties.

Setup Property


def doBusinessWithPropertyUpdate():
    # this guaranties you get a value - if it does not exist you get the fallback value
    # it writes the fallback value into the property
    if "property" not in owner:
        owner["property"] = fallbackValue
    businessValue = owner["property"]

This method definition represents pretty much the property “init” method, but checks for the concrete initialization status rather than looking for an aggregated place holder (property “init”). It also allows to use complex fallback value creation (e.g. loading from a database).

Additional Benefits:

  • the fallback value is persisted in the property
  • the faclback procesisng is done once per property and object
  • you can use complex fallback value calculation

Additional Drawbacks:

  • makes business code look complex
  • you might need encapsulate the access method into libraries

Advanced topic:

You can separate the fallback processing with a fallback processing method:


def doBusinessWithPropertyUpdate():
    businessValue = getProperty("property", fallbackValue)

def getProperty(propertyName, fallbackValue):
    if propertyName not in owner:
        owner["property"] = fallbackValue
    return owner["property"]


This is similar to the owner.get() method. In most cases this is fine.

Please notice you need to create the fallback value all the time. When you have complex fallback value calculation you might want to use a factory function (or another method) to avoid the processing overhead:


def calculateFallbackValue():
    return fallbackValue # assume this takes a while 

def doBusinessWithPropertyUpdate():
    businessValue = getProperty("property", calculateFallbackValue)

def getProperty(propertyName, factoryFunction):
    if propertyName not in owner:
        owner["property"] = factoryFunction()
    businessProperty = owner["property"]


As you see this increases the complexity, but it reduces the impact on processing time.

Remarks

Which way to go is up to you and strongly depends on your situation.

It is often good enough to assume the objects are setup correctly before the game even started.
When you create assets where you do not know the situation beforehand you might want to use one of the above methods to ensure the situation is as you expect. The has often impact on efficiency, readability, flexibility and maintenance.

When you write your business code you typically do not want to think about hundreds of checks. Unfortunately you need to deal with that.

Just my 5 cents

Woah! Overwhelming feedback xD

This stuff is golden, I truly appreciate it : D
I’ll start testing it right now : D

EDIT: I tested your code sdfgeoff, it works wonders :slight_smile: Hmmmm… I’m gonna work through all of these and find one that works well for me : D

EDIT 2: Typos.