Thoughts of this module

Please reviews my modulescript that i have just made recently.

If youre lazy to open the link
--!optimize 2
--!native
local Surround = {}

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

local scheduler = require(ReplicatedStorage:WaitForChild("scheduler"))

local begin_profiler, end_profiler = scheduler.profiler.Begin, scheduler.profiler.End
local Player: Player = game:GetService("Players").LocalPlayer

type Object = {
	root: BasePart,
	sound: Sound,
	baseVolume: number,
	max_distance: number,
}

local default_max_distance: number = 15
local throttle: number = 1 / 24
local last_throttle: number = 0
local objects: { Object } = {}
local queued: { Object } = {}

Surround.addObject = function(_object: BasePart, sound: Sound, baseVolume: number?, max_distance: number?)
	if not _object or not sound then
		return
	end
	local object: BasePart = _object
	if _object:IsA("Model") and _object.PrimaryPart then
		object = _object.PrimaryPart
	end
	if not object:IsA("BasePart") then
		return
	end
	table.insert(objects, {
		root = object,
		sound = sound,
		baseVolume = baseVolume or sound.Volume,
		max_distance = max_distance or default_max_distance,
	})
end

Surround.removeObject = function(_object: BasePart, sound: Sound)
	if not _object or not sound then
		return
	end
	local object: BasePart = _object
	if _object:IsA("Model") and _object.PrimaryPart then
		object = _object.PrimaryPart
	end

	for idx: number, obj: Object in objects do
		if obj.root == object and obj.sound == sound then
			table.remove(objects, idx)
			return
		end
	end
end

RunService.PreRender:Connect(function(dt: number)
	if last_throttle <= throttle then
		last_throttle += dt
		return
	end
	last_throttle = 0
	local characterRoot: BasePart? = Player.Character and Player.Character.PrimaryPart
	if not characterRoot then
		return
	end
	begin_profiler("Surround.simulate")
	local characterPos: Vector3 = characterRoot.Position
	if next(queued) then
		local sounds: { [Sound]: number } = {}
		for _, object: Object in queued do
			local pos: Vector3 = object.root.Position
			local distance: number = (pos - characterPos).Magnitude
			local volume: number =
				math.clamp(object.baseVolume * (1 - distance / object.max_distance), 0, object.baseVolume)
			local highest_volume: number = sounds[object.sound] or -1
			if volume <= highest_volume then
				continue
			end
			sounds[object.sound] = volume
		end
		table.clear(queued)
		for sound: Sound, volume: number in sounds do
			sound.Volume = volume
		end
	else
		for idx: number, object: Object in objects do
			if not object.root or not object.sound or not object.root.Parent then
				objects[idx] = nil
				continue
			end
			if not object.root:IsDescendantOf(workspace) then
				continue
			end
			table.insert(queued, object)
		end
	end
	end_profiler("Surround.simulate")
end)

return Surround :: typeof(Surround)

So basically, it dynamically adjusts sound volumes based on the local player PrimaryPart proximity to multiple potential sound sources. Its specifically designed to solve some issue such like: if you want several objects (e.g., multiple campfires) to use a single Sound instance to prevent phasing/layering/glitchy issues, which cant be always solved by Emitter property on sound instance at all. So this manually calculates the distance to the closest source object and applies the volume based on the calculated distance.
about the performance it use throttle and queue system to reduce the computational loads.

I need your feedbacks.

3 Likes

hm no feedbacks? ㅠㅠ

Well if it works then it’s awesome. I don’t know much about these, but the script seems to be not too cramped and its overral appearance looks clean

1 Like