2
$\begingroup$

In a script that I'm writing I'm trying to create a list of all the objects within a specific distance of the player which have a certain property assigned to them, but I can't figure out how to get the script to search within that specified distance instead of across the entire scene.

For instance, to create a list of all the objects within a scene that contain a specific variable, I can use the code:

object_list = [ob for ob in scene.objects if ob.get("property_name")]

I have several hundred objects that contain this property, but I only want the objects that are within a specific distance from the player to be put into the list.

Now here's the dilemna: I need to create the list every frame, and I know it's possible to do what I want by scanning for every object in the scene with the property, but with several hundred (potentially thousands) of objects in the scene using this method becomes way too resource intensive.

So my question to you; Is it possible to only scan the objects within a specific distance from the player, for any object with the specified property?

To clarify, for ob in scene.objects scans the entire scene for a specific object. Is it possible to do exactly the same, but only for a small section of the scene (within a distance from the player)?

$\endgroup$
  • $\begingroup$ Looping over all objects is not that inefficient and often used when the near sensor is too heavy. $\endgroup$ – Monster Aug 15 '17 at 7:30
  • $\begingroup$ How is scanning through thousands of objects to create a list of several hundred objects every frame not inefficient? $\endgroup$ – christai Aug 15 '17 at 12:16
2
$\begingroup$

You can do it this way.

  1. Add near sensor.

enter image description here

  1. Get the list of hit objects.

Script if near sensor look for specific property name

from bge import logic

nearSen = logic.getCurrentController().sensors['Near']
list = nearSen.hitObjectList 

or if near sensor not look for specific property name

list = [obj for obj in nearSen.hitObjectList if nearSen.positive and 'property' in obj.getPropertyNames()]

Edit

The objects need to be set to actor so the sensor can detect them.

enter image description here

Download the blend to see how it works.

KX_NearSensor

KX_TouchSensor

$\endgroup$
  • $\begingroup$ I tried what you've explained above, but everywhere that I tried to implement the code it gave me either a "KX_NearSensor object is not iterable" error, a "KX_PythonSeq object is not callable" error, or it printed an empty list, even though there should have been around 30 objects in the list. Why can't I get the names of the objects hit by the near sensor? $\endgroup$ – christai Aug 15 '17 at 11:31
  • $\begingroup$ Well you are doing something wrong. Make the objects actors so they can be detected by the sensor, Make Sure the distance is correct and check the blend file i upload in the answer. $\endgroup$ – Strapicarus Aug 15 '17 at 14:33
  • $\begingroup$ I sorted the problem, as you said they have to be actors, but also I had the distance set to 0, which is why the script kept printing a Null result. $\endgroup$ – christai Aug 16 '17 at 5:58
2
$\begingroup$

I think I can help you a bit.

To make the scanning more efficient you have some options:

Reduce the number of frames with scan

You need to check every time when at least one of your criteria changes:

  1. object with property gets added
  2. object with property gets removed
  3. object gets property added
  4. object gets property removed
  5. object with property enters scan distance
  6. object with property leaves scan distance

I guess you know when objects are added (1.) or removed (2.). This should not happen that often. After that you need a new scan.

I think 3. and 4. can be excluded as long as you do not manipulate the properties via Python. But you should know when that happens. So you need to scan after that.

From the information you provided I can't exclude any object to run into 6. and 7. any time. You might isolate objects that are not moving (as they do not change distance) as long as the player is not moving. A moving player needs a scan after each single step.

Reduce the number of scanned objects

Unless you can make some assumptions, there is nothing to isolate candidates.

This depends on your specific situation (e.g what objects can move what objects can't move).

In your example you can create a list of candidates via property containment (as in your code).

I assume you have much less objects with the property then without.

When you cache the candidates:

candidates = [object for object in scene.objects if "property_name" in object]
owner["candidates"] = candidates

You can iterate over the them at a later time:

candidates = owner["candidates"]
# do distance check with candidates

You update the list when on the events from above (1. - 4.).

Efficiency

When you use both methods (reducing scans + reducing number of candidates) you should reduce the processing time quite a lot.

Lets do some estimates:

Worst case

  • all objects (5000) are candidates

  • the player moves constantly

-> you are where you are now, scan all objects at each single frame

Best case

  • there is no candidate

-> nothing to do

-> a little overhead to check there is nothing to do

Average Case

This is hard to determine as it depends on your situation. So this is just a wild guess:

  • 100 of 5000 objects have the property
  • 10 of the candidates are constantly moving
  • the player moves frequently (~50% of the time)
  • candidates are added/removed every 10 seconds

You update the candidates every 10 seconds (iteration over 5000). You scan 100 candidates all the time (iteration over 100).

Conclusion

You can get more efficiency

  • when no candidate is moving (as you need to check only when the player moves).
  • when you can isolate candidates even further (e.g. exclude a complete group of candidates).

I know this is still a bit vague, but it should provide you an idea.

$\endgroup$
  • $\begingroup$ Thanks, this will be useful for refining the script further, highly appreciated. $\endgroup$ – christai Aug 16 '17 at 5:55
0
$\begingroup$

I would cycle through all the object in the scene with a for loop. Then check each with a if statement which checks if they are within a certain distance. If it passes that add it to the list.

$\endgroup$
  • $\begingroup$ Yes, that's what I'm currently doing, which I tried to explain in my question (but obviously I wasn't clear enough), but this method is waay too resource intensive... I want to only cycle through all the objects in the scene within the specified distance. Does that make sense? $\endgroup$ – christai Aug 14 '17 at 12:58
  • $\begingroup$ @christai Ok, sorry I didn't understand that. There might be another way to do it but I don't know of it. $\endgroup$ – Tim Aug 14 '17 at 16:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.