How would I make 3 part horns?

3 part horns are horns that you can make go on for any amount of time. They use a beginning audio, a middle audio, which can be looped, and an end audio. I have tried before to make this, but it didn’t work. I don’t know where the script is anymore. Anyway, how would I make this? I can’t find anything on how to do it.

Several ways, you could have the sequence just wait for the previous sound to finish playing before starting, have them fade volumes in/out, etc. I always used the TF2 minigun as an example of this sound behavior:

Player begins holding mouse1, the “wind up” audio plays. As soon as that soundfile finishes, it starts playing the “firing” audio. If at any point the player stops holding mouse1, both the wind up and fire audios are immediately stopped, and the “wind down” audio is started.

In code, you could make them play in order like this:

script.windup:Play()
repeat task.wait() until not script.windup.IsPlaying--IsPlaying == false when its finished

script.fire:Play()
repeat task.wait() until not script.fire.IsPlaying--needs different break point if its looping

script.winddown:Play()
repeat task.wait() until not script.winddown.IsPlaying

How would I make that break with the looping audio? I have tried detecting when the player stops holding H, it will set HornLoop.IsPlaying to false, but for some reason the horn never stops

Try calling :Stop() on the sound instead, .isPlaying is a read-only boolean. You may be thinking of .Playing which can be manually set, but that’s for pausing/resuming and I’m not sure how reliable/optimal that would be in this case.

I have no idea why my brain just removed :Stop() from itself.

1 Like

Ok, it still repeats forever. Here is the script (Not the entire thing, just the important part)

		repeat task.wait() until not HornStart.IsPlaying--IsPlaying == false when its finished
		

		HornLoop:Play()
		repeat task.wait() until not HornLoop.IsPlaying--needs different break point if its looping
		UIS.InputEnded:Connect(function(key)
			if key.KeyCode == Enum.KeyCode.H then
				HornLoop:Stop()
			end
		end)
		HornEnd:Play()
		repeat task.wait() until not HornEnd.IsPlaying

I see it. Delete the repeat task.wait() until not HornLoop.IsPlaying as this will yield the script infinitely, since it’s waiting for an infinite sound to stop playing. Reorder it to look something like:

repeat task.wait() until not HornStart.IsPlaying

HornLoop:Play()

UIS.InputEnded:Connect(function(key)
	if key.KeyCode == Enum.KeyCode.H then
		HornLoop:Stop()
		
		HornEnd:Play()--play the end *inside* the InputEnded for it to work as intended
		repeat task.wait() until not HornEnd.IsPlaying
	end
end)

Just tried this, and it still loops infinitely

Can you put a debug statement (like print("H")) inside the if key.KeyCode == ... part to see that the button is triggering it properly?

It does not seem to be printing anything.

Ok, your script may be held up somewhere before the InputEnded connection is made. “H” might also be getting observed internally, you can check that by changing your function to this:

UIS.InputEnded:Connect(function(key, gameProcessed)
    if(gameProcessed) then
        print("input was processed internally")
        return
    end

    if key.UserInputType == Enum.UserInputType.Keyboard then
	    print(key.KeyCode .. " has been released!")
	
		if key.KeyCode == Enum.KeyCode.H then
			HornLoop:Stop()
			print("Horn stopped!")
			HornEnd:Play()--play the end *inside* the InputEnded for it to work as intended
		end
    end
end)

Ensure that HornStart and HornEnd are not set to loop, only HornLoop should be.
*Edited to catch if InputType wasn’t a keyboard so the script wouldn’t crash

Still does not seem to work, and it also prints nothing

Ok, so the connection is never made. Somewhere prior to that in the code the script is yielding. Anywhere above it that it’s waiting for something? Another infinite task.wait() loop maybe?

Or… is this in a server-sided script? UIS has to be captured in a LocalScript.

It’s in a ModuleScript. I wasn’t sure how the game would react to having UIS in a ModuleScript, and I really can’t do anything else, so I tried it

Ok, so this should be done with Remote Events then. Have a localscript capturing UIS inside each player and have them fire an event when they begin and end pressing H. I’d recommend a single event for both and fire a boolean along with it (such that when they begin they fire “true”, when they finish they fire “false”).
Alternatively you could restructure it to be triggered by something the server can see, like a .Touched event or a button in workspace.