python modules only work on one new object

hello. When i add objects to my game whose behavior is defined by a python module controller the behavior is only applied to the first type of object. for instance if I have a module that tells a bullet to move, only the first bullet added to the scene will move all other bullets added to the scene will not adopt this behavior. The bulled object has the behavior module connected to it on an inactive layer. How to I make modules affect all new added objects? thank you.

You need to be aware how the Python module works.

It is imported when it’s first used. This means that thereafter it won’t be imported once more (this is the speed benefit). Therefore, you’re probably defining module-level references to the controller or object of the bullet. Check this.



import bge

cont = bge.logic.getCurrentController()

def printer():
    print()


I suspect you’ve something like that. That’s wrong, and it defeats the main benefit of the module controller:



import bge

def printer(cont):
    print()

Here is the correct way to get the controller reference - inside a function

Show the code that we can analyze it.

Btw. I suspect the same as agoose does.

As Monster and agoose mentioned, it is difficult to know how to answer your question when we cannot see the script you have written or how it is connected to the bullet. Your explanation gives only enough detail for making guesses.

It is important to understand that all added objects share a single object name, but they have a unique object reference. If you are trying to access the bullet using its object name, only the first object to match that name will respond. Since the script is connected to the bullet object, you can easily access the unique bullet object by getting the reference of the controller that called the script. The bullet that is calling the script is the owner of that controller, so you can find it easily that way.

from bge import logic

cont = logic.getCurrentController()
bullet = cont.owner

Everything you do in your script should be with using either the controller reference (cont) or its owner’s object reference (bullet).

If you need to access the individual bullets from an outside script (and you cannot get an object reference using hitObject or similar), then you will need to create an object registry and populate it with the bullets as they are added to the scene (you could put this inside of the global dictionary, logic.globalDict{}, or make your own global list). Each bullet should be given a unique id number (as a property of the bullet). Store the bullet’s id number and its object reference in the dictionary/list and use it as a filter to find the bullet in the future. (You cannot just use the scene’s object list, as all of the bullets share the same name.) Here are examples:

At scene startup, run this script once to register some globals that will be used for counting bullets:

from bge import logic

logic.bullet_number = 0
# make a dictionary to store references
logic.bullet_registry = {}

Register a new bullet (this script is run once by the bullet object when it is added):

from bge import logic

cont = logic.getCurrentController()
bullet = cont.owner

# assign the next bullet number to this bullet
bullet['id'] = logic.bullet_number
# increment the bullet number
logic.bullet_number += 1
# add the bullet reference and id to the bullet registry
logic.bullet_registry[bullet['id']] = bullet

Find a bullet by id:

from bge import logic

# get the object reference to bullet number 13
bullet_13 = bullet_registry[13]
# note that this is actually the 14th bullet, since bullet 0 was first

# remove the bullet object
bullet_13.endObject()
# remove the bullet from the registry (kill the zombie reference)
del bullet_registry[13]

@blendenzo:

Isn’t that a little convoluted? I mean, you could just use a simple list, where order is implied:


logic.bullets.append(cont.owner)

# and then get it like this:

bullet = logic.bullets[13]

It’s simpler, don’t you think?

I chose a dictionary instead because of the need to remove object references from the list once an object has been removed from the scene. If you don’t remove references from the list, you will have a list full of references to objects that no longer exist (zombie object references). Also, if you are keeping a list of bullets, and 10,000 bullets are fired in a given timeframe, your list eventually starts to become a memory leak (because you actually can’t remove items from it when you are using the list key as the id)

Consider this: If you wanted to remove bullet 13 from the list, you would do the following


bullet = logic.bullets[13]

logic.bullets.remove(bullet)

The entire list then reorders itself. So the bullet that was logic.bullets[14] becomes logic.bullets[13]. Now the list will no longer correspond with the ‘id’ property on the bullets.

So it seemed to me that a dictionary offered more flexibility and scalability.

I didn’t plan on removing them: I assumed a finite (and relatively low) number of bullets, which seemed probable in a scenario where one needs to actually remember each and every one.

In that case, a list would perform better, because it’s a simpler structure that wraps an array (lookups index directly into memory). The dictionary structure is a little more complex, and must resolve keys via the hash function.

For a large number of bullets, your solution would be fine, but I really can’t think of a scenario where that kind of “registry” would be required.

Everything that comes to mind seems better served by just putting some logic on the bullet itself.

I have a question. In the end, isn’t it better to just use the controller provided by the module? (as agoose77 was pointing out).

Edit: I mean, is registering really necessary? Why would you need to access the bullet once it is fired if every bullet would (normally) end when it hits. I’m curious of what kind of scenarios you are thinking off.

First, I am not recommending that the registry is the best way to handle bullets in this situation. As Goran says, it is best to handle things through the bullet. To reiterate, a registry is only needed when you want to refer to an added object without being able to get its handle (object reference) through some accessible function or variable (so you can’t use ray.hitObject or similar).

Imagine, for example, an RTS where you need to group your military units and send them out to engage the enemy army (think Starcraft). You set up a nice box select functionality for selecting groups of units, and it looks beautiful, but suddenly you realize that you have no way to tell the selected units apart from the unselected ones when you are sending command messages. Using a registry here would work great. Especially if you want to set up multiple combat groups, and quickly switch between them with the numerical keypad or some such thing. Just switch from one registry to the next.

Sorry if I complicated things. The only reason I brought up registries is because I have run into real situations where I couldn’t easily access the object reference of a particular added object, and registering references in a list was a way for me to work around that.

No no, I’m glad to learn about registering (it’s new to me) for the reasons you mentioned. But maybe for those people who won’t easily understand what it’s all about it may have cleared up some things. Thanks for explaining this possible scenario. I think it will be helpful to me and might as well be helpful to others who read this thread.

In the event that Goran was insinuating list lookup were faster than dictated lookup, this is not the case. List lookup is o(n) whilst dict /set lookup o(n). However with more items that dict will eventually converge upon o(n) (many items). Sets are smaller than dictionaries, but lists are smaller still.

Sent from my Nexus 7 using Tapatalk 4 Beta

Thanks for the responses.

The bullet has an always sensor ACIVATE True connected to a python module controler PlayerBulletScript1.behavior1

PlayerBulletScript1.py:


import bge
from bge import logic
from bge import events


cont = bge.logic.getCurrentController()
bullet = cont.owner

SPEED=0.5

class PlayerBulletScript1:
    def __init__(self):
        pass
     
    def behavior1(self):
        bullet.applyMovement((0,SPEED,0), False)
            

logic.globalDict["PlayerBulletScript1"] = PlayerBulletScript1()

def behavior1():
    logic.globalDict["PlayerBulletScript1"].behavior1()

from this script I expected the bullet all new bullets to move at SPEED when created. Only the first bullet does this. The rest don’t move once added to the scene unless I call behavior1 in init and use a python script instead of a module.

Have you read the posts above? Specifically my post regarding references.

You’re using module mode, and that executes behaviour1. The module is imported once, meaning that cont is only assigned once (to the first bullet controller). Also, this is not the best way to do things, you’re mixing namespaces / scopes and that’s pointless.


from bge import logic, types


class PlayerBullet:
    def __init__(self, owner):
        self.owner = owner
        self.speed = 0.5
     
    def behaviour_1(self):
        self.owner.applyMovement((0, self.speed, 0), False)


def behaviour_1(cont):
    own = cont.owner
    
    try:
        bullet = own['bullet']
    except KeyError:
        bullet = own['bullet'] = PlayerBullet(own)
    
    bullet.behaviour_1()
    



Or a more advanced demo


from bge import logic, types


class PlayerBullet(types.KX_GameObject):
    
    def initialise(self):
        self.speed = 0.5
    
    def behaviour_1(self):
        self.applyMovement((0, self.speed, 0), False)


def behaviour_1(cont):
    own = cont.owner
    
    try:
        update = own.behaviour_1
        
    except AttributeError:
        own = PlayerBullet(own)
        own.initialise()
        update = own.behaviour_1
    
    update()



Or even more advanced

from bge import logic, types

class GameObjectMeta(type):
	def __new__(cls, name, bases, attrs):
		initialiser = attrs.get("__init__", object.__init__)
		
		if not types.KX_GameObject in bases:
			bases = bases + (types.KX_GameObject,)
			
		attrs['__init__'] = cls.initialiser
		attrs['__oldinit__'] = initialiser
		
		return super().__new__(cls, name, bases, attrs)
	
	def initialiser(self, obj, *args, **kwargs):
		self.__oldinit__(*args, **kwargs)
		
class PlayerBullet(metaclass=GameObjectMeta):
	
	def __init__(self):
		self.speed = 0.5
	
	def behavior_1(self):
		self.applyMovement((0, self.speed, 0), False)




def main(cont):
	own = cont.owner
	
	try:
		update = own.behaviour_1
		
	except AttributeError:
		own = PlayerBullet(own)
		update = own.behaviour_1
	
	update()




Just to make sure. here is your code corrected:


from bge.logic import getCurrentController, globalDict

# never do this at module level:
#    cont = bge.logic.getCurrentController()
#    bullet = cont.owner

# this is fine as it defines a constant:
SPEED=0.5

# better choose a more descriptive name for the class (e.g. skip "...Script1")
class PlayerBulletScript1:
    def __init__(self):
        pass
     
    # better choose a meaningful name (best starting with a verb ;) )
    def behavior1(self):
        bullet = getCurrentObject()
        bullet.applyMovement((0,SPEED,0), False)
            

globalDict["PlayerBulletScript1"] = PlayerBulletScript1() # you get exactly one instance!

# again, choose a decriptive name (with a verb that tells what it does)
def behavior1():
    logic.globalDict["PlayerBulletScript1"].behavior1()

def getCurrentObject():
   return getCurrentController().owner

I just wonder why you need a class/instance in this code.

No.

The lookup time is effectively constant for both lists and dictionaries. However, the constant factor for lists is lower, making it faster.

Hello guys. Thankyou for all the help

I have adopted your first example agoose77 and it works. I have added some comments where I have questions


import bge
from bge import logic, types

cont = bge.logic.getCurrentController()
bullet = cont.owner

class PlayerBullet:
    def __init__(self, owner):
        self.owner = owner
        self.speed = 0.5
     
    def behavior_1(self):
        self.owner.applyMovement((0, self.speed, 0), False)
    
    def die(self):
        self.owner.endObject()

logic.globalDict["PlayerBulletScript1"] = PlayerBullet(bullet)

def behavior_1(cont): #is the 'cont' an automatic argument for the python module controller
    own = cont.owner
    
    bullet = PlayerBullet(own)
    
    bullet.behavior_1()

I am a little confused. In my python module controller I have entered ‘PlayerBulletScript1.behavior_1’

behavior_1 is a global method. The behavior_1 being called is not part of the PlayerBullet class. Why does the python module controller call it?

No, behaviour one is local to the class, it’s an instance method. You can access it globally calling PlayerBullet.behaviour_1
Unless you mean the one with the controller argument. In this case, it is a global method, but its arguments aren’t bound - they’re provided by the controller running the module. Therefore, rather than having a static (single assigned) reference to a controller, you get the current controller each frame. This is because before hand the getCurrentController() was only executed once.


import bge
from bge import logic, types

cont = bge.logic.getCurrentController()
bullet = cont.owner

class PlayerBullet:
    def __init__(self, owner):
        self.owner = owner
        self.speed = 0.5
     
    def behavior_1(self):
        self.owner.applyMovement((0, self.speed, 0), False)
    
    def die(self):
        self.owner.endObject()

logic.globalDict["PlayerBulletScript1"] = PlayerBullet(bullet)

def b1(cont): #is the 'cont' an automatic argument for the python module controler
    own = cont.owner
    
    bullet = PlayerBullet(own)
    
    bullet.behavior_1()

ok. I have changed the global behavior_1 to ‘b1’ and the python module controller now says ‘PlayerBulletScript1.b1’ and the game runs the same.

How about this?


from bge.logic import getCurrentController

SPEED = 5.0
def move():
    owner = getCurrentController().owner
    owner.applyMovement((0, SPEED, 0), False)
    
def die():
    owner = getCurrentController().owner
    owner.endObject()

All of that is much simpler just with logic bricks :slight_smile:

the lines 4 and 5
cont = bge.logic.getCurrentController()
bullet = cont.owner

here read only the first time the code run (from the first bullet obj)

so cont in ever the first controller of the first bullet, and bullet is ever the same bullet.
this the difference with script mode(that instead read ever all)

note that , also this line is read only one time :

import bge

but this not have to change , so not give “problem”

i make in this way to write class

[QUOTE=oladitan;2415580]


import bge
#from bge import logic, types

#cont = bge.logic.getCurrentController()
#bullet = cont.owner

class PlayerBullet:
    def __init__(self, owner):
        self.owner = owner
        self.speed = 0.5
     
    def behavior_1(self):
        self.owner.applyMovement((0, self.speed, 0), False)
    
    def die(self):
        self.owner.endObject()

#logic.globalDict["PlayerBulletScript1"] = PlayerBullet(bullet)

def b1(cont): #is the 'cont' an automatic argument for the python module controler
    own = cont.owner
    if not "PlayerBullet" in own:
        own["PlayerBullet"] = PlayerBullet(own)
    
   own["PlayerBullet"].behavior_1()

PS:maybe using try/except can be better if the initialization require many argument and is prone to error
i not sure