Have you ever wanted to create a buoy or boat or make any effect that required the height of a wave in Roblox? Unfortunately, as of now, Roblox provides now way to do this. So, I reverse-engineered Roblox’s current wave function and came up with a pretty accurate approximation, if not the exact equation.
Just a disclaimer, this may not always match the Roblox terrain waves. If Roblox changes its wave function, this script won’t work anymore.
--[[
robro786
5/18/2021
]]
--[[
NOTE:
Waves use their own clock to calculate wave height. Since that clock is private and invisible from the lua
side, a separate clock needs to be kept in this script to emulate the waves. If these clocks are desynced, the waves
will appear desynced. This means that both clocks must start at the same time, so THIS SCRIPT SHOULD BE
REQUIRED IMMEDIATELY
I recommend initially setting your wavespeed to 0 in studio, then, in a LocalScript, require this module
and then set your wavespeed to the desired values. That way, both clocks start at 0.
]]
local module = {}
local runService = game:GetService("RunService")
local terrain = game.Workspace:WaitForChild("Terrain")
-- these are roblox-set values, they aren't meant to be changed
local MAX_WAVE_MAGNITUDE = 1.94 -- studs (Terrain.WaterWaveSize is NOT measured in studs like the documentation says)
local MAX_WAVELENGTH = 85 -- studs
local function getWavelength()
local a = math.sqrt(terrain.WaterWaveSize)
return a * MAX_WAVELENGTH
end
-- approximation, catch up in case this script was required a few seconds late
local wt = time() * terrain.WaterWaveSpeed -- ideally, time() will be about 0 when this runs
runService.Stepped:Connect(function(t, dt)
wt += dt * terrain.WaterWaveSpeed
end)
function module.calcWaterHeightOffset(x, z)
if terrain.WaterWaveSize > .02 then -- don't divide by 0
local w = getWavelength()
local o = math.cos(2 * math.pi * (x + wt) / w) * math.sin(2 * z * math.pi / w)
o *= terrain.WaterWaveSize * MAX_WAVE_MAGNITUDE
return o
else
return 0
end
end
do -- make sure the clock starts before this module returns
local c = true
c = runService.Stepped:Connect(function()
c:Disconnect()
c = nil
end)
repeat wait() until not c
end
return module
The only caveat is errors in my approximation will magnify over time and distance from (0, 0). I haven’t done much extensive testing at extreme times and distances, so if anyone makes improvements or notices issues, please let me know!