Need help optimizing code

Hello,

this isn’t a real issue but it is something I always wanted to understand.
I am applying a code as a module to 4 objects which use a ray sensor.
The code is for all 4 objects exactly the same, except for the ray sensor they are using (which is from each objects parented empty).
What I did was just copy and paste the code and replace the object that was loaded.
But I know that somehow it is possible to make things easier by using index or something like that, I think it was something like “for … in …”. But I have no idea how to apply it.


import bge


def obj1(cont):
    cont = bge.logic.getCurrentController()
    own = cont.owner
    scene = own.scene
    rayEmpty= own.scene.objects["Obj.001"]
    ray = rayEmpty.sensors["Ray"]
    
    if ray.positive:
     (...)

def obj2(cont):
    cont = bge.logic.getCurrentController()
    own = cont.owner
    scene = own.scene
    rayEmpty= own.scene.objects["Obj.002"]
    ray = rayEmpty.sensors["Ray"]

    if ray.positive:
        (...)

--> And so on for the other objects

Could someone help me changing this code, so that I don’t have to copy/paste it and that it is possible to load just one module function for all the objects running the module?

have the code run the same module on each of the objects. make use of the “owner”.

another thing to consider, is this really necessary? can you simplify or approximate by having only one raycast? will the player even notice or care? if this is for your skateboard, then honestly, i would just rotate all the wheels the same, or not at all at this point.

To reduce the amount of replicated code you first need to analyse what is common (shared) and what code is different.

According to your statement, everything is common just the sensor is different.


import bge


def obj1(cont):
    cont = bge.logic.getCurrentController()
    own = cont.owner
    scene = own.scene
    rayEmpty= own.scene.objects["Obj.001"]
    ray = rayEmpty.sensors["Ray"]
    
    if ray.positive:
     (...)

def obj2(cont):
    cont = bge.logic.getCurrentController()
    own = cont.owner
    scene = own.scene
    rayEmpty= own.scene.objects["Obj.002"]
    ray = rayEmpty.sensors["Ray"]

    if ray.positive:
        (...)

What is the variable part?

It is object name “Obj.001” and “Obj.002” (to be more precise it is “1” and “2” ;)) and it is the name of the function - obj1 and obj2.

You have different options to provide the variable value. This comes to my mind:

  • current object (owner)
    –> object name
    –> object property
  • current controller
    –> controller name
    –> connected logic brick
    ----> name of brick
    ----> field value of brick

samples:


scene.objects[ "Obj." + own.name]
scene.objects[ own["rayEmptyName"] ]
scene.objects[ cont.name ]
scene.objects[ cont.actuators["property"].name ]
scene.objects[ cont.actuators["property"].propName ]
...

Great, thank you, that helps!

Another option:

Parent child relationship

Ok, I tried the following, but I get an error message:


import bge


def wheel(rayEmptyStr):
    cont = bge.logic.getCurrentController()
    own = cont.owner
    scene = own.scene
    board = own.scene.objects["Board"]
    boardVelY = board["speedY"]
    rayEmpty= own.scene.objects[rayEmptyStr]
    ray = rayEmpty.sensors["Ray"]
    vel = boardVelY
    
    if ray.positive:
        velWheel = vel*-0.1
        own["saveVel"] = velWheel
    else:
        if own["saveVel"] != 0.001:  #assuming your property is positive
            own["saveVel"] -= own["saveVel"]*0.01  #how fast to slow the rotation (-30% per frame)
            velWheel = own["saveVel"]
        else:
            own["saveVel"] = 0
            velWheel = 0


    own.applyRotation((velWheel, 0, 0), True)


wheel("WheelRay.000")
wheel("WheelRay.001")
wheel("WheelRay.002")
wheel("WheelRay.003")

As an error message I get:
line 11, in wheel
KeyError: “list[key]: ‘And’ key not in list”

Any ideas what is wrong?

line 10+11.

rayEmpty= own.scene.objects[rayEmptyStr]    ray = rayEmpty.sensors["Ray"]

list is and the info in it. grabbing sensors is also trough a list. So in this case ‘Ray’ does not exist.
Why? because rayEmptyStr does not exist at all. assign a variable with that name defining the object or put the name in ‘quotes’ (without quotes = a variable, with ‘quotes’ is a string).

How could you find this error yourself? just read your code and you see, if not then you needed to use more checks like:


if rayEmpty:
    ray = rayEmpty.sensors....

But I did assign a variable in the function

def wheel(rayEmptyStr):

The content of the variable is defined below when executing the function:
wheel(“WheelRay.000”)
wheel(“WheelRay.001”)
wheel(“WheelRay.002”)
wheel(“WheelRay.003”)

That’s why I don’t understand why it doesn’t work…

oops, my bad, read over that.

#edit

try this:


# wheels do not need anything beside a property, used in get_wheels().
# hook this script to always(true) -> python(module) -> scriptname.set_wheel_rotation (be sure that your scriptname in the editor ends with .py


def get_wheels(cont):
    own = cont.owner
    # change the property(WHEEL) to a property you use in every wheel
    wheel_list = [obj for obj in own.scene.objects if 'WHEEL' in obj]
    own['wheel_list'] = wheel_list


def set_wheel_rotation(cont):

    own         = cont.owner
    
    #if we don' have the wheels, grab them.
    if not 'wheel_list' in own:
        get_wheels(cont)

    board       = own.scene.objects["Board"]
    vel         = board["speedY"]
    ray_range   = 5.0 # how far the ray should cast in blender units
    
    # looping trough the wheels
    for wheel in own['wheel_list']:
        
        #create a downwards z vector (it is possible that you need to change the + into - in order for it to work)
        z_vector = wheel.worldPosition + wheel.worldOrientation.col[2]
        #cast a ray from the wheel 
        ray = wheel.rayCast(wheel, z_vector, ray_range)
        
        if ray[0]: #if ray hits an object
            
            own["saveVel"]  = vel*-0.1
            
        elif own["saveVel"] != 0.001:  #assuming your property is positive
                
            own["saveVel"] -= own["saveVel"]*0.01  #how fast to slow the rotation (-30% per frame)
                
        else:
            own["saveVel"] = 0.0


         wheel.applyRotation((own["saveVel"], 0, 0), True)

KeyError means the key you are looking for does not exist.
As Cotaks wrote in post#7 (line 11: rayEmpty.sensors[“Ray”]) the key “Ray” is not in the collection. But it is not because the list does not exist it is because the game object rayEmpty has no sensor called “Ray”).

Btw. I do not think it is a good design to access the sensors from outside the owning object. This creates very complicated and hard to understand dependencies.

I suggest to let the ray sensor from the empty trigger code (rather what ever runs the code now - the wheel I guess). The event is on the empty. The operation(s) can be performed on other (known) objects. I mean when the empty knows the wheel it belongs too, it can tell it to turn.