Playing sounds with proximity prompts

Right now I have a local script in StarterPlayerScripts and a script in ServerScriptService. I have one remote event in replicatedStorage called PlaySoundInteraction. I have three parts in workspace. Each part has a proximity prompt and a sound parented to the part. I am trying to make it where when a player interacts with any of the three proximity prompts, the PlaySoundInteraction will be fired. It will be fired and connected to the script in ServerScriptService. It will then initialize a function in that script which basically plays the sound in the part that had the proximity prompt that was interacted with.

What is the error?
Sound is not playing when player interacts with anyone of the proximity prompts.

Here is what I have have right now but does not exactly work.

Local script in StarterPlayerScripts.

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local EnableInteraction = ReplicatedStorage:WaitForChild("EnableInteraction")

local function onPromptTriggered(prompt)
	local part = prompt.Parent
		EnableInteraction:FireServer(part.Name)
	end

-- Connect to the ProximityPrompt's Triggered event
local lockerPrompt = game.Workspace.LockerInteract.ProximityPrompt
lockerPrompt.Triggered:Connect(onPromptTriggered)

local garbageBagPrompt = workspace.GarbageBagInteract.ProximityPrompt
garbageBagPrompt.Triggered:Connect(onPromptTriggered)

local ventPrompt = workspace.VentInteract.ProximityPrompt
ventPrompt.Triggered:Connect(onPromptTriggered)

Script in ServerScriptService.

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local EnableInteraction = ReplicatedStorage:WaitForChild("EnableInteraction")

local function initializeInteraction(player, partName)
	local part = workspace:FindFirstChild(partName)
	local sound = part:FindFirstChild("Sound")
	
	if sound and sound:IsA("Sound") then
		sound:Play()
	end
end

-- Listen to the remote event
EnableInteraction.OnServerEvent:Connect(initializeInteraction)

And then of course I have a remoteFunction in ReplicatedStorage. Thank you.

3 Likes

Does it reach the sound:Play() on the server?

1 Like

I want to mention something about this. You do not need to use a local script to detect ProximityPrompt triggers, as everything can be done on the server. Hence, a remote event will not be used for this (if you want the sound to play for everyone in the server, use a Server Script, but if you want it to play only for the person who triggered it, use a Local Script).

Now there are 2 ways to go about this-

Method 1: Get every ProximityPrompt instance in its own variable and call the Triggered function for each variable to play the sound.

Method 1 Script:

local lockerPrompt = workspace:WaitForChild("LockerInteract"):WaitForChild("ProximityPrompt")
local garbageBagPrompt = workspace:WaitForChild("GarbageBagInteract"):WaitForChild("ProximityPrompt")
local ventPrompt = workspace:WaitForChild("VentInteract"):WaitForChild("ProximityPrompt")

local function playSound(prompt, plr)
	local sound = prompt.Parent:FindFirstChild("Sound")

	if sound and sound:IsA("Sound") then
		sound:Play()
	end
end

lockerPrompt.Triggered:Connect(function(plr)
	playSound(lockerPrompt, plr)
end)

garbageBagPrompt.Triggered:Connect(function(plr)
	playSound(garbageBagPrompt, plr)
end)

ventPrompt.Triggered:Connect(function(plr)
	playSound(ventPrompt, plr)
end)

Method 2: Use ProximityPromptService to detect any ProximityPrompt triggers. To detect specific ProximityPrompts, it’s best to have a different name for each one, but in your case, we can just check the parent’s name.

Method 2 Script:

local ProximityPromptService = game:GetService("ProximityPromptService")
local names = {
	"LockerInteract",
	"GarbageBagInteract",
	"VentInteract"
}

local function playSound(prompt, plr)
	local sound = prompt.Parent:FindFirstChild("Sound")
	
	if sound and sound:IsA("Sound") then
		sound:Play()
	end
end

ProximityPromptService.PromptTriggered:Connect(function(prompt, plr) -- 'prompt' is the ProximityPrompt that was triggered
	for _, promptNames in pairs(names) do -- loops through the list of the parent names
		if prompt.Parent.Name == promptNames then -- checks if any one of them matches the parent of the prompt that was triggered
			playSound(prompt, plr)
		end
	end
end)

If you were to ask, I’d suggest using the second method, as I feel it’s a little more organized than the first method, though people do have their own preference.

3 Likes

Hi I appreciate the response. I was planning on using a local script because I was eventually going to code it where if any of the proximity prompts are interacted with, then the player who interacted with the proximity prompt would get their screen locked to the part parent of the prompt using CameraSubject. Thats why I made a local script and connected it to a script in ServerScriptService. Because I wanted some things like the Camera lock to be local and other things like the sounds because played to be server sided.

1 Like

Nope. Also I forgot to mention that I get an error.

You shouldn’t pass instances (client-owned ones) as arguments in FireServer as they do not replicate.

1 Like

I’ve looked through your LocalScript and found that you are passing the Player object’s parent’s name rather than the part’s name. This is because the first and only parameter of ProximityPrompt.Triggered is the player who triggered it, and you were considering that the player object is the prompt object.

Code lines indicating the issue:

lockerPrompt.Triggered:Connect(onPromptTriggered) -- the first parameter is the player object
local function onPromptTriggered(prompt) -- you are referring the player object as 'prompt'
	local part = prompt.Parent -- the player object's parent (which is technically 'Players' or 'PlayerService'
	EnableInteraction:FireServer(part.Name) -- passes the name of the player object's parent
end

Try one of my methods in my previous post as it should fix your issue. Now since you plan to make some client changes only, you can use the same RemoteEvent to fire it to the client using FireClient, and make the necessary changes in the LocalScript.

Updated Server Script (using Method 2):

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ProximityPromptService = game:GetService("ProximityPromptService")

local EnableInteraction = ReplicatedStorage:WaitForChild("EnableInteraction")
local names = {
	"LockerInteract",
	"GarbageBagInteract",
	"VentInteract"
}

local function playSound(prompt, plr)
	local sound = prompt.Parent:FindFirstChild("Sound")
	
	if sound and sound:IsA("Sound") then
		sound:Play()
	end
end

ProximityPromptService.PromptTriggered:Connect(function(prompt, plr) -- 'prompt' is the ProximityPrompt that was triggered
	for _, promptNames in pairs(names) do
		if prompt.Parent.Name == promptNames then

			playSound(prompt, plr)
			EnableInteraction:FireClient(plr, prompt.Parent) -- fires the remote event
		end
	end
end)

Then in your Local Script:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local EnableInteraction = ReplicatedStorage:WaitForChild("EnableInteraction")
local player = Players.LocalPlayer

EnabledInteraction.OnClientEvent:Connect(function(part) -- 'part' is the parent of the ProximityPrompt' that was triggered
	-- client changes
end)
1 Like

You can’t detect ProximityPrompt triggers on LocalScripts anyway.

1 Like

Incorrect, you are still able to detect ProximityPrompt triggers in a LocalScript, as long as the LocalScript is not a descendant of Workspace.

1 Like

I really appreciate your help because I have been struggling on this for a while. Your code that you provided me did the trick. Although I just have one more problem if you dont mind helping.

Now I have coded it where once a player interacts with one of the three proximity prompts, the players screen will be locked to the proximity prompt part parent, a TextButton will be cloned to the players screen and the proximity prompt will be disabled. Once the TextButton is clicked, the players camera will be reverted back to normal. But I am also trying to make it where when the TextButton is clicked, the proximity prompt will be reenabled. I am not exactly sure how to do this or if it requires a second remoteEvent in order to do this.

Here are both scripts.

Server Script:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ProximityPromptService = game:GetService("ProximityPromptService")

local EnableInteraction = ReplicatedStorage:WaitForChild("EnableInteraction")
local DisableInteraction = ReplicatedStorage:WaitForChild("DisableInteraction")

local names = {
	"LockerInteract",
	"GarbageBagInteract",
	"VentInteract"
}

local function playSound(prompt, plr)
	local sound = prompt.Parent:FindFirstChild("Sound")

	if sound and sound:IsA("Sound") then
		prompt.Enabled = false
		sound:Play()
	end
end

ProximityPromptService.PromptTriggered:Connect(function(prompt, plr) -- 'prompt' is the ProximityPrompt that was triggered
	for _, promptNames in pairs(names) do
		if prompt.Parent.Name == promptNames then

			playSound(prompt, plr)
			EnableInteraction:FireClient(plr, prompt.Parent) -- fires the remote event
		end
	end
end)

And the Local Script:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local camera = workspace.CurrentCamera

local EnableInteraction = ReplicatedStorage:WaitForChild("EnableInteraction")
local DisableInteraction = ReplicatedStorage:WaitForChild("DisableInteraction")
local player = Players.LocalPlayer

camera.CameraType = Enum.CameraType.Scriptable

local function RevertCamera()
	player.PlayerGui.ScreenGui.TextButton.LocalScript.Disabled = false
	player.CameraMode = Enum.CameraMode.LockFirstPerson
	camera.CameraSubject = player.Character.Humanoid
	camera.CameraType = Enum.CameraType.Custom
end

EnableInteraction.OnClientEvent:Connect(function(part) -- 'part' is the parent of the ProximityPrompt' that was triggered
	local exitButton = ReplicatedStorage:WaitForChild("ExitButton"):Clone()
	exitButton.Parent = player.PlayerGui
	
	exitButton.TextButton.MouseButton1Click:Connect(function()
		exitButton:Destroy()
		RevertCamera()
	end)
	
	player.CameraMode = Enum.CameraMode.LockFirstPerson
	camera.CameraSubject = part
end)

The code you provided seems to be mostly correct. However, there is one issue that could be causing the sound not to play when the player interacts with the proximity prompts.

In the local script, you are using EnableInteraction:FireServer(part.Name) to send the part name to the server script. However, in the server script, you are using workspace:FindFirstChild(partName) to find the part based on the name. This may not work as expected because the FindFirstChild method searches for direct children of the workspace, not the entire workspace hierarchy.

local EnableInteraction = ReplicatedStorage:WaitForChild("EnableInteraction")

local function initializeInteraction(player, partName)
	local part = game.Workspace:FindFirstChild(partName, true) -- Use 'true' to search the entire workspace hierarchy
	
	if part then
		local sound = part:FindFirstChild("Sound")
		if sound and sound:IsA("Sound") then
			sound:Play()
		end
	end
end

-- Listen to the remote event
EnableInteraction.OnServerEvent:Connect(initializeInteraction)

You will need to fire a RemoteEvent back to the server to reenable the ProximityPrompt for everyone. You won’t need to create another RemoteEvent for this.

Side note: I have added a few security measures for this system to ensure exploiters don’t get an advantage over it.

Updated Local Script:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local camera = workspace.CurrentCamera

local EnableInteraction = ReplicatedStorage:WaitForChild("EnableInteraction")
local DisableInteraction = ReplicatedStorage:WaitForChild("DisableInteraction")
local player = Players.LocalPlayer

camera.CameraType = Enum.CameraType.Scriptable

local function RevertCamera()
	player.PlayerGui.ScreenGui.TextButton.LocalScript.Disabled = false
	player.CameraMode = Enum.CameraMode.LockFirstPerson
	camera.CameraSubject = player.Character.Humanoid
	camera.CameraType = Enum.CameraType.Custom
end

EnableInteraction.OnClientEvent:Connect(function(part) -- 'part' is the parent of the ProximityPrompt' that was triggered
	local exitButton = ReplicatedStorage:WaitForChild("ExitButton"):Clone()
	exitButton.Parent = player.PlayerGui
	
	exitButton.TextButton.MouseButton1Click:Connect(function()
		exitButton:Destroy()
		RevertCamera()

		EnableInteraction:FireServer(part.Name, "EnablePrompt") -- fires the same remote event back to the server
	end)
	
	player.CameraMode = Enum.CameraMode.LockFirstPerson
	camera.CameraSubject = part
end)

Updated Server Script:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ProximityPromptService = game:GetService("ProximityPromptService")

local EnableInteraction = ReplicatedStorage:WaitForChild("EnableInteraction")
local DisableInteraction = ReplicatedStorage:WaitForChild("DisableInteraction")

local names = {
	"LockerInteract",
	"GarbageBagInteract",
	"VentInteract"
}

local function playSound(prompt, plr)
	local sound = prompt.Parent:FindFirstChild("Sound")

	if sound and sound:IsA("Sound") then
		prompt.Enabled = false
		sound:Play()
	end
end

ProximityPromptService.PromptTriggered:Connect(function(prompt, plr) -- 'prompt' is the ProximityPrompt that was triggered
	for _, promptNames in pairs(names) do
		if prompt.Parent.Name == promptNames then
	
			playSound(prompt, plr)
			
			local curPlrValue = prompt.Parent:FindFirstChild("CurrentPlayer")
			if not curPlrValue then
				local plrValue = Instance.new("ObjectValue")
				plrValue.Name = "CurrentPlayer"
				plrValue.Value = plr
				plrValue.Parent = prompt.Parent
			else
				curPlrValue.Value = plr
			end

			EnableInteraction:FireClient(plr, prompt.Parent) -- fires the remote event
		end
	end
end)

-- reenable ProximityPrompt
EnableInteraction.OnServerEvent:Connect(function(plr, partName, stringMsg)
	local mainPart = workspace:FindFirstChild(partName)

	if mainPart and stringMsg == "EnablePrompt" then

		local prompt = mainPart:FindFirstChild("ProximityPrompt")
		local curPlrValue = mainPart:FindFirstChild("CurrentPlayer")

		if prompt and curPlrValue and 
			prompt:IsA("ProximityPrompt") and curPlrValue.Value == plr
		then
			prompt.Enabled = true
		end
	end
end)
2 Likes

It works perfectly, thank you so much.

1 Like

You’re welcome, happy to help!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.