How do I improve my buoyancy simulation?

In my script, I have a system that attaches BodyForce instances to parts below the height of the water’s surface. This is done to replicate buoyancy. How can I improve my simulation?
Some issues I’ve noticed already are that the simulation seems to move too fast, and it slows down the server significantly when there are too many parts.
Here’s the FlashFlood script containing the buoyancy code:

local BaseDisaster = require(script.Parent.BaseDisaster)

local FlashFlood = BaseDisaster:Create()
FlashFlood.Name = "Flash Flood"
FlashFlood.Hint = "You'd better hope there's a high-up place around here!"
FlashFlood.Duration = 5
FlashFlood.PreparationTime = 10
FlashFlood.Finished = false

FlashFlood.BodyMovers = {}

function FlashFlood:Prepare()
	FlashFlood.Finished = false
	
	local waterPart = Instance.new("Part")
	waterPart.Size = Vector3.new(400, 400, 400)
	waterPart.CFrame = CFrame.new(0, -201, 0)
	waterPart.Anchored = true
	waterPart.CanCollide = false
	waterPart.CastShadow = false
	waterPart.BrickColor = BrickColor.new("Deep blue")
	waterPart.Transparency = 0.5
	waterPart.Reflectance = 1
	waterPart.Material = Enum.Material.SmoothPlastic
	waterPart.Parent = workspace.Disaster
	
	local waterTexture = Instance.new("Texture")
	waterTexture.Texture = "rbxassetid://14034336356"
	waterTexture.Transparency = 0.5
	waterTexture.Face = Enum.NormalId.Top
	waterTexture.StudsPerTileU, waterTexture.StudsPerTileV = 12, 12
	waterTexture.Parent = waterPart
	
	self.WaterPart = waterPart
end

function FlashFlood:Start()
	local waterPart = self.WaterPart
	
	while waterPart.Position.Y < -176 do
		waterPart.CFrame *= CFrame.new(0, 1 / 60, 0)
		
		local waterTexture = waterPart.Texture
		waterTexture.OffsetStudsU += 1 / 60
		waterTexture.OffsetStudsV += 1 / 60
		
		for _, v in workspace:GetChildren() do
			if v:IsA("Model") then
				local rootPart = v:FindFirstChild("Head")
				
				if rootPart then
					if rootPart.Position.Y < waterPart.Position.Y + 200 then
						self:DamageCharacter(v, 0.5)
					end
				end
			end
		end
		
		for _, v in workspace.Map:GetDescendants() do
			if v:IsA("BasePart") then
				if v.Anchored == false then
					if v.Position.Y < waterPart.Position.Y + 200 then
						local weld = v:FindFirstChildOfClass("WeldConstraint") or 
							v:FindFirstChildOfClass("Weld") or 
							v:FindFirstChildOfClass("Snap")

						if weld then
							weld:Destroy()
						end
						
						if not self.BodyMovers[v] then
							local volume = v.Size.X * v.Size.Y * v.Size.Z
							local waterDensity = 50
							local gravity = workspace.Gravity
							local force = volume * waterDensity * gravity
							
							--[[local bodyVelocity = Instance.new("BodyVelocity")
							bodyVelocity.Velocity = Vector3.new(0, 0.5, 0) * v.Mass
							bodyVelocity.P = 1000
							bodyVelocity.Parent = v
							
							self.BodyMovers[v] = bodyVelocity]]
							
							local bodyForce = Instance.new("BodyForce")
							bodyForce.Force = Vector3.new(0, force / 60, 0)
							bodyForce.Parent = v
							
							self.BodyMovers[v] = bodyForce
						end
					else
						if self.BodyMovers[v] then
							self.BodyMovers[v]:Destroy()
							self.BodyMovers[v] = nil
						end
					end
				end
			end
		end
		
		wait()
	end 
	
	self.Finished = true
end

function FlashFlood:CleanUp()
	for part, bodyMover in self.BodyMovers do
		self.BodyMovers[part]:Destroy()
		self.BodyMovers[part] = nil
		
		part.AssemblyLinearVelocity = Vector3.zero
	end
	
	local waterPart = self.WaterPart
	
	while waterPart.Position.Y > -200 do
		waterPart.CFrame *= CFrame.new(0, -0.25, 0)
		wait()
	end
	
	waterPart:Destroy()
	self.WaterPart = nil
	
	self:Reset()
end

return FlashFlood

Have you tried using

obj:ApplyImpulseAtPosition(a: Vector3, b: Vector3)

instead?

Yeah, it just wasn’t as good at faking buoyancy as BodyForce/BodyVelocity are.

1 Like

do u know how to make the character swim

I don’t know what that has to do with this thread. This isn’t about swimming.

Help

Did you use like a RunService function? If you did try using RunService:PreSimulation() it works pretty smooth compared to RunService:Stepped()

But just incase you havent tried RunService:Stepped(), try adding the deltaTime variable thingy to smoothen sum stuff out

Sorry for the late response (I haven’t been checking DevForum lately), but I can’t use simulation events because of the way my disaster script works. The simulation code for each disaster runs in a loop 60 times per second for as long as the disaster lasts for. I can’t connect to events or bind to callbacks or anything like that.