Custom Python Classes.

Abstract base classes are used to specify an interface, without actually implementing it. This is useful in larger-scope projects where the designer requires all abstract methods to be implemented to make a class a valid subclass - ABCs cause a failure at class definition if these aren’t met. And more.

That is actually really helpful, but I have a question,
If you change some attributes defined in a class in one script,

eg.

import bge
import Classes
        
def main():
    own = bge.logic.getCurrentController().owner
    own = Classes.Enemy(own)
    own.health = 20
    print(own.health)
main()

and inside the “Classes” module you have this:

class Enemy:
    def __init__(self, obj):
        self.name = obj.name
        self.health = 100

How would you make it so that these changes happen throughout?
So if you access the same health through a different script on another controller it returns 20 not 100.

So what you can do is this…
In your class object you can change the enemy class to accept more than one attribute, then have a trigger that would over write the class with the new data


class Enemy:
    def __init__(self, obj, health):
        self.name = obj.name
        self.health = health



#~~~~~~~~~~~~~#
#        latter on        #
#~~~~~~~~~~~~~#


# This happens when you trigger it by anything.... could be by a message sent globally 
#or the script watching the player's health...
#or you could be listening for a message for only one object and if it hears it, it runs... 
if trigger:
    own["stored_enemy_class"] = Enemy(own,100)

You need to store the class in a property, not in own:

import bge
import Classes
        
def main():
    own = bge.logic.getCurrentController().owner
    own['name'] = "troll"
    own['enemy'] = Classes.Enemy(own)
    own['enemy'].health = 20
    print(own['enemy'].health)
main()

If your class is like this:

class Enemy(object):
    def __init__(self, obj):
        self.game_object= obj
        self.name = obj.name
        self.health = 100

Then your class is created when your write

own['enemy'] = Classes.Enemy(own)

At that point the enemy you have created is stored in own[‘enemy’] and has 100 health.

When you write

own['enemy'].health = 20

You change the enemy’s health to 20.

if in another script I find that game object I can access the enemy health:

troll = [ob for ob in own.scene.objects if ob['name'] == "troll"][0]
print (troll.enemy.health)

As you can see, a better name for the enemy class would be stats or something.

The problem is that this enemy only lives in that game object.
Imagine I want to see my wife, so I take a picture of her (a reference).
I’ve got a wallet and I put the picture of my wife in that wallet. If I go get another wallet I won’t find the picture of my wife in it. I need to put the picture in every wallet I own, or go get that one wallet with the picture very time. Or I can put the picture online then I can check it using my phone or computer or whatever.

If you want your enemies to be accessed from anywhere and not be linked to game objects you need a place to store all your enemies, so you can get to them any time.
I usually have a controller object in my scene which creates and keeps all the agents in the game in a list. Each turn it goes through the list and updates them. If an enemy dies it deletes them, and it can add new enemies as needed.

This is kind of duplicating what the BGE core does already though, so you might not want to take this road. In your example the Enemy class doesn’t do anything special which own can’t do. In that case you’d be better off having own as the enemy and making health and other things as properties of own.

In my method I only have one central script, not one per enemy. Everything is controlled from the controller object. I like this method because I can pause the updating so the agents are all paused. If an agent is paused it is not using any CPU resources at all. I can selectively pause agents, for example if they are out of view and so save on processing time.

But the key thing is that each agent is an autonomous object, an instance of a class. The controller is only running update on them or not. It doesn’t control how they act or try to micro manage them. It’s not some kind of god controller, just a structure for keeping things centralized.

I like the idea of a centralized controller but I am sorry I am having trouble grasping the concept.

So, in my controller object I could have a function, say “patrol(agent)”, that moves an enemy around waypoints. I could create a list of all the agents in my scene and then iterate through them putting them in the patrol() function one by one, is that what you’re getting at? And all of this is happening in that one controller object.

But would this mean that individual agents don’t have any logic applied to them at all?

And where would classes come in all this?

I am sorry but I have always used functions on individual enemy objects and just came across classes so don’t really know where they could be used.

EDIT: I found a method in which I could create classes and methods in a module and import them into the main script and call the methods depending on different factors like health and other inputs.

So, if I collide with something I could do,

enemy1.die()

or if I spot the player (through a ray sensor),

enemy1.set_AI_mode(chase)

and if the player is lost I could reset it and do,

enemy1.reset()
enemy1.set_AI_mode(patrol)

the reset would just deactivate all the actuators.

This is just an example though, but I don’t see how it would be different from just using functions imported from a module.

More like you could select a bunch of agents and then click a button on the UI and it sends the “PATROL” message to all those agents. Having a central controller makes sending messages very easy. But thats kind of available in Blender already.
Setting up your own classes is useful when you want to encapsulate a lot of complex behaviour in a single object. An example would be the door class I just added to my project. Doors can have a lot of complicated behaviour, like being locked, opening, closing blocking vision etc… and they might have several parts. The door frame. The door parts. A button or keyhole. Rather than have a lot of different objects you can fit them all together and give them their own methods. Then you only have to specify that a door exists at a certain place and they create themselves, setting up all the game objects and things all as soon as they are added.

Thanks again for this nice thread Smoking_mirror. I often search in the forum for theards that help me to improve my code.

But I have a question to your explainations. How do you manage the paused state of your state machine? Do you run your gameloop in a second scene? (Just out of interest)

The disadvantage of an extra scene for the gameloop is that you always have to make sure that your logic work in the right scene but thats not a problem so I would prefare it.

Work only in one scene and stop the logic don’t stop the other parts of the scene like the physics. You can set the gravity to 0 but it is not a fancy way in my opinion.

Well, my main object has the game loop and it runs an update on all the child objects:

if not self.paused:
    for agent in self.agents:
       agent.update()
    for particle in self.particles:
        particle.update()
else:
    for agent in self.agents:
       agent.pause()
    for particle in self.particles:
        particle.pause()

self.camera.update()

This way I can choose what to pause, so the camera may remain active while all the particles and agents are paused.

I needed to write some code (the pause method) to prep agents and other things for being paused, since some things in blender are handled not by me but by blender, like animation or the like. It is necessary to stop the animation, then get the correct frame and restart when un-paused.

As a side issue:
When child objects are created they automatically append themselves to the right list, so I don’t do something stupid like for get to do it or do it twice:

class Agent(object):
    def __init__(self, manager):
        self.manager = manager
        self.manager.agents.append(self)
class Player(Agent):
    def __init__(self, manager):
        super().__init__(manager)
class Enemy(Agent):
    def __init__(self, manager):
        super().__init__(manager)
        self.player = self.manager.player
class GameLoop(object):
    def __init__(self):

        self.player = Player(self)

        for i in range(3):
             Enemy(self)

In this case the player is added to the agents list and is also stored as a reference on the game loop. Enemies are simply added to the agents list since I don’t need a direct reference to them. But they pick up the reference from the manager so they can find the player easily.