I would recommend to create a custom sensor (checking the distances) and a custom actuator (adding the objects).
Unfortunetly the BGE does not allow to create any of them (a mysterium since I know the BGE). This is a perfect example where we need it as we get a processing conflict (constant checking sensor vs. one frame running actuator).
Cotax showed you an example how to behave like a sensor by storing the last evaluation status. (I recomend to give the proeprty a more meaningful name e.g. “last evaluation status” or “was in range” or “was positive”).
Additionally accessing by “get()” allows you to use an internal property (no need to create it via GUI).
General Custom Sensor
A basic “mimicry” of a sensor could look like that (it includes the controller part assuming there is no additional sensor to check):
import bge
INTERNAL_PROPERTY_LAST_EVALUATION_STATUS = "last status"
currentStatus = evaluate() # call your custom evaluation
lastStatus = owner.get(INTERNAL_PROPERTY_LAST_EVALUATION_STATUS)
owner[INTERNAL_PROPERTY_LAST_EVALUATION_STATUS] = currentStatus
if currentStatus:
if isTrueLevelTriggering() or currentStatus != lastStatus:
activateAllActuators()
else:
if isFalseLevelTriggering() or currentStatus != lastStatus:
deactivateAllActuators()
This sample does not include the details of isTrueLevelTriggering(), isFalseLevelTriggering, activateAllActuators(), deactivateAllActuators() I guess you see what it is meant to do.
evaluate() should perform your evaluation check resulting in True or False as this are the only results that are possible.
Applied to your situation
Translated to your situation your code might look like this (including the actuator part):
import bge
INTERNAL_PROPERTY_LAST_EVALUATION_STATUS = "last status"
owner = bge.logic.getCurrentController().owner
player = owner.scene.objects["player"]
#evaluate
currentStatus = owner.getDistanceTo(player) > 29
lastStatus = owner.get(INTERNAL_PROPERTY_LAST_EVALUATION_STATUS)
#store current status to be used within hte next frame
owner[INTERNAL_PROPERTY_LAST_EVALUATION_STATUS] = currentStatus
if currentStatus and currentStatus != lastStatus:
# behave like sensor without pulse mode
# do your thing when the sensor just went positive
positions = [[3,4,5],[2,2,2],[1,1,1],[3,3,3],[8,8,8],[10,10,10]]
reference = player # it might maybe better to use owner as reference rather than player
for i in range(0, len(positions)):
addedLung = reference.scene.addObject("lung", reference, 0)
addedLung.worldPosition = positions[i]
What does it do?
- it grabs the objects that are needed for evaluation
- it evaluates if the distance between the owner and the player are above 29
- it checks if the evaluation is True and the evaluation of the previous frame differs (must be False)
3A) if so … add the objects
3B) otherwise no further processing
I hope you notice this code snippet does not look for True or False level triggering. I assume you do not want it configurable. Therefore this configuration is hard-coded by skipping according checks. This belongs to False evaluation too (3B).
With build-in sensors
Alternatively you can use a near sensor. This way you need the “controller” and “actuator part” only:
import bge
#controller part
controller = bge.logic.getCurrentController()
sensor = controller.sensors[0]
if sensor.positive:
#actuator part
owner = controller.owner
player = owner.scene.objects["player"]
positions = [[3,4,5],[2,2,2],[1,1,1],[3,3,3],[8,8,8],[10,10,10]]
reference = player # it might maybe better use owner as reference
for i in range(0, len(positions)):
addedLung = reference.scene.addObject("lung", reference, 0)
addedLung.worldPosition = positions[i]
I hope you see the “actuator part” is the same as above. Here you do not need to worry about the sensor processing as the build-in sensor already does everything for your.
Indeed both methods can be mixed e.g. when you want to perform custom evaluation and build.in sensor together. I guess these are more details as you want to know right now ;).
[edit]
Small mistake in the above code:
addedLung = scene.addObject("lung", reference, 0)
must be
addedLung = reference.scene.addObject("lung", reference, 0)
(otherwise Python will complain that ‘scene’ is not present)