Let me explain (hopefully with understandable words):
Script mode:
A script gets executed at each call starting with the first statement at the top.
All code will be executed unless you explicitly implement alternatives (if, for, while …).
The local (script) context lives for the call only. When the execution finishes -at the end of the script- all variables are removed. Objects can survive when they are referenced by objects outside of the script (e.g. a property of a KX_GameObject, an attribute of a module).
The effect is, you need to create all variables at the next call again. This is not that bad as it sounds. Think about a script as stateless code.
Module mode
A module gets executed at the first call starting with the first statement at the top.
After that the called function will be executed from top to bottom but only that function.
Any subsequent call will execute the function only.
In difference to a script a module can store variables. You do that just by defining them at the module level (indentation zero).
These module variable remain as long as the module remains loaded. Think about a module as stateful code.
You can change module variables. But you need to do that explicitly. If you do not change them they will keep the last set value.
Dangerous code
Why is this code dangerous in module mode but fine in script mode?
import bge
SCRIPT_MODE = 0
controller = bge.logic.getCurrentController()
def printController():
print()
print("Is module variable up-to-date?",
controller == bge.logic.getCurrentController())
print()
print(" module variable:", id(controller), controller)
print("current controller:", id(bge.logic.getCurrentController()), bge.logic.getCurrentController())
print()
if controller.mode == SCRIPT_MODE:
printController()
- Because script mode is stateless. The local variable “controller” gets updated from context (bge.logic) at each execution. It always refers the calling controller.
- Because module mode is stateful. The module variable “controller” gets updated at the first call only. This means “controller” will refer to the first controller calling any function in this module. This is not necessarily the calling controller.
Examples:
called the above code from two module controllers and one script controller:
Blender Game Engine Started
Is module variable up-to-date? True
module variable: 225527520 PythonModuleCallA
current controller: 225527520 PythonModuleCallA
Is module variable up-to-date? False
module variable: 225527520 PythonModuleCallA
current controller: 225527808 PythonModuleCallB
Is module variable up-to-date? True
module variable: 225527184 PythonScript
current controller: 225527184 PythonScript
Blender Game Engine Finished
How to avoid that?
A) grab the controller from context. There is no need to store it in a module variable.
B) update the module variable before accessing it.
C) you guaranty that only one (and just that one) controller is calling this module
D) use script mode
Called the above code before and after a scene reload (module mode):
Blender Game Engine Started
Is module variable up-to-date? True
module variable: 225528648 Python
current controller: 225528648 Python
---- Reloading scene ----
Is module variable up-to-date? False
Python script error - object 'Empty', controller 'Python':
Traceback (most recent call last):
File "...\demo.py", line 10, in printController
SystemError: Blender Game Engine data has been freed, cannot use this python variable
Blender Game Engine Finished
This example shows a much more obvious error. Unfortunately it is not that easy to identify, because you might think you have a single object with just one controller. But a scene restart does not cause a module reload.
How to avoid that?
A) grab the controller from context. There is no need to store it in a module variable.
B) update the module variable before accessing it.
C) you guaranty that only one (and just that one) controller is calling this module
D) use script mode
E) reload the module after scene reload
This belongs to the current controller. There are other objects that result in a similar situation:
- current game object
- current scene
- sensors
- controllers
- …
Still there are situations when it is useful e.g. referring to an inventory object, referring to a current selection.
Attention: The same situation applies when you store the above objects in an object of an own class and this object is stored in a module variable.