Ren'Py Looping images with random sound effects (Solved)

Wistala

Broken Sky Dev
Game Developer
Jul 24, 2020
152
651
I am trying to make a short "animation" that is really just a loop of two pictures. Each time one of the pictures displays I'd like a random sound effect to play from a list. Here is the code i have so far (Thank you to a different thread for the python function):


Python:
default bjeff = ["bjeff1.wav", "bjeff2.wav", "bjeff3.wav", "bjeff4.wav"]
init python:
    class atl_play_sound(object):
        def __init__(self,sound):
            super(atl_play_sound,self).__init__()
            self.sound=sound
        def __call__(self,*args,**kwargs):
            if not renpy.predicting():
                renpy.sound.play(self.sound, relative_volume = 1.0)


image l first bj anim dom:
    "bg l_first_bj_finish.png" with Dissolve(0.15)
    function atl_play_sound(renpy.random.choice(bjeff))    #File selected here will be random the first iteration only
    pause 1.2
    "bg l_first_bj_deep.png" with Dissolve(0.15)
    function atl_play_sound(renpy.random.choice(bjeff))    #File selected here will be random the first iteration only
    pause 1.2
    repeat
The problem is that Renpy selects a random file on the first iteration but then continues to play the same file it selected for every iteration after that. I've already done some digging and found that this is how Renpy treats "random" choices due to its rollback feature. Is there anything I can do to fix this? I would say it's not a huge issue because there are only four audio files but I will need to do the same thing again for a different scene where I have 12 different audio files. Any help would be appreciated!
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,979
16,236
[...] found that this is how Renpy treats "random" choices due to its rollback feature.
It's true that Ren'Py have a particular way to treats randomization, but it's not the reason why you have that issue.

The value of function is created once, and only once, when the image is built. And like it's when that value is created that you perform the randomization, you'll always get the same sound.

Your "alt_play_sound" class should be:
Python:
init python:
    class atl_play_sound(object):

        def __call__(self,*args,**kwargs):
            if not renpy.predicting():
                renpy.sound.play(renpy.random.choice(store.bjeff), relative_volume = 1.0)
 
  • Like
Reactions: gojira667

Wistala

Broken Sky Dev
Game Developer
Jul 24, 2020
152
651
It's true that Ren'Py have a particular way to treats randomization, but it's not the reason why you have that issue.

The value of function is created once, and only once, when the image is built. And like it's when that value is created that you perform the randomization, you'll always get the same sound.

Your "alt_play_sound" class should be:
Python:
init python:
    class atl_play_sound(object):

        def __call__(self,*args,**kwargs):
            if not renpy.predicting():
                renpy.sound.play(renpy.random.choice(store.bjeff), relative_volume = 1.0)
You're a lifesaver! I have been trying to figure this out for so long. So if I wanted to do basically this same thing but with a different set of sound files I would need a new class correct? So if I modify the code to the following:

Python:
init python:
    class atl_play_sound_bjeff(object):
        def __init__(self,sound):
            super(atl_play_sound_bjeff,self).__init__()
            self.sound=sound
        def __call__(self,*args,**kwargs):
            if not renpy.predicting():
                renpy.sound.play(renpy.random.choice(store.bjeff), relative_volume = 1.0)
(Which does work)
If I wanted to do the same thing with a list called 'slide' I would need to make a new function like so?:

Python:
default slide = ["file1", "file2". "file3"]

init python:
    class atl_play_sound_slide(object):
        def __init__(self,sound):
            super(atl_play_sound_slide,self).__init__()
            self.sound=sound
        def __call__(self,*args,**kwargs):
            if not renpy.predicting():
                renpy.sound.play(renpy.random.choice(store.slide), relative_volume = 1.0)
I imagine trying to make this class modular for different lists would be a hell of a headache so I'd be better off just making a bunch of separate functions?
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,979
16,236
So if I wanted to do basically this same thing but with a different set of sound files I would need a new class correct?
No, you don't need a new class for each set of sound. It suffice to mix the code for the two classes.
From your original you get the "I give you the information you need" part, and from mine the "I randomize with every call". Except that here "the information you need" isn't anymore the sound to play, but the list where to randomly pick it.

Python:
init python:

    class atl_play_sound(object):

        def __init__(self, sound_list ):
            self.choices=sound_list

        def __call__(self,*args,**kwargs):
            if not renpy.predicting():
                renpy.sound.play(renpy.random.choice(self.choices), relative_volume = 1.0)
Side note: The super is totally useless in this particular case.

Then you'll have function alt_play_sound( bjeff ) for one image, and function alt_play_sound( slide ) for the other.
 

Wistala

Broken Sky Dev
Game Developer
Jul 24, 2020
152
651
No, you don't need a new class for each set of sound. It suffice to mix the code for the two classes.
From your original you get the "I give you the information you need" part, and from mine the "I randomize with every call". Except that here "the information you need" isn't anymore the sound to play, but the list where to randomly pick it.

Python:
init python:

    class atl_play_sound(object):

        def __init__(self, sound_list ):
            self.choices=sound_list

        def __call__(self,*args,**kwargs):
            if not renpy.predicting():
                renpy.sound.play(renpy.random.choice(self.choices), relative_volume = 1.0)
Side note: The super is totally useless in this particular case.

Then you'll have function alt_play_sound( bjeff ) for one image, and function alt_play_sound( slide ) for the other.
Oh, I see now! That is insanely helpful! Thank you!
 
  • Like
Reactions: osanaiko