BGE Runs Script Twice

Context: I have a game that I wanted to add a sound to. Since I was using Python, I had my script play a sound using aud, though the game played the sound twice. I changed it trigger a sensor, and the sound still went twice. I soon found out that the script was running twice, so I dissected my game trying to figure it out.
Eventually I made a new .blend file and added a single cube. I set an Always sensor with a tap pulse to run a python script with one line:
print(“A”)
And when I ran it there were two As in the console.

Question: Has BGE always run scripts twice? I’ve never had this issue before but I usually don’t have scripts run. It also doesn’t run it twice on the same frame, since the sound playing twice is somewhat noticeable (enough for a short click to sound like two)
Is there a fix for this?

Moved to #game-engine:game-engine-support-and-discussion.

It has always been like that.

You can check the sensor status to execute the script only one time:

import bge

cont = bge.logic.getCurrentController()

if cont.sensors["sensorName"].status == bge.logic.KX_INPUT_JUST_ACTIVATED:
    #run the script

or ensure in another way that the script isn’t executed twice:

import bge

cont = bge.logic.getCurrentController()
own = cont.owner
scene = bge.logic.getCurrentScene()

# examples
if "init" not in own:
    #run the script
    own["init"] = True

if "init" not in scene:
    #run the script
    scene["init"] = 1

if not hasattr(bge.logic, "init"):
    #run the script
    bge.logic.init = True

.....

It’s like aWeirdOwl said, but some more details about it: the sensors have statuses that indicate its status on the current frame. By default, the sensor sends KX_SENSOR_INACTIVE when not triggered, KX_SENSOR_JUST_ACTIVATED when just triggered (during one single frame), KX_SENSOR_ACTIVE when its pulse is still positive (such as pressing and holding a button) and KX_SENSOR_JUST_DEACTIVATED when just deactivated (such as releasing a button, during one single frame). Most of the time, checking these statuses is not needed, only checking the sensor.positive status would be enough.

Below there’s an example of a sound played correctly with aud, including an optimization to avoid loading and buffering the sound each time the sensor is triggered.

ex_aud.zip (96.9 KB)

import bge
import aud
from bge.logic import expandPath

cont = bge.logic.getCurrentController()
own = cont.owner
sensor = cont.sensors["Spacebar"]

# Will run on sensor positive pulse
if sensor.positive:
    
    # Will run once even with the sensor with pulse mode enabled
    if sensor.status == 1: # bge.logic.KX_SENSOR_JUST_ACTIVATED == 1
        
        # Loads and buffers the factory into scene if not already loaded
        if not "Factory" in own.scene:
            own.scene["Factory"] = aud.Factory(expandPath("//choice2.wav")).buffer()
            print("> Factory loaded and buffered at time", bge.logic.getRealTime())
            
        # Play factory if already loaded
        if "Factory" in own.scene:
            aud.device().play(own.scene["Factory"])
            print("Factory played at time", bge.logic.getRealTime())