Script to apply same sensor/controller to multiple objects

Hi all,
I am making a game where you can click certain objects in the environment. I want to have a log file (.txt) that logs which objects are clicked. Currently, my approach is to add a Mouse sensor to the clickable object and I connect that with a Python controller with the script that logs the objectname.
However, a very high number of objects should be clickable, and currently I have to add the sensor and python controller to all of these objects. All objects that should be clickable are on the same layer. Is there a way to attach the Mousesensor to all objects on a certain layer so that I do not have to add the sensor to all single objects?
Thanks a lot,
Best,
Nynke

It is better you use a separate object that reads a mouse over any sensor.

That sounds like a good idea. Only the ‘owner’ of the sensor is than that other object (to which I attached the mouse over any sensor), so how can I than get the name of the object that was actually hovered over?

You should use the “hitObject” attribute of the KX_MouseFocusSensor (Mouse Sensor), which returns a KX_GameObject instance (the hit game object)

Thanks for your responses, I first tried Agoose77’s suggestion. I made a new (empty) object and attached a MouseOver Any sensor (named MouseOver) and a Mouse Left button sensor (named MouseClick) to it. These two sensors are connected to the script below. I tried to output the hitobject with the line print(cont.hitObject) but I now get the following errors:
Python script error: object ‘MouseOverObject’, controller python.
TypeError: KXpythonseq not callable.

This is the script I now use:
Do you have any ideas what is going wrong?

import bge
import os


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


log_dir = bge.logic.expandPath('//purchase_logs//')


mClick = cont.sensors['MouseClick']
mOver = cont.sensors['MouseOver']
if mClick.positive & mOver.positive:
    fid = open(log_dir + 'purchase_log.txt', 'a')
    print(cont.hitObject)
    fid.write(str(obj))
    fid.close()
    obj.visible = 0

Apologies, I made a small error in my last script. I now got it working. However, the only problem is now that ALL objects are clickable and disappear when I click them. However, I want this procedure to apply only to objects on the second layer. Any idea how I could add this limitation to the script?

import bge
import os


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


log_dir = bge.logic.expandPath('//purchase_logs//')


mClick = cont.sensors['MouseClick']
mOver = cont.sensors['MouseOver']
if mClick.positive & mOver.positive:
    fid = open(log_dir + 'purchase_log.txt', 'a')
    print(mOver.hitObject + '
')
    fid.write(str(mOver.hitObject))
    fid.close()
    mOver.hitObject.visible = 0

Have a look at the mouse sensor … you can filter by property (as in the demo)

Thanks, I noticed indeed that you can filter by property. But I do not seem to find how you can get the layer an object is on, at least not in the game engine. I have got it working in Blender itself with bpy but unfortunately this does not work when I create a runtime of it. In Blender the script below checks if the object is on the 2nd layer.

import bgeimport os
import bpy


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


log_dir = bge.logic.expandPath('//purchase_logs//')


mClick = cont.sensors['MouseClick']
mOver = cont.sensors['MouseOver']
purchase =  mOver.hitObject
products = [ob.name for ob in bpy.context.scene.objects if ob.layers[1]]


if mClick.positive & mOver.positive & any(purchase.name in s for s in products):
    fid = open(log_dir + 'purchase_log.txt', 'a')
    fid.write(str(mOver.hitObject) + '
')
    fid.close()
    mOver.hitObject.visible = 0

There is no layer.

There are active objects (residing in enabled layers) and inactive objects (residing in disabled layers).

Hmm… so there does not exist an bge equivalent for the line products = [ob.name for ob in bpy.context.scene.objects if ob.layers[1]]? That is a pity…
I have not disabled any layer to I guess all my objects are active, I cannot make a distinction there.

Can you think of alternatives? My game exists of an environment (non clickable objects) and in that environment are hundreds of clickable objects. It will not be very convenient to add a property to each of these objects separately. Or is there a way to script that?

Thanks a lot,

Best, Nynke

Either group your objects with property name, you can select all clickable objects and copy property to all of them at once(spacebar and enter “Copy Game Property”) or you can try to mess around with their physic collision layer

The access to gameobject.collisionGroup and gameobject.collisionMask was added into the python api recently, you can group/update your object layer on the fly by changing their bitmask value

Properties are the common way on that.

The most sensors filter by property already.
You can setup as many objects with properties of your choice as you like.

You can add properties via Logic bricks and via Python controller.
With Python it is possible to remove properties from objects (usually it is not done that way).

Other alternatives are:

  • Object name or part of the object name
  • property value
  • partial property name
  • children
  • parent
  • group objects
  • group members
  • assigned logic bricks
  • objects referred in logic bricks (e.g. camera actuator)
  • location
  • size

Hi Guramax and Monster,
Thanks for your help!
I decided to prepend the clickable objectnames to solve the problem. For people with the same problem, please see my code below.

import bgeimport os


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


log_dir = bge.logic.expandPath('//purchase_logs//')


mClick = cont.sensors['MouseClick']
mOver = cont.sensors['MouseOver']
purchase =  mOver.hitObject
# Get all products in scene 
products = [ob.name for ob in bge.logic.getCurrentScene().objects if ob.name[:2]=='p_']


if mClick.positive & mOver.positive & any(purchase.name in s for s in products):
    fid = open(log_dir + 'purchase_log.txt', 'a')
    fid.write(str(mOver.hitObject) + '
')
    fid.close()
    mOver.hitObject.endObject()