Is there a way to make LinearVelocity position you?

Basically when I jump into the water in my game, I want to rise back up without actually moving in that general direction.

Here’s how it works currently:

Here’s my code if it helps:

local SwimForce = Instance.new("LinearVelocity")
local SwimAttachment = Instance.new("Attachment")
local wPressed = false
local RunService = game:GetService("RunService")
local SwimSpeed = 0
local MaxSwimSpeed = 20
local Swimming = false
local Player = game.Players.LocalPlayer

local Root = script.Parent:WaitForChild("HumanoidRootPart")
local Hum = script.Parent:WaitForChild("Humanoid")

SwimAttachment.Orientation = Root.Orientation

SwimAttachment.Parent = Root
SwimForce.Attachment0 = SwimAttachment
SwimForce.RelativeTo = Enum.ActuatorRelativeTo.World
SwimForce.Parent = Root
SwimForce.Enabled = false
SwimForce.MaxForce = math.huge

local TS = game:GetService("TweenService")
local SwimForceInfo = TweenInfo.new(1, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut, 0, false, 0)

local UIS = game:GetService("UserInputService")
UIS.InputBegan:Connect(function(input, processed)
	if not processed then
		if input.KeyCode == Enum.KeyCode.W then
			wPressed = true
		end
	end
end)
UIS.InputEnded:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.W then
		wPressed = false
	end
end)
RunService.Stepped:Connect(function()
	if Root.Position.Y < workspace.Ocean.Position.Y then
		Swimming = true
	end
	if Swimming then
		SwimForce.Enabled = true
		if wPressed then
			local Direction = (Root.CFrame.LookVector*SwimSpeed)*Vector3.new(1,0,1)
			--local Track = TS:Create(SwimForce, SwimForceInfo, {VectorVelocity = Direction}):Play()
			SwimForce.VectorVelocity = Direction
			if SwimSpeed < MaxSwimSpeed then
				SwimSpeed += 1
			end
		else
			SwimForce.VectorVelocity = Vector3.new()
		end
	end
end)
1 Like

The velocity should move the character by itself. I see you set the enabled state to false. This may be why you’re not moving. Another reason could be that the force is too small, or unable to counteract gravity. You may need to increase the force when you’re trying to move a player upwards against gravity.

I don’t think you read my post at all, do you think maybe you could re read it so you can understand it properly? I’m not saying that I can’t move i’m simply just asking if there’s a way to position the character at the y axis of the ocean.

There’s no simple or built-in way.

You’ll have to write control logic that makes a VectorForce or LinearVelocity do the right thing instead. The problem with writing your own physics logic is that AFAIK there’s no way to update it as fast as or in lock-step with the built-in physics engine, so there’s always going to be a bit of jank. It’s definitely possible to get some nice systems though, especially for something like this where you don’t need it to be perfect from frame to frame.

It would really be more natural to use a VectorForce or LineForce, since we’re working with the buoyant force, but a LinearVelocity is fine too. The LineVelocity should just always be higher than the part’s actual velocity, otherwise the buoyant force will work to slow the part down, instead of speeding it up. That way we can just set the MaxForce to be the actual force we want.

You’ll want to move the body upwards when it’s submerged, and stop once it reaches the surface. IRL this happens because the buoyant force is proportional to the submerged volume, which is 0 when out of the water and equals the volume of the object when it’s fully submerged, and some fraction of that when it’s partly submerged.

For starters let’s just pretend like it’s only ever fully submerged or not at all submerged, and use a LinearVelocity to just apply a force when a body is submerged.


local TagS = game:GetService("CollectionService")
local RunS = game:GetService("RunService")

local WATER_DENSITY = 3

function submergedBuoyantForce(part: BasePart): number
	local displacedVolume = part.Size.X * part.Size.Y * part.Size.Z
	local partDensity = (part.CustomPhysicalProperties or PhysicalProperties.new(part.Material)).Density
	local gravity = -game.Workspace.Gravity
	
	return gravity * displacedVolume * (partDensity - WATER_DENSITY)
end

function setupBuoyantPart(part: BasePart)
	local buoyancyAtt = Instance.new("Attachment")
	buoyancyAtt.Parent = part
	
	local lineVel = Instance.new("LinearVelocity")
	lineVel.Parent = part
	lineVel.RelativeTo = Enum.ActuatorRelativeTo.World
	lineVel.VelocityConstraintMode = Enum.VelocityConstraintMode.Line
	lineVel.LineDirection = Vector3.yAxis
	lineVel.Attachment0 = buoyancyAtt
	
	RunS.Heartbeat:Connect(function()
		local y = part.Position.Y
		if y < 0 then
			lineVel.LineVelocity = 1000
			lineVel.MaxForce = submergedBuoyantForce(part)
			print(lineVel.MaxForce)
		else
			lineVel.LineVelocity = 0
			lineVel.MaxForce = 0
		end
	end)
end

for _, tagged in ipairs(TagS:GetTagged("BuoyantPart")) do
	setupBuoyantPart(tagged)
end
TagS:GetInstanceAddedSignal("BuoyantPart"):Connect(setupBuoyantPart)

buoy1

It works, but has some issues. The bodies are oscillating violently instead of coming to rest at some partly- submerged depth. This probably has to do with the “all-or-nothing” submerged volume approach, but no matter what there also needs to be some dampening that causes the bodies to lose kinetic energy as they move through the viscous water, i.e. drag.

Drag is a force that opposes movement, and is proportional to the body’s coefficient of drag * it’s cross-sectional area in the direction of movement, which is complicated to determine because it depends on the body’s rotation and shape and stuff. Let’s just say it’s proportional to the volume, that way at least bigger parts have a bigger C_d. Of course drag also depends on the fluid the body is moving in, so it’s going to be much bigger in the water than in air. Let’s just say it’s 0 in air to simplify things:

local TagS = game:GetService("CollectionService")
local RunS = game:GetService("RunService")

local WATER_DENSITY = 1.5
local WATER_DRAG_DENSITY = 1 --Could be same as WATER_DENSITY, but it's nice to tweak buoyancy and drag separately

function submergedBuoyantForce(part: BasePart): number
	local displacedVolume = part.Size.X * part.Size.Y * part.Size.Z
	local partDensity = (part.CustomPhysicalProperties or PhysicalProperties.new(part.Material)).Density
	local gravity = -game.Workspace.Gravity
	
	return gravity * displacedVolume * (partDensity - WATER_DENSITY)
end

function submergedDragForce(part: BasePart): Vector3
	local velocity = part.AssemblyLinearVelocity
	local dragCoefficientTimesReferenceArea = part.Size.X * part.Size.Y * part.Size.Z
	return -velocity * dragCoefficientTimesReferenceArea * WATER_DRAG_DENSITY
end

function setupBuoyantPart(part: BasePart)
	local buoyancyAtt = Instance.new("Attachment")
	buoyancyAtt.Parent = part
	
	local buoyancyVel = Instance.new("LinearVelocity")
	buoyancyVel.Parent = part
	buoyancyVel.RelativeTo = Enum.ActuatorRelativeTo.World
	buoyancyVel.VelocityConstraintMode = Enum.VelocityConstraintMode.Line
	buoyancyVel.LineDirection = Vector3.yAxis
	buoyancyVel.Attachment0 = buoyancyAtt
	
	local dragVectorForce = Instance.new("VectorForce")
	dragVectorForce.Parent = part
	dragVectorForce.RelativeTo = Enum.ActuatorRelativeTo.World
	dragVectorForce.Attachment0 = buoyancyAtt
	
	RunS.Heartbeat:Connect(function()
		local y = part.Position.Y
		if y < 0 then
			buoyancyVel.LineVelocity = 1000
			buoyancyVel.MaxForce = submergedBuoyantForce(part)
			dragVectorForce.Force = submergedDragForce(part)
		else
			buoyancyVel.LineVelocity = 0
			buoyancyVel.MaxForce = 0
			dragVectorForce.Force = Vector3.zero
		end
	end)
end

for _, tagged in ipairs(TagS:GetTagged("BuoyantPart")) do
	setupBuoyantPart(tagged)
end
TagS:GetInstanceAddedSignal("BuoyantPart"):Connect(setupBuoyantPart)

buoy1

Lot’s better, the bodies are now bobbing up and down a bit more like we’d expect, although it’s still not great. As soon as it’s center goes above the waterline, the buoyant force becomes 0 and it starts “falling” rapidly, making the bobbing a bit too violent still. This time I really think it’s because of the wrong submerged volume calculation, so let’s fix that:


local TagS = game:GetService("CollectionService")
local RunS = game:GetService("RunService")

local WATER_DENSITY = 2.25
local WATER_DRAG_DENSITY = 2 --Could be same as WATER_DENSITY, but it's nice to tweak buoyancy and drag separately

function submergedVolume(part: BasePart): number
	local totalVolume = part.Size.X * part.Size.Y * part.Size.Z
	local sizeY = part.Size.Y
	local waterY = 0
	local yMax = part.Position.Y + sizeY / 2
	local yMin = part.Position.Y - sizeY / 2
	local submergedProportion = math.clamp((waterY - yMin)/(yMax - yMin), 0, 1)
	return totalVolume * submergedProportion
end

function submergedBuoyantForce(part: BasePart): number
	local displacedVolume = submergedVolume(part)
	local partDensity = (part.CustomPhysicalProperties or PhysicalProperties.new(part.Material)).Density
	local gravity = -game.Workspace.Gravity
	
	return gravity * displacedVolume * (partDensity - WATER_DENSITY)
end

function submergedDragForce(part: BasePart): Vector3
	local velocity = part.AssemblyLinearVelocity
	local dragCoefficientTimesReferenceArea = submergedVolume(part)
	return -velocity * dragCoefficientTimesReferenceArea * WATER_DRAG_DENSITY
end

function setupBuoyantPart(part: BasePart)
	local buoyancyAtt = Instance.new("Attachment")
	buoyancyAtt.Parent = part
	
	local buoyancyVel = Instance.new("LinearVelocity")
	buoyancyVel.Parent = part
	buoyancyVel.RelativeTo = Enum.ActuatorRelativeTo.World
	buoyancyVel.VelocityConstraintMode = Enum.VelocityConstraintMode.Line
	buoyancyVel.LineDirection = Vector3.yAxis
	buoyancyVel.Attachment0 = buoyancyAtt
	
	local dragVectorForce = Instance.new("VectorForce")
	dragVectorForce.Parent = part
	dragVectorForce.RelativeTo = Enum.ActuatorRelativeTo.World
	dragVectorForce.Attachment0 = buoyancyAtt
	
	RunS.Heartbeat:Connect(function()
		buoyancyVel.MaxForce = submergedBuoyantForce(part)
		dragVectorForce.Force = submergedDragForce(part)
		
		local y = part.Position.Y
		if y < 0 then
			buoyancyVel.LineVelocity = 1000			
		else
			buoyancyVel.LineVelocity = 0
		end
	end)
end

for _, tagged in ipairs(TagS:GetTagged("BuoyantPart")) do
	setupBuoyantPart(tagged)
end
TagS:GetInstanceAddedSignal("BuoyantPart"):Connect(setupBuoyantPart)

Here I’m just modeling the body as like an upright cylinder or other prismatic shape, to simplify volume calculations.

buoy1

I’d say that’s a pretty good result. It can be tweaked a bit more, both with the density constants but also by setting the density of the bodies themselves. Something with greater density than the water will sink faster than just normal gravity, same density and it’s like if it was in air, a bit less density and it’ll float.

It’s got some limitations, like completely ignoring the rotation of the bodies. So something like a raft made from a flat part won’t keep itself upright. You can kind of get it to work with more smaller parts, but it’s still not great IMO. If you just need it to make characters float, you should be good to go.

buoy1

6 Likes