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