This is something I’ve been wrestling with for all of the time that I’ve used the BGE.
As soon as I started to learn python I ran in to the idea of classes and wondered what they are and why I should use them.
I’m not a professional programmer, this is just my hobby, so I wanted more than just “It’s the way to do things”.
It wasn’t clear to me (and remains a mystery to some other BGE developers I know) why it’s better to use a class than another structure. So here’s an explanation of why classes are useful and why you should definitely use them if you’re not already:
***1. You are already using classes!
The KX_GameObject is an class/object. When ever you add a object in game you are adding a new instance of that class.
You can subclass it to add more functionality if you want. When you use:
own.worldPosition = [1.0,0.0,0.0]
You are using a class method.
Ditto:
own.rayCast(other)
When you set a property on an object that is similar to the way you set attributes on a class (but not exactly the same, it was changed once upon a time).
Wouldn’t it be great to make thins that behave like a gameobject? Imagine if you have a player and you can do:
player.die()
Tying a function to a class (making it a class method) makes your code much simpler and easy to understand than having all your functions floating around in your code. (and it doesn’t make them less reusable as I’ll show later).
***2. Classes can be Complex!
At first I made very simple classes:
class Player(object):
def __init__(self):
self.health = 4
self.ammo = 100
self.dead = False
And I wondered why use that if I could use a dictionary instead:
player = {"health":4,
"ammo":100,
"dead":False}
The answer is that there’s no real reason to use a class instead of a dictionary or list or something else if all you’re doing is storing some data.
On the other hand if you want that player to do something, like pick up keyboard input or have an AI or move, jump, do animations etc… it’s time to use a class. Of course you could use a gameobject as your class and attach a script to it.
def player(cont):
own = cont.owner
if "ini" not in own:
own['health'] = 4
own['ammo'] = 100
own['dead'] = False
own['ini']= True
forward = cont.sensors['forward']
if forward.positive:
own.worldPosition.y += 1.0
But sooner or later you’re going to run in to limitations with that. One problem is that any in-game object is rarely a single object. You usually have a collision box, skeleton, skin, other components etc… Maybe it makes sense for the collision box to be the “main” object, but it can be cleaner and more organized to contain all those objects in a custom python object. The other problem with these “hybrid” objects is they can be very messy. That’s because a large part exists as a collection of objects and logic bricks in Blender, while the other part exists as some code in a script. If you want to change something about it you have to keep both parts in mind so that things don’t get broken.
Honestly, almost everything you can do with logic bricks you can do with python with a better degree of control. Isn’t it more flexible to keep everything in once place? My Agent object in Blender consists of a collision box with an armature parented to it and a skin parented to that. That means with a skin change and a code change (which can be done on the fly) I can use that agent to be a player or an ally or an enemy.
I can also set up the agent as I create it.
enemy_1= agents.Enemy("player_object",health=50,location=[12,56,128], gun="AK47",AI_mode="Patrol")
I don’t have to add a player to the game, then move it to where I want it to be and mess around setting it up.
This is very useful when saving or loading a game.
***3. Use templates to reduce the amount of code in your project.
I can also set up variations of the basic agent by subclassing it. A subclass is a version of the main object with some changes. So if I have a base agent:
class Agent(object):
def __init__(self,scene):
self.scene= scene
self.add_game_object()
self.skeleton = self.game_object.children[0]
self.dead = False
self.mode = "patrol"
self.animation_type = "agent"
def add_game_object(self):
self.game_object = self.scene.addObject("agent_object")
def set_AI_mode(self):
do_AI(self.mode)
def animations(self):
self.skeleton.playAction(self.animation_type,0,20)
...
Then I can set up all the code for it at one time. The I make a version of it with slight differences:
class Monster(Agent):
def __init__(self,scene):
super().__init__(scene)
self.mode = "attack"
self.animation_type = "monster"
def add_game_object(self):
self.game_object = self.scene.addObject("monster_object")
Now my monster will work just as I want it to with only a few lines of code. That’s surely better than copying and pasting all the code from my agent script and rewriting it for a monster script. What if something doesn’t work in the agent and I don’t notice until later? Then I have to go back and rewrite all my agents, trying to remember how monsters are different from ghosts.