Questions on voice chat range, volume

I’ve seen previous DevForum posts here asking questions on changing the voice chat range, however, all of them really have no responses. I am wondering how I could change the range of voice chat and also disable the ability to hear voice chat through walls and smooth terrain. I heard some suggestions about using RollOffMaxDistance - but this does not work.

Sorry to bump this post but I wanted to answer it incase anyone else wants to know.

If you are using the Audio API then all audio from the mic will have to wired up to an audio emitter which you would place inside the player character. You can then use the SetDistanceAttenuation() method to adjust the range of the emitter:
For example:

local emitter = script.Parent:FindFirstChild("VoiceEmitter")
if not emitter then return end

local MIN_DISTANCE = 7 
local MAX_DISTANCE = 150
local CURVE_STEP_SIZE = 2

local voiceCurve = {}
for i = MIN_DISTANCE, MAX_DISTANCE, CURVE_STEP_SIZE do
	voiceCurve[i] = ((i - MIN_DISTANCE) - (MAX_DISTANCE - MIN_DISTANCE))^2 / (MAX_DISTANCE - MIN_DISTANCE)^2
end
voiceCurve[MAX_DISTANCE] = 0

emitter:SetDistanceAttenuation(voiceCurve)

SetDistanceAttenuation takes a table which coresponds to what the volume of the audio emitter should from a given number of studs. You can read about it in AudioEmitter | Documentation - Roblox Creator Hub.

As for disabling voice chat through walls, you can achieve this by raycasting from each player to the camera and checking if a part is detected. I have done something similar before but with audio emitters inside the workspace:

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

local player = Players.LocalPlayer
local cachedParts = {}  -- Array to store parts with MuffledEffects folder

-- Function to check if a part has a MuffledEffects folder and an AudioEmitter
local function cacheMuffledParts()
	cachedParts = {}  -- Reset the cached parts list
	for _, part in workspace:GetDescendants() do
		if part:IsA("BasePart") then 
			local muffleFolder = part:FindFirstChild("MuffledEffects")
			local emitter = part:FindFirstChildWhichIsA("AudioEmitter")

			if muffleFolder and emitter then
				table.insert(cachedParts, part)  -- Add to cached array
			end
		end
	end
end

-- Call this function once at the start to cache the parts
cacheMuffledParts()

-- Occlusion check with debug prints
local function isOccluded(emitterPos, playerPos, ignoreList)
	local rayParams = RaycastParams.new()
	rayParams.FilterDescendantsInstances = ignoreList
	rayParams.FilterType = Enum.RaycastFilterType.Exclude

	-- Raycasting from emitter position to player position
	local direction = playerPos - emitterPos
	local result = workspace:Raycast(emitterPos, direction, rayParams)

	if result then
		return true  -- Something was hit
	else
		return false  -- Nothing was hit
	end
end

-- Continuously check the cached parts
RunService.Heartbeat:Connect(function()
	local char = player.Character
	if not char then return end
	local head = char:FindFirstChild("Head")
	if not head then return end
	local playerPos = head.Position

	-- Loop through cached parts only
	for _, part in ipairs(cachedParts) do
		local muffleFolder = part:FindFirstChild("MuffledEffects")
		local emitter = part:FindFirstChildWhichIsA("AudioEmitter")

		if muffleFolder and emitter then
			-- Check if there's an obstruction in the raycast
			local blocked = isOccluded(part.Position, playerPos, {char})

			-- Set the bypass to false if blocked, true if not blocked
			for _, effect in ipairs(muffleFolder:GetChildren()) do
				effect.Bypass = not blocked  -- If blocked, Bypass = false, otherwise Bypass = true
			end
		end
	end
end)

-- Listen for new parts added to the workspace with MuffledEffects
workspace.DescendantAdded:Connect(function(descendant)
	if descendant:IsA("BasePart") then
		local muffleFolder = descendant:FindFirstChild("MuffledEffects")
		local emitter = descendant:FindFirstChildWhichIsA("AudioEmitter")

		if muffleFolder and emitter then
			table.insert(cachedParts, descendant)  -- Add to cached array
		end
	end
end)

-- Listen for parts being removed from the workspace
workspace.DescendantRemoving:Connect(function(descendant)
	if descendant:IsA("BasePart") then
		local index = table.find(cachedParts, descendant)
		if index then
			table.remove(cachedParts, index)  -- Remove from cached array
		end
	end
end)

This script checks to see if a part in the workspace has a specific folder called MuffledEffects and disables or enables the bypass on any audio filters based on the ray to create a “muffled” version. One issue with this however is that even a small box in the way of the player with trigger the muffle so using multiple rays at different parts of the player or by filtering out small parts using tags/ groups could make this system feel more realistic.

Hope this helped someone.

1 Like

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