Subclassing Tricks for BGE

I must say that I am a convert to subclassing game objects. It allows some really neat things to be done quite simply, such as making lots of identical (or nearly so) setups on objects.

/ / / / / CAUTION / / / / / /
Subclassing is a relatively advanced python programming skill. This resource assumes some programming knowledge. If you don’t have it, here are some great resources about subclassing:

Create a basic sub-class:
To create a subclass in blender, you simply create a class subclassing bge.types.KX_GameObject, and pass the object to it when you initialize it:


import bge


def make_projectile(cont):
    Projectile(cont.owner)
    cont.script = __name__ + '.execute'
    
def execute(cont):
    cont.owner.run()
    
class Projectile(bge.types.KX_GameObject):
    def __init__(self, old):
        print('subclassing1')
        
    def run(self):
        print('running')

Simply creating an always linked to a python controller running the function ‘make_projectile’ will mutate the game object into a projectile object.

Sub-sub-class in blender:
Yes, it works like it should. I thought I’d just point this out for the people who don’t know about it:

import bge

def make_homingprojectile(cont):
    HomingProjectile(cont.owner)
    cont.script = 'subclasstest.run'
    
def execute(cont):
    cont.owner.run()
    
class Projectile(bge.types.KX_GameObject):
    def __init__(self, old):
        print('subclassing1')
        
    def run(self):
        print('running')

    def other(self):
        print('other')
        
class HomingProjectile(Projectile):
    def __init__(self, old):
        super().__init__(old)
        print('subclassing2')
        
    def run(self):
        print('running2')
        self.other()

This will subclass the game object with HomingProjectile, and will execute the ‘run’ function from Homing Projectile, and the ‘other’ function from Projectile.

Auto-make subclassing functions:
To subclass an object in bge, you have to create a function to mutate the object. So let’s say I have a class called ‘HomingWeapon’. To mutate a game object into one of these ‘homing weapons’ I have to create a function, something like ‘make_homing_weapon.’
But if I have a large script with 10-20 different object types defined in it, it get’s cumbersome to have all of these functions separately defined.
So let me present to you, the auto-subclass-function-generator:

# This is a solution to create a million nearly identical
# functions, one for each object type/class. It is only necessary because
# blender's logic has an odd way of handling subclassing of game_objects
def execute(cont):
    cont.owner.run(cont)


globals_class_list = [c for c in globals() if type(globals()[c]) == type]
exec_str = ''
for class_type in globals_class_list:
    function_name = 'make_'+class_type.lower()
    function_contents = '''
def {0}(cont):
    {1}(cont.owner)
    cont.script = '{2}.execute'
    cont.owner.run()
'''.format(function_name, class_type, __name__)
    exec_str += function_contents


exec(exec_str)

Put this at the end of your script, and when the module is run, for each class defined within, it will create the function:


def make_{classname}(cont):
    ClassName(cont.owner)
    cont.script = 'module_name.execute'
    cont.owner.run()

If you have a bit of coding knowledge, it is easy enough to see that this will mutate your object, and then change the controllers script to run a function in the same file called ‘execute’ which is also defined in the codeblock I posted above. This simply calls a function internal to the class. I tend to use ‘run’ but you can use whatever you like.

A simple demo module for this would be:

class HomingProjectile(bge.types.KX_GameObject):
    def __init__(self, old_object):
        print('Making Homing Projectile')
        
    def run(self, cont):
        print('boo')

# This is a solution to create a million nearly identical # functions, one for each object type. It is only necessary because
# blender has an odd way of handling subclassing of game_objects
def execute(cont):
    cont.owner.run(cont)


globals_class_list = [c for c in globals() if type(globals()[c]) == type]
exec_str = ''
for class_type in globals_class_list:
    function_name = 'make_'+class_type.lower()
    function_contents = '''
def {0}(cont):
    {1}(cont.owner)
    cont.script = '{2}.execute'
    cont.owner.run()
'''.format(function_name, class_type, __name__)
    exec_str += function_contents


exec(exec_str)

Then in logic bricks, create:
Always -> python controller: ‘modulename.make_homingprojectile’

The console will spam you with ‘boo’ which comes from the ‘run’ function inside the class. This also works fine with the sub-sub-class like I described earlier.

Thanks sdfgeoff, I will look into this closer for sure!

Thanks for the nice explanation. I haven’t seen it before but now it helps me alot:)

Problem with subclassing is that you have to mutate the objects all the time. Also that the resulting objects are huge (have a lot of methods and attributes). Instead BGECore uses behaviors which allows for the same (if not more) features without mutating. Adventages respect behaviors would be the possibilty to overwritte an existing a method, but you probably don’t want that.

In any case is still better than no form of OOP, which is the current standard here.

EDIT: When I’ve said OOP here, I mean the specific case where you use and declare classes in your own code, not a more abstract interpretation of it.

I use managers, and listeners, and try and run the listeners only when doing things, and have the sensing in the manager,

This is OOP correct?

Logic runs in the object executing the command, over time and it then returns to a resting logic (not running python)

How would you do this with subclassing?

Example manager = random raycast cone vision in each unit on list, every x frames(not each frame)

Example= listener, when the manager detect a enemy etc, it moves the listener objects steering target to the hitObject and then activates steering logic in the listner until it reaches its target, or forgets about them.

@elmeunick9:
You mustate each game object once, and if you do it right, properties like position and rotation on your object make sense.

To be honest, I don’t use mutation that much for BGE, but then I haven’t done any big games here for a while.

They’re not inherently relevant to one another. Forget OOP - nearly everything you do is OOP. Even composition is OOP (Python data are objects).

Mutating objects (subclassing is technically something else) just adds a layer of convenience that you don’t need to keep a relationship with your python object and the bge game object. It also creates problems as now the BGE owns the python object etc…

I don’t use it any more because I prefer to keep things clean and implementation agnostic.

import bge

def make_projectile(cont):
    Projectile(cont.owner)
    cont.script = 'subclasstest.run'
    
def execute(cont):
    cont.owner.run()
    
class Projectile(bge.types.KX_GameObject):
    def __init__(self, old):
        print('subclassing1')
        
    def run(self):
        print('running')

Line 5 (cont.script = ‘subclasstest.run’) doesn’t quite work for me. What’s meant by “subclasstest” - I guess it’s the filename right?
When I connect an Always-sensor without Pulse-mode, run() won’t be executed at all. With Pulse-mode on I get:
AttributeError: module ‘subclasstest’ has no attribute ‘run’

But since I call the instance once I don’t need triggering anyways. Moreover what’s the matter with the execute()-function then?

@piccobello:

In the snippet you sent, there is no “run” method, so indeed when you set the controller to run “subclass.run” it fails.
In your case you should write "cont.script = ‘subclass.execute’ ".

The trick is that you plug an Always brick in pulse mode on your Python controller in module mode, so that it triggers the module each frame or so. In the Python controller at first you tell him to run “subclass.make_projectile”, its the initialization. Right away, on the first trigger, the “make_projectile” function change the function the controller will execute to the “execute” function: from now on it is the function that will be called each frames or so.

Although this implies that the frame where you convert your object to a bullet the “execute” method will not be called because it is delayed on every next frames, you could write this:


def make_projectile(controller):
    Projectile(controller.owner)
    controller.script = 'subclasstest.execute'
    execute(controller)

Nice! Tried your solution and it works perfectly asyou described. Thanks for pointing things out :slight_smile:

Thanks for finding the solution WKnight02. Fixed in first post.
Also changed so it auto-finds the module name rather than having ‘subclasstest’ hardcoded.

Is there any way to initiate an instance with this method when using external modules only by using a python controller?
Let’s say I have a bunch of classes I placed externally and I want my objects to be instanced once I call them, however I don’t want to write an additional python file inside Blender to import the external module/class

EDIT: Nevermind, found it by myself. it’s pretty the same like doing it in plain python:
scripts/file.py

[ Always ] -> [ Python(Module mode): scripts.file.main ]