Best Way To Play Water Sounds by Terrain Water?

Hey, I’m trying to play some water sounds when you’re near water to help my game’s aesthetic. I already implemented this with a water fountain in my game by using an invisible part. But, how would I do it for larger bodies of water like rivers and the ocean? I tried the invisible-part-play-sound method but it doesn’t really work since the parts are WAY bigger, and you have to get closer to the middle of the part to actually hear the sound. Extending the range and stuff isn’t an option because the sound is still going to be coming from the middle. How would I get it to play when you’re just close to it, like on the side? I was thinking of just locally playing the sounds and using Region3s but I wanna know if there’s a better way to do this

EDIT: A more in-depth explanation of what I want

The only reason why I used this simple method was because it’s for a fountain, which is something that you can easily lay 1 part over, and then just Play the sound on the server and utilize roll off modes and play with the range for a good result. Also, a fountain has a fixed middle.

Rivers and oceans don’t have a fixed middle, and i only need the sound to play when you get close to any area of the part, and not to have it just emanate from the middle.

2 Likes

The “distance to point” method you used is really nice because it’s so simple. Thankfully we can also do a “distance to line segment” method that would work great for rivers. Distance to point makes a sphere- shaped region, distance to line segment makes a capsule-shaped region.

We can find that kind of distance with maths, but it’s already been done before so let’s just google how to do it. Here’s a stackoverflow result showing how it can be done with code, and here’s a port to Lua from an earlier reply I posted.

In your case this might also work for the ocean. If players can’t actually travel on the ocean and can only be near the coast, then the coast can be made up of a bunch of line segments just like a river. Let me know if your use case makes this not work and I’ll come up with another way if I can.

EDIT: Part of what made your fountain solution nice is that you can use an invisible part to represent the center of the sphere-shaped region, so you can edit the sphere in a visual and interactive way. The same can be done with line segments! A line segment can be represented by the points at the Front and Back faces of a Part, and you can get the endpoints like so:

function partEndPoints( part )
	return 
		(part.CFrame * CFrame.new(0, 0, -part.Size.Z/2)).p, 
		(part.CFrame * CFrame.new(0, 0, part.Size.Z/2)).p
end

You could even make the width or height of the part affect the max distance from the line segment where it detects players.

1 Like

Alright, I’ll try this, thank you! What is .p by the way?

The p is a representation of the CFrame’s XYZ position. It excludes the rotation.

1 Like

Oh ok, thanks! I’ma try this method in a bit

Wait, so how would I utilize this exactly?

If I used magnitude, wouldn’t it still be the middle of the region? And your code looks very similar to just making a Region3.

How would I get the sound to play if they are anywhere near the segment and not just toward the middle?

Why can’t you just input water audio?

Also, why extend the part, can you duplicate it and spread it across the river/lake?

What? I don’t understand.

30 chars

For the water, you said you needed to do the invisible-part-play. So, luckily you still hear the sound. So you can just duplicate and spread it across?

I already have, there’s a bunch of invisible parts that I’ve laid on the river and the coast of my ocean

I see, my bad. Didn’t know you have.

No, that wouldnt work.

The only reason why I used this method was because it’s for a fountain, which is something that you can easily lay 1 part over, and then just Play the sound on the server and utilize roll off modes and play with the range for a good result. Also, a fountain has a fixed middle.

Rivers and oceans don’t have a fixed middle, and i only need the sound to play when you get close to any area of the part, and not to have it just emanate from the middle.

Ah, alright. Sorry for wasting time. Just thought I can help. :slight_smile:

Its fine, thanks for your input :+1:

To find the distance from one point to another, you do
function distanceToPoint( p1, p2 )
	return (p2 - p1).Magnitude
end

So that’s what it means to “use magnitude”. If you compare this distance to a fixed number, like 5, you’ll get whether or not one point is inside a sphere of radius 5 within the other, because the definition of a sphere of radius 5 is every point that is 5 or less away from the center.

In other words, a function that checks if a point is inside a sphere:

function isInSphere( sphereCenter, sphereRadius, point )
	return distanceToPoint( sphereCenter, point ) <= sphereRadius
end
How I'd do it
local coastAmbiance = --whatever sound object you want
local coastAmbianceRegions = game.Workspace:WaitForChild("CoastAmbianceRegions") --some folder containing Parts that represent regions of space

function isInSphereRegion( point, regionPart )
	local center = regionPart.Position
	--if the part shape is set to sphere, the smallest size component determines the visual size
	local radius = math.min(regionPart.Size.X, regionPart.Size.Y, regionPart.Size.Z)

	return (point - center).Magnitude <= radius
end

function isInCapsuleRegion( point, regionPart )
	local point1, point2 = 
		(regionPart.CFrame * CFrame.new(0, 0, regionPart.Size.Z * 0.5)).p,
		(regionPart.CFrame * CFrame.new(0, 0, -regionPart.Size.Z * 0.5)).p

	--Don't use the Z size, as it's being used to define the line segment
	local radius = math.min(regionPart.Size.X, regionPart.Size.Y)

	return distanceToLineSegment(point, point1, point2) <= radius
end

function isInRegion( point, region )
	if region.RegionType.Value == "Sphere" then
		return isInSphereRegion(point, region)
	elseif region.RegionType.Value == "Capsule" then
		return isInCapsuleRegion(point, region)
	else
		warn("Unknown RegionType in " .. region:GetFullName())
	end
end

function isInAnyRegion( point, regions )
	for _, region in pairs(regions) do
		if isInRegion(point, region) then
			return true
		end
	end

	return false
end

while wait(1) do
	local character = player.Character
	if character and isInAnyRegion(character.HumanoidRootPart.Positiion, coastAmbianceRegions) then
		if not coastAmbiance.Playing then
			coastAmbiance:Play()
		end
	else
		if coastAmbiance.Playing then
			coastAmbiance:Stop()
		end
	end
end
3 Likes