How to access to an object in an object? (child and parent)

Hello,

I use bge.logic.getCurrentScene().addObject() to add an object in my scene. It works fine. The object that is added is an armature that contains also other object as the child in the outliner. How to acces to those objects? I need to control the shape key actions on one mesh as well as the actions on the added “parent” armature.

For example:


current_scene = bge.logic.getCurrentScene()
current_controller = bge.logic.getCurrentController()
character_armature = current_scene.addObject("character_armature", current_controller.owner, 0)

–> I can play action by character_armature.playAction(“my_anim”, 0, 15)
How to do the same on the other object inside “character_armature”?

Thank you!!

KX_GameObject.parent
KX_GameObject.children

I suggest the armature controls the animation by itself, rather than the adding object. It knows best how to do that and the adding object will be happy to care its job as factory only.

Thanks, that’s perfect, I didn’t know what to search.

Do you mean that I should give a python script/logic brick to each object I need to control?
I found it easier to have a global script to control everything. It’s not correct to do this way? I’m new to game development, so I’m still looking for how to do lot of stuffs.

In this example, my goal is to have a 2 players game with 2 character based on the same model. So, I load my .blend file character with libLoad, then I add it two times in the global script and change the shape keys and armature depending of what player does.
Is that a wrong ? Should I divide the script into all my objects? I’m afraid to be quickly lost.

Thank you for your advices!

It’s still time to make changes. I will do my best and see what I can do better.
Thanks again.

Hey Monster,

I was thinking of your suggestion and I’d like to know something. You spoke about the OOP approch for the object instance. That’s indeed exacly what I was trying to do with my 2 players character. Actually, I already have several classes like “Character” and “Player” with methods like “move to the left”, “jump”, etc. I try to use OOP approch more and more since my last projects so I become a little bit more familiarised with that, but not really directly with my 3D objects / logic brick.

For the moment, when I create my characters in the scene, I use addObject for each character and store them into variables. Then I can create my two character objects associate to my Character class and tell to each object to use one of the armature via the variable where my 3d object are stored.
It works but your message and your links make my think I should not add my object and use them with the Character class in the same script/area. I should instead just add it with addObject and that’s all for this script. Just let them behave like they have to depending of the logic linked to them once they are created. Am I right? It seems more clear to me. I’ve realised that it should be really easier to setup and debug.

But I don’t know how to do. I mean, how to, in the BGE, create an instance of an object with a specific values (size, speed, player 1 or 2 that is manage by the class). Like an object should be created in OOP programming. Something like char1 = Character(player = 2, speed = 10) # he is the player2 with a speed of 10.
For my example, I have a .blend file that contains a mesh which is deformed by an armature. I libLoad the file and add it the the scene with addObject. How to tell to this object that he is the player two with a speed of 10? and use those values in the script attach to it that it is a player 2 with speed of 10?

Sorry if I’m hard to understand, English is not my mother tongue.

store variable that are different in a property,


own.applyForce(0,9.8*own['MoveSpeed'],0),1)

if own['Player']=1:
   do player 1 stuff

if own.localLinearVelocity.y>own['MaxSpeed']:
    own.localLinearVelocity.y*=.9

Of course! as simple as that :slight_smile:
Thanks!

This is typically part of the creation process. So your “builder” does not only call addObject(), but continues to configure the newly created object. Another way is, that the object configures itself after creation. And a third way is a configuration process does that.

According to the above mentioned principles, the operation of configuring is separate from the “daily work” of the object.

For simplification, I suggest to use the Builder as BluePrintRandom demonstrates. AddObject() sets up configuration already (pos, rot, scale) there is no problem to do more. If you have a closer look you see that this encapsulates the operation of creating the object and configuring the object into an higher level object building operation.

While performing the configuration, you can establish the relationship between your python objects, 3D objects representing the behavior of the characters with the 3D objects representing the visible parts of the character.



...
   def assemble(self, root):
      parts = self.parts
      parts.physics = root
      parts.skeleton = root.children["Armature"]
      parts.leftHand = parts.skeleton.get("Hand.hook.L")
...

Very simple example demonstrating a way to register game objects at an Python object. I hope you notice it does not mention, if the Character defines the assembling operation or another object does that. It is up to you.


def setCharacter(gameObject, character):
   gameObject["character"] = character

...
character = Character()
setCharacter(owner, character)

Very simple example to register a character object (python object) at the 3D object.

I was thinking about if that should be part of the character class or not. Let the character register the game objects by itself seams very natural. But there is a drawback.
When you register an object, you want to grab it at a later stage. These two operations semantically belong together (like save and load). But it makes no sense to let the object grab itself, as you do not know it at that stage. So you would need to place both operations at different entities. I prefer them at the same entity. When I know this entity I can set and get the character object which belongs to a specific game object.


def getCharacter(gameObject):
   return gameObject["character"]

The advantages are:

  • one place to define the keys e.g. “character”
  • the refered object is independent from the registering operations

(My entity is typically a module, but you can use any object you like, as long as you know ho to get it beforehand. You can even use the class itself to perform getting and setting via class methods. )

Thank you!
I’m trying to play with that and to convert what I did with this but something doesn’t work.

Here is a simplified script attached to my character object that is connected via the logic editor with a Python controller in module mode with “character_play.main”


import bge
import Player
    
# get the controller
controller = bge.logic.getCurrentController()
owner = controller.owner

player = Player.Player(owner["player"])

print ("---", owner["player"])


def main():
    
    player.update()
    
    if player.right_active == True:
        owner.applyMovement((0.25, 0, 0), False)
        
    if player.left_active == True:
        owner.applyMovement((-0.25, 0, 0), False)
        

I add my first character in my builder script like that :


# set character1 on an active layer
character_armature1 = bge.logic.getCurrentScene().addObject("character_armature", bge.logic.getCurrentController().owner, 0)

# set character1 game properties
character_armature1["player"] = 1

It’s working well as espected. It prints the player number in the terminal and I can move the character of the player 1 to the left and to the right.
But when I add my second character:
character_armature2 = bge.logic.getCurrentScene().addObject(“character_armature”, bge.logic.getCurrentController().owner, 0)
character_armature2[“player”] = 2
It prints nothing and I can’t use this character.

What did I wrong? Can I initialise variables and classes like that? (outside the main() function that is called by the python module logic brick). I thought this part of the code will be called each time this object on the scene.

bge.logic.getCurrentController().owner

may need to be defined outside the command?

own= bge.logic.getCurrentController().owner

What exactly is an object of class Player supposed to do?
With the above code it looks like player collects the input from the user. The module character_play performs the changes to the game object.

What is the relation between Player and the game objects executing “character_play”?

Why do you need this number at the created game objects?
I assume you want to use it as sort of “name” to differentiate them when displaying something.

Remarks
[I really do not like the term “main” it describes nothing. It is in that way strange as you describe the operation in the file name already character_play -> why not call it character.play?]

Are you using module mode or script mode?

From your last statement I assume you use module mode. The following explanation belongs to module mode only:

Please be aware, that it is usually not a good idea to store references to controllers, or game objects in module variables

If you do it that way, you can use this code ONLY at one object at exactly one object. You can’t even reload the scene without breaking this code.
The module variable “controller” will be the controller that loads the module the first time. This might be fine if you guaranty that ONLY this one controller will ever access this variable at any subsequent execution.

—> you bind the module to this controller.

If you reload the scene, the object will be deleted (as the old scene gets deleted) and a new one with the same name gets created during load. The module variable controller still refers to the (in the meantime deleted) controller. No, it will not automatically refer the the new object. To get that you need to call getCurrentController().

The same belongs to “owner”. Because several controller can reference the same game object, this can be fine when use at another controller of the same game object. But this is more or less an accident, “controller” is still incorrect, other objects can’t use it and reload will result in problems.

“player” might be different. This depends what you want to do. In you current version of the code, you create exactly one Player object. This might be fine, I can’t tell, without more details what this object is good for.

You store the object reference at the module, this creates a 1:1 relation to the module. This is a good idea if there is no relation to a single game object (e.g. one player per character). If you have such an relation it is better to keep the reference to the Python object at the game object itself (as mentioned in post #9).

Player is a class that reads a ini file by configparser with the inputs settings of the user. Keyboard or joystick and which button does what.
For example , if I create a player object:
player1 = Player(1) # it will create a player object that has all the inputs of the buttons for the player 1 from the ini file. I can then, read on each frame if keys are pressed.
player1.left_active # returns if the left button is pressed or not
player1.right_justActive # for a tap mode

I can then create a second object to use with my player2.
player2 = Player(2)
if (player2.left_active == true):
…object.applyMovement(…)

It was working fine when I execute this code “twice”. Once for the player 1, and a second time for the player 2. Now I tried to have just one object with the code i’ve shown you in my precious post. I thought that with player = Player(— game property with the player number —), it could do the same but it doesn’t on my tests. Probably because what is in the script before my loop that is called in the python module logic is executed once even if my object is created twice (?). I don’t really understand why. If a code is attached to an object, when I add it twice on the scene, why the code is executed just once?

Yes, I’m using the module like I’ve just said. What is the difference? It seemed easier to me to initialised my variables and objects from classes but apparently it isn’t correct. Why? Should I replace it by the script mode initialise in the “loop”? Or maybe I’m wrong and that’s not why my second player doesn’t work but if it’s something else, I’m a bit lost on what I should do.

I don’t understand either why the script I put outside the main() loop is “dangerous”.

Should I just use them without storing in a variable or did you mean something else?

Thank you again for the time you spend to answer me!! :slight_smile:

Let me explain (hopefully with understandable words):

Script mode:

A script gets executed at each call starting with the first statement at the top.
All code will be executed unless you explicitly implement alternatives (if, for, while …).
The local (script) context lives for the call only. When the execution finishes -at the end of the script- all variables are removed. Objects can survive when they are referenced by objects outside of the script (e.g. a property of a KX_GameObject, an attribute of a module).

The effect is, you need to create all variables at the next call again. This is not that bad as it sounds. Think about a script as stateless code.

Module mode
A module gets executed at the first call starting with the first statement at the top.
After that the called function will be executed from top to bottom but only that function.

Any subsequent call will execute the function only.

In difference to a script a module can store variables. You do that just by defining them at the module level (indentation zero).
These module variable remain as long as the module remains loaded. Think about a module as stateful code.

You can change module variables. But you need to do that explicitly. If you do not change them they will keep the last set value.

Dangerous code

Why is this code dangerous in module mode but fine in script mode?


import bge 
SCRIPT_MODE = 0

controller = bge.logic.getCurrentController()

def printController():
    print()
    print("Is module variable up-to-date?", 
           controller == bge.logic.getCurrentController())
    print()
    print("   module variable:", id(controller), controller)
    print("current controller:", id(bge.logic.getCurrentController()), bge.logic.getCurrentController())
    print()

    
if controller.mode == SCRIPT_MODE:
    printController()

  • Because script mode is stateless. The local variable “controller” gets updated from context (bge.logic) at each execution. It always refers the calling controller.
  • Because module mode is stateful. The module variable “controller” gets updated at the first call only. This means “controller” will refer to the first controller calling any function in this module. This is not necessarily the calling controller.

Examples:
called the above code from two module controllers and one script controller:


Blender Game Engine Started

Is module variable up-to-date? True

   module variable: 225527520 PythonModuleCallA
current controller: 225527520 PythonModuleCallA


Is module variable up-to-date? False

   module variable: 225527520 PythonModuleCallA
current controller: 225527808 PythonModuleCallB


Is module variable up-to-date? True

   module variable: 225527184 PythonScript
current controller: 225527184 PythonScript

Blender Game Engine Finished

How to avoid that?
A) grab the controller from context. There is no need to store it in a module variable.
B) update the module variable before accessing it.
C) you guaranty that only one (and just that one) controller is calling this module
D) use script mode

Called the above code before and after a scene reload (module mode):


Blender Game Engine Started

Is module variable up-to-date? True

   module variable: 225528648 Python
current controller: 225528648 Python

---- Reloading scene ----

Is module variable up-to-date? False

Python script error - object 'Empty', controller 'Python':
Traceback (most recent call last):
  File "...\demo.py", line 10, in printController
SystemError: Blender Game Engine data has been freed, cannot use this python variable
Blender Game Engine Finished

This example shows a much more obvious error. Unfortunately it is not that easy to identify, because you might think you have a single object with just one controller. But a scene restart does not cause a module reload.

How to avoid that?
A) grab the controller from context. There is no need to store it in a module variable.
B) update the module variable before accessing it.
C) you guaranty that only one (and just that one) controller is calling this module
D) use script mode
E) reload the module after scene reload

This belongs to the current controller. There are other objects that result in a similar situation:

  • current game object
  • current scene
  • sensors
  • controllers

Still there are situations when it is useful e.g. referring to an inventory object, referring to a current selection.

Attention: The same situation applies when you store the above objects in an object of an own class and this object is stored in a module variable.

I forgot to mention the solution to your problem. I just mentioned what to avoid ;).


import bge
import Player
    

def play():
    controller = bge.logic.getCurrentController()
    owner = controller.owner
    playerNumber = owner["player"]
    player = Player.getPlayer(playerNumber) # assuming this returns an according player 

    print ("---", owner["player"])

    ...

Thank you a lot. It seems quite clear to me now and will try out that with my current game project :slight_smile:

Yes indeed it works!
But is it correct to create a new Player object on each frames? Isn’t it too heavy?

Can I do something like that on the first iteration:
if owner[“set”] == False
— creation of my variables/objects
— owner[“set”] = True
And not repeat that creation of variables (which will still be there because it’s in module mode if I understood well) on each frames to optimise my code or I don’t need to do that because it’s not a problem?
I’ve just tried it but it doesn’t seems to keep my player variable after the first iteration (“referenced before assignment”)

Yes it is common practice to cache such values. You need to think about whee to keep this cache.

A typical place would be a property of a game object or a container in a module.

The common technique is:

Boss:Cache get object!
Cache: when i know the object i can refer to it, otherwise i search for it (or i create one) remember where it is and refer to this one.

In code


def getPlayer(key):
   If key in players:
       return players[key]

   player = CreatePlayer()
   players[key]
   return player

Just a sample, that you see what I mean. You can implement it as you like.
Btw. With the above example you can store the cache “players” at the module level. GetPlayers() manipulates the content of the variable when needed. But you better ensure that you can remove objects that are not valid anymore ;).

I didn’t know that game properties could handle that but that’s just fine and works well.
Very nice advices. I have all I need to continue what I’d like to do, thanks!! :D:cool: