I tried to do this myself but have no idea how to do this. Just curious if anyone knows how and could help. Thanks!
Local Wave Script in StarterPlayerScripts:
local Wave = require(script.Wave)
local Plane = workspace:WaitForChild("Wave"):WaitForChild("Plane")
local Settings = {
WaveLength = 90,
Direction = Vector2.new(0, 0),
Steepness = 0.6,
TimeModifier = 8,
MaxDistance = 1500,
}
local TestWave = Wave.new(Plane, Settings)
TestWave:ConnectRenderStepped()
_G.OceanWave = TestWave
Module Script | Child of Local Wave Script:
local Wave = {}
Wave.__index = Wave
local newCFrame = CFrame.new
local IdentityCFrame = newCFrame()
local EmptyVector2 = Vector2.new()
local math_noise = math.noise
local Stepped = game:GetService("RunService").RenderStepped
local Player = game:GetService("Players").LocalPlayer
local default = {
WaveLength = 85,
Gravity = 1.5,
Direction = Vector2.new(1, 0),
PushPoint = nil,
Steepness = 1,
TimeModifier = 4,
MaxDistance = 1500,
}
local function Gerstner(pos, wavelength, direction, steepness, gravity, time)
local k = (2 * math.pi) / wavelength
local a = steepness / k
local d = direction.Unit
local c = math.sqrt(gravity / k)
local f = k * d:Dot(Vector2.new(pos.X, pos.Z)) - c * time
local cosF = math.cos(f)
local dX = d.X * (a * cosF)
local dY = a * math.sin(f)
local dZ = d.Y * (a * cosF)
return Vector3.new(dX, dY, dZ)
end
local function CreateSettings(s, o)
o = o or {}
s = s or default
return {
WaveLength = s.WaveLength or o.WaveLength or default.WaveLength,
Gravity = s.Gravity or o.Gravity or default.Gravity,
Direction = s.Direction or o.Direction or default.Direction,
PushPoint = s.PushPoint or o.PushPoint or default.PushPoint,
Steepness = s.Steepness or o.Steepness or default.Steepness,
TimeModifier = s.TimeModifier or o.TimeModifier or default.TimeModifier,
MaxDistance = s.MaxDistance or o.MaxDistance or default.MaxDistance,
}
end
local function GetDirection(settings, worldPos)
if settings.PushPoint then
local target = settings.PushPoint:IsA("Attachment")
and settings.PushPoint.WorldPosition
or settings.PushPoint.Position
local dir3 = (target - worldPos).Unit
return Vector2.new(dir3.X, dir3.Z)
end
return settings.Direction
end
function Wave.new(instance, waveSettings, bones)
bones = bones or {}
for _, v in pairs(instance:GetDescendants()) do
if v:IsA("Bone") then
table.insert(bones, v)
end
end
return setmetatable({
_instance = instance,
_bones = bones,
_time = 0,
_connections = {},
_settings = CreateSettings(waveSettings),
}, Wave)
end
function Wave:Update()
local center = self._instance.Position
local settings = self._settings
for _, bone in pairs(self._bones) do
local worldPos = bone.WorldPosition
local dir = settings.Direction == EmptyVector2
and Vector2.new(math_noise(worldPos.X, worldPos.Z, 1), math_noise(worldPos.X, worldPos.Z, 0))
or GetDirection(settings, worldPos)
local dist = (worldPos - center).Magnitude
local minSteep, maxSteep = 0.1, 0.2
local steepness = dist <= 100
and (minSteep + (maxSteep - minSteep) * (dist / 100))
or settings.Steepness
bone.Transform = newCFrame(
Gerstner(worldPos, settings.WaveLength, dir, steepness, settings.Gravity, self._time)
)
end
end
function Wave:Refresh()
for _, bone in pairs(self._bones) do
bone.Transform = IdentityCFrame
end
end
function Wave:UpdateSettings(waveSettings)
self._settings = CreateSettings(waveSettings, self._settings)
end
function Wave:ConnectRenderStepped()
local conn = Stepped:Connect(function()
if not game:IsLoaded() then return end
local char = Player.Character
if not char or (char.PrimaryPart.Position - self._instance.Position).Magnitude < self._settings.MaxDistance then
self._time = (DateTime.now().UnixTimestampMillis / 1000) / self._settings.TimeModifier
self:Update()
else
self:Refresh()
end
end)
table.insert(self._connections, conn)
return conn
end
function Wave:GetHeightAtXZ(worldX, worldZ)
local settings = self._settings
local worldPos = Vector3.new(worldX, 0, worldZ)
local dir = settings.Direction == EmptyVector2
and Vector2.new(math_noise(worldX, worldZ, 1), math_noise(worldX, worldZ, 0))
or GetDirection(settings, worldPos)
local disp = Gerstner(worldPos, settings.WaveLength, dir, settings.Steepness, settings.Gravity, self._time)
return self._instance.Position.Y + disp.Y
end
function Wave:Destroy()
self._instance = nil
for _, conn in pairs(self._connections) do
pcall(function() conn:Disconnect() end)
end
self._bones = {}
self._settings = {}
end
return Wave
Boat Positioner Local Script in StarterPlayerScripts:
local RunService = game:GetService("RunService")
local wave = _G.OceanWave
local boat = workspace:WaitForChild("Boat")
local hull = boat.PrimaryPart
RunService.Heartbeat:Connect(function()
local pos = hull.Position
local height = wave:GetHeightAtXZ(pos.X, pos.Z)
boat:SetPrimaryPartCFrame(CFrame.new(pos.X, height, pos.Z))
end)
Boat Model:
Any ideas would be a big help. Thanks in advance.