The utility of Class , objects and methods --- basicaly OO programming in BGE

I know that BGE uses already class and objects and that one use them (often ignoring it when no experience at all in Python or even programming … just writing the usual first lines of each python script)

But can someone explain more deeply the “how” and the “why” of the bonus of script programming with Class ? And how the object’s property in the BGE are somehow limited in their purpose.

I know for example that it’s cool to use class to affect some specific functions to objects created from that class ; so it’s possible to use the function on the object without having to explicitly refer to the object within the function

myobject.getVectTo(paramaters)

Well, my question is a bit fluffy - just wanting your opinion in general about the way of programming in BGE :stuck_out_tongue:

My concern is about spawning new characters in a scene (collsion box, armature, meshes). For the moment, the simpliest method seems to create duplicates of a character (so having different names) in the same scene. Well, that’s the way I do for the moment because i feel it’s hard to do it with " add.newObject " and have all the npc carrying the same name x)

If you’re a perfect programmer, there’s strictly nothing you can do in C++ that cannot be done in C.

C++ is simply an overlayer to C that can allow data nesting ( through encapsulation ) while beeing sure all resources ( structs & data ) propely allocated and initialized, allowing a proper and predictible execution of the classes methods.

OO languages are nice things when the classes user don’t wanna spend ages to see and understand what is inside, just like you can drive a car without knowing mechanics, electronics or thermodynamics.

Unfortunately and it’s where OO programming uses to be a pain, is that the ( mandatory ) documentation explaining how to use classes and what they do is often lacking infos, is unclear or simply don’t exist at all.

IMHO the blender python API is, according this, a pain…

Happy blending !

1 Like

yes, i see. For the moment, i’m just discovering a new world , the OO (though i had lessons of that at uni long time ago) . I will figure out to adapt all this to my game code.

If i can afford a simple advice, you could try starting with legions of small piece of code, cutting your app as much as possible, and do simple tests for simple things.

When you have a global sight of things and of what you want and can do with your knowledge, it’s much easier to put all together :wink:
It’s the way i do things with unity3D C# scripting.

Good luck !

and happy blending !

EDIT: oh and also find books about OO programming.
The bjarne stroustrup one is the bible but might be tough for starting.
The web is your friend for this :stuck_out_tongue:

1 Like

you want specific attributes and operations directly associated with certain objects. simple as that.
by extension, you want objects derived from those to inherit the base behaviour of their parent class.

take KX_LightObject for example. it is a subclass of game object, it can do most things a regular object can, and has some more functionality specific to lamps. advantage is: you didnt have to re-write inherited attributes and methods.

so the key word here is abstraction. the core idea is that objects derived from the same base class behave similarly, which makes writing systems that interact with these objects a lot simpler.

for a quick example, imagine you want every object in your scene to have a certain attribute, like an ID or key, and have them send data to a log of sorts. if all your objects are created from a base class you can define this basic behaviour just once and have each subclass inherit it. so it doesnt matter if its an armature or a mesh, they still call the same functions for this process. and if at any point you want to change something, you override in the subclass rather than rewrite the base class.

that said, purely object oriented is as stupid as it gets. that is why java is bullsheezels, everything having to be inside a class is completely idiotic. python doesnt suffer from this disease. you want to write code that gets the job done in the most efficient way possible, not code that conforms to the theoretical farts of a paradigm.

3 Likes

ahahah java or it’s script retard son… :joy:

Yes you are right @Liebranca ! for many reasons. The one i use to point out is performance.
OOP ( for Object Oriented Programming ) make many things simple and transparent !
For bypassing the encapsulation limit, you can use delegates, for globalization, statics etc… all those are ‘language addons’ but the counterpart of this is the ‘access time overhead’.

The deeper the class is ( in the object ) the longer the access to its methods is.
Hopefully our CPUs today are code-eaters but on an old 386SX25 C++ was at least 8 times slower than pure C.
It’s ease at the cost of performance.
And it’s nothing but a stack performance bottleneck

This is why i sometimes ( not always as i also like the OOP comfort ) flatten all ( game objects, strings, tables, etc ) and treat them in a row in a single handmade function.

I don’t know Python at all but i guess it can also do this, kicking perfs to the top.
What Python bothers me with is the lack explicit typing and also :joy: it’s indentation… but am quite old with bad habits; this might be the reason :stuck_out_tongue:

1 Like

to make it simple. I know that objects in the BGE belong to some BGE Class already defined

How could i create a sub-class for npc (so assing objects to that sub-class and inheritance of parent class) and give them more functions ?

npc.worldPosition
npc.myAIfunction
from bge.types import KX_GameObject
from bge.logic import getCurrentController
own = getCurrentController().owner

class BaseClass(KX_GameObject):
    def __init__(self, old_owner, *args, **kwargs):
          #from here on out, self is the game object.

class NPC(BaseClass):
    def __init__(self, old_owner, *args, **kwargs):
          #same as befoore.

own = NPC(own)

or you can check the clean way to do it here.
everything else is standard python. write methods, call them from the object.

throne has a lot of game object subclasses. its a bit messy, but if you need examples,

and the longer an array is the longer itll take to read. same for files. same for objects.
when i first started writing image formats, i used to pass the whole enchilada of bytes from function to function. still facepalming to this day. thank god for pointers and slicing.

i do like the c way to be honest. name the function after the object it operates on. type_method is informative enough. type.method is just convenient. but if one is orderly about their code its not so much of a problem nowadays.

3 Likes

This is almost machine code made understandable to human through C pointers…
Nothing is faster but i guess today they’re not tought anymore at school :confused:

Oop is a special type of container designed specifically for rpgs. The easiest way to understand it is to think of it like a character sheet. A character sheet has the following things.

–Attribute–
An attribute is a number or something that describes the character. Like dex or con.
–Method–
A method is something the the char can do. Like heavy attack or fireball.
–Property–
A property is an attribute that causes something to happen. Like dieing if your hp goes below 0.

All of this can be done using dictionarys, but classes are designed for this specific usage. Like most specialized tools, there harder to learn, but once you do, there easier to use.

I can, off the top of my head, think of two situations that oop gives a clear advantage.

If the thing has a position in space, real or virtual. A robotic arm would be an example of a real object that would be given a virtual class. A health potion would be a virtual example. Its my understanding that most video games are object orientated. I believe this is be cause they operate on actual virtual objects. I often think its use full to describe etheric entitys as objects, but this is somewhat more questionable.

If your language is subject-verb-object(linguists). Functional programing is vso, goto(you, store). Oop is svo, you.goto(store). English is svo, “You go to the store.” People can be very particular about word order. I think that the majority of people who program in oop do so prominently for this reason.

1 Like

Just read the RPG thing, but OOP solved a different kind of problem when it was first introduced:

What is the data, and what can you do with it?

Example: a vector.

In C, you would define a vector 2D as a struct like:

struct Vector2 {
    float x;
    float y;
};

So that’s the data: a “vector” is made up of “x” and “y”. But data alone is not useful, you may want to do things with this vector. Say you want to get the length of this vector, you would then write:

float vector_length(struct Vector2 *v) {
    return sqrt(v->x * v->x + v->y * v->y)
}

But you can see how the data and methods to operate on it are “disconnected”.

In Python with OOP, you can create a “class” which essentially is data + functions to act upon said data.

class Vector2:

    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def length(self):
        return sqrt(self.x * self.x + self.y * self.y)

# usage:
v = Vector2(3, 4)
v.length() # returns 5
v.x = 0
v.length() # returns 4

Then there’s the concept of inheritance, like how 2D vectors have things in common with 3D vectors:

# in Python you use parenthesis to express inheritance:
class Vector3(Vector2):

    def __init__(self, x=0, y=0, z=0):
        super().__init__(x, y) # delegate work to Vector2, it will set x and y
        self.z = z

    def length(self):
        return cubic_root(self.x * self.x + self.y * self.y + self.z * self.z)

# usage:
v = Vector3(3, 4, 0)
v.length() # returns 5
v.x = 0
v.length() # returns 4
v.z = 3
v.length() # returns 5

Vectors are a bad OOP example though, let’s try something else.

Let’s take an RPG example:

class NPC:

    def __init__(self, max_life, name):
        '''
        Data on a NPC: "max_life", "life", and "name"
        '''
        self.max_life = max_life
        self.life = max_life
        self.name = name

    def deal_damage(self, damage):
        '''
        Removes "damage" from current "life". Make sure we dont go below 0.
        '''
        self.life = max(0, self.life - damage)

NPCs will track their “max_life” as well as their current “life”. We will also give them a name, but we won’t use this data in this example.

class Villager(NPC):
    '''
    Villagers are NPC.
    Villagers with the role "priest" take twice as much damage as other NPCs!
    '''

    def __init__(self, name, role):
        '''
        All villagers have a "max_life" of 100.
        Villagers can have a specific role, which is not used at the moment.
        '''
        super().__init__(100, name)
        self.role = role

    def deal_damage(self, damage):
        # detect this instance is a priest:
        if self.role == 'priest':
            damage = damage * 2
        # the NPC class already knows how to apply damage, let's use that:
        super().deal_damage(damage)

Here you can see how inheritance helps us. Since the NPC class knows how to handle max_life, life and damages, we can then focus on specialties for villagers.

In this case, we add a role property to our data that defines NPCs, and we apply double damage based on the value stored in “role”.

Even better, all code that expects an instance of “NPC” should be able to handle instances of “Villager”, since villagers will share functions with NPCs.

npc1 = NPC(max_life=100, 'marc')
npc2 = Villager('henry', 'priest')

# same calls, but different instances:
npc1.deal_damage(10)
print(npc1.life) # 90
npc2.deal_damage(10)
print(npc2.life) # 80

OOP is all about data management. But you should also use it when different type of data must go through the same workflow. Example:

class Action:
    def run(self):
        raise NotImplementedError('subclasses must define this')

Let “Action” be our interface: something that has a “run” method.

class Attack(Action):

     def __init__(self, damage):
          self.damage = damage

     def run(self):
          print(self.damage)

The “Attack” class is the useful thing here: it actually defines how its internal data should look like, and implements what “run” should do. You could create a variety of actions, and as long as all actions have a “run” method like this, you will be able to use them all in the same way:

for action in actions:
    action.run() # don't care what action is, as long as it is a subclass of "Action".

Although, interfaces are more important for statically typed languages. In Python, you should practice “duck typing”: even if instances don’t share a common class, as long as they have the same methods then its ok, it will work!

class Duck:
    def quack(self):
        print('quack quack')

class Bird:
    def quack(self):
         print('cui cui')

for thing in [Duck(), Bird()]:
    thing.quack() # works, the bird knows how to quack!

Hope this helps. If not, just play around with it.

3 Likes

Worth repeating. Not that I have any pretense of knowledge about these things… Memory allocation and management seem like secondary concerns when using OOP, but hit back when trying things “live”.

1 Like

They are secondary, but not forgotten. Code re-use, readability, maintainability, likelyhood of bugs, are very important. Something being 80% quicker at the expensive of it being 30x longer code and hard to maintain is rarely a good trade-off.
Like you said, computers these days are powerful. That small performance hit means nothing most of the time.

Same goes for file sizes. In the days of N64 games, an extra half megabyte of space is a big deal! These days, developers happily push out 20GB patches.

Optimise when you need to optimise. Premature optimising can kill a project.

lots of memory doesnt mean be lazy about it. you can spare a few extra bytes in exchange for not writing a few hundred lines, but the little things add up. you dont think much about it until you start to notice you might run out of space if you keep it up.

garbage collection is a tricky one for a lot of people. not because its hard. the idea is easy enough to understand: free what you dont need. but theyve just never had to worry about it, and then all those big ole leaks become a serious problem.

generally, people ive studied and worked with go with “get it done first, optimize later” and i usually ruin the fun by having them plan ahead. it makes people hate your guts but its still better to begin coding with at least some idea of how expensive to run the thing is. helps prevent hitting that roadblock later on when youre all messed up, sleepless and definitively not up for major rewrites.

1 Like

For most of what I saw, the small python modules for the game engine here would not inflate into 30x the amount of code if done in C/C++, but then again, I’m only 3 days old into python… :wink:

I’m trying to get my head wrapped around the Tkinter library “included” in python 3.8 to make a small application to browse through long files and play with data. It’s obvious that I’m dealing almost exclusively with classes from which I simply make new objects, but then when a simple button will not do what I need I have a problem… Well that’s only because of my poor knowledge of all the pre-cooked C functions that make the foundation of the python language ; I just have to read for eons until I find the pre-built way to do what I want cooked into an object. But I found even “better” than this :

The last paragraph of this explanation : “Composition provides a superior way to manage delegation since it can selectively delegate the access, even mask some attributes or methods, while inheritance cannot. In Python you also avoid the memory problems that might arise when you put many objects inside another; Python handles everything through its reference, i.e. through a pointer to the memory position of the thing, so the size of an attribute is constant and very limited.

By the way this “composition” method has nothing to do (or so little) to what “composition” is in C++, because it’s simply a “shortcut” to copying whole parent class and making a new custom instance of it in a script, in a manner of speech, if I understand well. And honestly I don’t want to dive deeper to try to understand the behind-the-scenes process : it’s available, great.

But quite honestly, why not simply create a new class from scratch that will access the C code itself, instead of going through a whole hierarchy ? And to that point, having only dipped my big left toe in the python pool, this makes me wonder if I’m not wasting a lot of time that I could instead invest into refining my C/C++ skills, and try yet “another” library and another set of “objects” to interact with…

The problem almost always boils down to how good the API is made and documented. I would not say that tkinter is easy for a beginner, but it does the work once you get familiar with it. Brace for that.

It’s the same concept, really.

Wrong question. The good question is: Why would you use Python over C?

Python’s pros:

  • Easier to write: simple syntax, easy types, garbage collection, yada yada…
  • Easier to read: this means easier to maintain too.
  • Easier to plug: no ABI management, program format is textual format, all you need is a pre-compiled VM for your system and you are done, cross-platform by default.

Python’s cons:

  • Might be slow at times, but you should not be using this for speed anyway.
  • Static typing possible but is a somewhat advanced topic.

You can write Python native modules in C/C++ to speed up some functions of your program, this way you can tickle as many low-level constructs as you wish. But then you need to have fun compiling your shared library for all platforms if you want to distribute it. That and the C/C++ hassle.

What do you want to do? This question should drive the technology you are going to use. Python is by far easier to learn/use if you find the right libraries. Objects are an implementation detail. Although knowing how to make things happens in C/C++ is an important skill for any developer in my opinion. Just have fun.

1 Like

Here is a basic WASD trick I made to test output to the windows console (a console game!! ) in which the interpreter will run the python code. The size of the console is set in the first line, so it will fit the width of the arrays that make the “playground”:

import os
import msvcrt
import random
from time import sleep

os.system('mode con: cols=25 lines=24')

x=10
y=10

game1 = [['-------------------'],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['                   '],
['-------------------'],]

player = [['-------------------'],
['o                  '],
[' o                 '],
['  o                '],
['   o               '],
['    o              '],
['     o             '],
['      o            '],
['       o           '],
['        o          '],
['         o         '],
['          o        '],
['           o       '],
['            o      '],
['             o     '],
['              o    '],
['               o   '],
['                o  '],
['                 o '],
['                  o'],
['-------------------']]

print(game1)  # To see greetings & instructions ?

while True:
	
# Autonomous block
	
#	if (x > 3) or (x < 17):
#		x = x + random.randint(-1,1)	
#	if (y > 3) or (y < 17):
#		y = y + random.randint(-1,1)

# WASD block
	
	key = msvcrt.getwch()
	if key == 'w':
		if y > 1:
			y=y-1
	elif key == 's':
		if y < 19:
			y=y+1
	elif key == 'a':
		if x > 1:
			x=x-1
	elif key == 'd':
		if x < 19:
			x=x+1

# Update block

	game2 = game1.copy()
	game2[y] = player[x]
	print(game2)
#	sleep(0.1)
		

The commented - out lines are for the “autonomous” motion of the ball, driven by a randomizer function. To switch from WASD to “random move” just comment out the WASD block and un-comment the “autonomous” block. Also enable the last line (sleep) to slow the thing down a bit…).

When using the WASD part to controll movement, the “o” stays inside the bounds no problem, when it hits one it just stays there until another direction is chosen.

But when using the “autonomous” part, the “o” will eventually move close to a boundary, cross it a few times and eventually crash the loop by going “out of range”, even if I make a 2- wide safety “buffer” to make sure the “o” will not get too close to boundaries.

Any ideas why it crosses boundaries anyway ?

EDIT : I solved it this way, by changing the order of the condition check:

	x = x + random.randint(-1,1)
	if (x < 2) or (x > 18):
		continue
	
	y = y + random.randint(-1,1)
	if (y < 2) or (y > 18):
		continue

But still, can you explain why the “if” statements were not simply “skipped” if x or y were out of range ?

By the way, it’s a bit hard to edit posts on this forum : I almost never see my cursor or selection when I set it somewhere with the mouse… and no colors !

if (x > 3) or (x < 17): I guess you meant that you wanted x to be between 3 and 17. But given your current boolean expression, let’s take x=1 then we have (1 > 3) or (1 < 17) which evaluates to (false) or (true) which computes to true.

Python allows you to write if 3 < x < 17: or if (x > 3) and (x < 17):.

If you try “(x > 3) and (x < 17)” you will see that the “ball” stops moving when reaching any one of the sides and will stay there forever.

I will try the 3 < x < 17. Thanks!

Edit : I see you are typing, sorry. I read again and got it ( for the "or> allowing to update x and y).

OK :slight_smile:

But note that using (x > 3) and (x < 17) or 3 < x < 17 will cause the ball to stop moving indeed. This means you need to handle the allowed movement in a different way, because the current logic is faulty.

1 Like