Subclassing game objects ...

*** Moderation ***
Threadsplit:

This thread is a side thread of dragonspammer’s support request. Nevertheless it is an interesting discussion.
@Raco: feel free to change the title (edit this post in advanced mode)
*** End of Moderation ***

I think I know why this is happening, but I can be wrong. If you are initializing values that supposed to be applied to one object, you should store the class as a duplicate into a property on that object so it will behave locally.

So instead of:

class MyClass:
    
    def __init__(self):
        # initializing
        
    def main(self, object):
        # main function of the class
        
my_class = MyClass()
        
def my_module(cont):
    own = cont.owner
    my_class.main(own)

You can do this:

class MyClass:
    
    def __init__(self):
        # initializing
        
    def main(self, object):
        # main function of the class
        
def my_module(cont):
    own = cont.owner
    if 'my_class' not in own:
        own['my_class'] = MyClass()
    own['my_class'].main(own)

Thanks, sdfgeoff. But I don’t understand it. Could you please apply this to my example? I don’t know if you are talking about the same class and where ‘called first’ fits into all this.

I’ll attempt to do some more explaining.

The game object (your enemy) is represented as a python class. What we can do is we can ‘merge’ this with a class that you write, so that the enemy has special enemy-only functions. This helps to keep information self-contained inside the enemy.

To do this we use the procedure known as sub-classing.

class enemyObject(bge.types.KX_GameObject):

Here we have created a class that has all the same functions and properties as a regular game object (eg worldPosition etc)

Then we can add whatever functions, variables anything else we like into the class, remembering to add the ‘self’ parameter into functions, and before class variables.

But we also have to tell the game engine that we want our object to become an enemyObject, and that is where the function ‘make_custom_game_object’ comes in. A better name in this case is ‘make_enemy’:

def make_enemy(cont):
    old_object = cont.owner
    mutated_object = enemyObject(cont.owner)

After we’ve run it, our game object is an ‘enemyObject’ and the various assert statments were there to check that it had happened properly.

So let’s say that we want to create an enemy with a function ‘kill_player’
Our class will then be:

class enemyObject(bge.types.KX_GameObject):
    def __init__(self, old_owner):
        self.target = player #presumably player is a global variable? Or you can get is some other way
        self.health = 1000

    def kill_player(self):
        self.target(self.player)
        self.fire()

    def target(self, targetObj):
        self.alignAxisToVect(.... and so on)

    def fire(self.targetObj):
        ...

   def activate(self):
        '''This function will be run every frame by the function written a bit later'''
        self.kill_player()

We then need two functions. One to turn the object into an enemy, and one to activate it:


def make_enemy(cont):
    '''Call on the first frame only'''
    old_object = cont.owner
    mutated_object = enemyObject(cont.owner)

def activate_enemy(cont):
    '''call every frame. This will make sure the enemy's code is run'''
    cont.owner.activate()

Note that inside the class, instead of using ‘obj = cont.owner’ or anything like that, we simply use ‘self’ Remember that the class IS the enemy.

Hi sdfgeoff, Thanks a lot! I think I’m starting to understand. I’ll try making an exercise later this day.

Edit: I’ve got it! This really helpful, thank you again. For comparison a blend to illustrate the behaviors of ‘storing the class globally (standard)’, ‘storing the class in a property (unconventional)’ and ‘sub-classing’.

Edit2: Oops, I thought I got it. Seems that I still have to find the proper way…

Edit3: I think I got it finally. Thanks agoose77 and sdfgeoff!

Edit4: As you might notice reading the next posts, it seems I’m not there yet :wink:

Attachments

Classes_00.blend (87.9 KB)

In the blend, how should I ‘call’ the function which makes the mutated object ‘on the first frame’, so I could use the main function in the class later? You are talking about initialization, right?

You run it once on the first frame. To do this, either write a separate initialisation function and run that with priority, or check for a special “signature” on the object, e.g have your mutator class include an attribute. You could also perform the isinstance() check

Thanks for your reply. I ran it on the first frame but it doesn’t work. The console says I can’t call the module from the class on the next frame. Could you maybe just have a quick look at the blend and tell me what I’m doing wrong, please? I really want to know how to properly use scripts with classes for multiple objects. It’s a very simple piece of code.

It does work, you’ve just got far too many other things that needn’t be running. Delete all the logic apart from a single true pulse controller that runs the last script.

However, I wouldn’t go using global variables like that. Just check for a tag as aforementioned.

Okay, I assume with ‘tag’ you mean a property. So now I have this and it works:

from bge.types import KX_GameObject

class MyClass(KX_GameObject):
    
    def __init__(self, own):
        self.property = 0
        
    def main(self):
        self.property += 1
        print('subclassing:', self.property)

def main(cont):
    own = cont.owner
    if 'init' not in own:
        new_own = MyClass(own)
        new_own['init'] = True
    own = cont.owner
    own.main()

If this is the proper way, then thanks a lot for teaching me!

Edit: I guess this is only good if you only have one class to work with, because you have only one object and that’s the owner. So with multiple classes it would seem to me that I’ve no choice but to chose the unconventional way and store every class into a property on the owner. Would that be a correct assumption? (I’m sorry I’m asking so much questions about this, I hope I will be done after this)

Edit2: @ dragonspammer: I hope all of this will help you too, if in fact you had a problem of multiple objects sharing the same variables.

I fail to see why this is being made so complex. You’re passing cont.owner to the class but doing nothing with it. It’s not necessary. Also, if you open your system console you’ll see that cont.owner has to attribute main. What you’re looking for is new_own.main(). I don’t fully understand what you’re trying to accomplish here. Are you just trying to make an enemy template that you can create whenever you need a new one? If so you best bet is to create that enemy with all of its logic and throw it on a different layer or something. Then when you need a new one just use an Edit Object > Add Object actuator with the reference being say, Cube.000. That leaves just a simple script that loops through all objects in the scene and call their activate method as sdfgeoff described.

So, unless I’m completely misunderstanding what you’re trying to accomplish here, you’re making this way harder on yourself than you need to be. Your new_own object will share absolutely no similar properties to your current cont.owner with the exception of what is in KX_GameObject. And for that you’re not even fully inheriting from KX_GameObject.

I’m fairly certain I understand what you’re trying to do, and if I’m right let me know and I’ll gladly show you what I mean if it doesn’t make sense. If I’m totally not understanding your goal then just disregard, haha.

EDIT: I JUST realized this thread was hijacked. I thought it was the op asking all these questions, which was why I was so confused. Anyway, my idea should work for both of you. Just let me know if you need further help.

Thanks for your comments. You must know that I’m trying to understand what sdfgeoff suggested as would be the solution for the problem mentioned earlier in this thread. As you can see in the blend I attached there are two cubes sharing the same script(s). This is to illustrate three ways to of running a class. In the first case you will see the traditional way of defining the class at an initialization point, but globally:

my_class = MyClass()

And here is the problem: both cubes will add a value of 1 to the same variable, defined as self.property.

So, what could be a possible solution? Instead of storing the class globally, store a duplicate of it into properties, one on the first cube, another on the second cube. And this works:

def main(cont):
    own = cont.owner
    if 'my_class' not in own:
        own['my_class'] = MyClass()
    own['my_class'].main()

But as mentioned by people who know so much more about this, it would be more conventional (better?) to go with another approach, and that’s with ‘sub-classing’, as was mentioned by sdfgeoff.

I’m not fully understanding it yet but as I see it now this would involve creating a new object, so that’s why you have to pass in the owner. So when you read:

def main(cont):
    <b>own = cont.owner</b>
    if 'init' not in own:
        new_own = MyClass()
        own['init'] = True
    <b>own = cont.owner</b>
    own.main()

It seems a bit confusing because you would think both owners would be the same object. Except this isn’t true on the first frame. Please, anyone, correct me if I’m wrong.

Is solving the problem of dragonspammer off-topic, or is it what you would call ‘hijacking’? But I agree, just to avoid any confusion, I had better started a thread on my own.

Edit: To moderation: you could maybe split this topic after Monster’s reply? Sorry, guys.

I meant it more sarcastically. The thread became more of answering the questions you were asking and less about what he was asking. If no one else cares I sure don’t. It just made it initially confusing for me because his description of the problem didn’t match up with the code you were posting.

More importantly I’m interested in solving problems. :slight_smile: Let me know if my above mentioned solution and opinions helped or not.

You’re right that the thread became more of answering my questions, and I apologize that I made it confusing because of this. As long as you know that I had only the intention to find a solution to the initial question of this topic. I do have some questions about what you said, because there’s some things I do not fully understand. But I’ll retreat for now as I don’t want to dominate this thread any further :smiley:

Perhaps the correct way to do this (or at least another way to do this) is to subclass the game object.

It’s pretty easy to do, and can make some things easier (eg you don’t have to store the class in a property, the object IS the class).

Quite simply, you co it like this:


import bge

class CustomGameObject(bge.types.KX_GameObject):
    def __init__(self, old_owner):
        '''Runs when you create/mutate the game object'''

    def update(self):
        '''some specific function you want to add to the game object'''

# Called first
def make_custom_game_object(cont):
    old_object = cont.owner
    mutated_object = CustomGameObject(cont.owner)

    # And you can check to make sure this all worked:
    assert(old_object is not mutated_object)
    assert(old_object.invalid)
    assert(mutated_object is cont.owner)

def use_custom_game_object(cont):
    cont.owner.update()

(Based heavily off the example on the blender API page)

Once the AI object is mutated, there is no way it can interfere with other objects because, well, it doesn’t know they exist, it doesn’t share any variables etc.

@ Monster: Thanks for splitting the thread. No I can dominate this one :smiley:

@ agoose77, sdfgeoff, jtsmith1287…: post 10 shows my attempt to use sub-classing in order to have local variables for multiple objects. Is this how it should be done? It works but I don’t only want it to work, I also want it to be conventional and clean.

Also is my assumption correct that for using multiple classes (in the same script) there’s no choice but to store the class into a property on the object (in this case the owner)?

Thanks.

If you are trying to use multiple classes, and you can temple mentioned behaviour through subclassing, then using composition is a good idea. You could use a class that handles this as well - a behavior class that handles component like classes

Sent from my Nexus 5 using Tapatalk

You’re post 10 should work, but I normally do things slightly differently when I have to call them once. Anyway, I’ve made a demo blend for subclassing:
subclassdemo.blend (498 KB)

Edit: I guess this is only good if you only have one class to work with, because you have only one object and that’s the owner. So with multiple classes it would seem to me that I’ve no choice but to chose the unconventional way and store every class into a property on the owner. Would that be a correct assumption? (I’m sorry I’m asking so much questions about this, I hope I will be done after this)

No-one minds answering questions! That’s why I visit these forums (I enjoy solving problems).

It all depends on what you are trying to store.
Let’s say that I create a class for ‘Person’ and is has a whole bunch of functions for health, eating etc.
Let’s say that I want to make a single instance of this person a player. The player IS a person, so I can subclass again (Remember, only if it’s an IS). I would subclass with a simple class that simply adds keyboard and mouse input to the player. Any other ‘Person’ object could be subclassed again with an AI class.
Note that when you’re doing multiple subclasses like this, the interface between them get’s quite important. In my hovercar racing game, each car had a function ‘activate’ which took the throttle and desired turn amount. Each frame, the AI (or Player) class fed values into this function, which then did all the motion.

The other case is if the class HAS something. Like an inventory. An inventory is not a person (a player is a person), so we can’t subclass it. But the person has an inventory. So if we create an inventory class for our person, correct practice would have us store it in the person class. Probably we’d generate it in the init function and store it in ‘self.inventory.’
The inventory itself would probably contain a dictionary of other classes, each representing objects owned. (each in-game object could be turned into it’s own class)
Once again looking at my hovercar racing game, each hovercar was held in the air by ‘magdrives’. These magdrives were game objects that had been mutated into magdrive objects. These were then stored in a list inside the hovercar object.

If this had been posted on the board two months ago, I wouldn’t have had a clue about this method. It’s only because I recently tried it that I actually understand what’s going on. That and doing a course on Object Oriented Programming (OOP) two sememsters ago.
If you don’t get what I’m meaning by ‘Is’ and ‘Has’ for deciding whether to subclass or to store, there are some great resources on OOP around the web that explain it better than I have.

Thank you both. So far I understand. Every object could be turned into it’s own class (=mutated) and even be stored in a mutated object. I didn’t think of that. But for every class you’ll need an object, so in some cases storing classes into a property on the object would be the only option, and Object Oriented Programming will help you to avoid this cases as much as possible.

But agoose77, is composition something else? Or are the component classes handled by the behavior class not mutated objects? Also, guys, I really appreciate the thorough explanations, but I realize that this kind of stuff is more about programming than the game engine maybe. So I do appreciate references to study notes on the web. I will have a look into OOP first and put all of this into an exercise.

Edit: @ sdfgeoff: thanks for the blend, it’s really helpful.
Edit2: It’s a real smart way of initialization, first time I see that you can actually change the controller’s script during the game!

Composition is a method of extending behaviour. Google it :slight_smile: essentially components can lens themselves to composition.
In all honesty it made much more sense to me to simply abstract the game object as an attribute and point properties to its properties, which can be useful for adding extra features, and avoids cluttering the attribute list with unnecessary data.

Sent from my Nexus 5 using Tapatalk