How to prevent players from swimming vertically?

I have a script that turns all parts named “Water” into swimmable areas like terrain water, but I want to prevent players from being able to swim up or down, so that they will always be swimming on the surface.

I tried forcing the y-axis bodyvelocity to 0 using hum.MoveDirection * hum.WalkSpeed*Vector3.new(1, 0, 1) but it didn’t change anything; the player was able to move up and down like normal.

Is there any solution to this? (I know that bodyvelocity is deprecated but idk how to use linear velocity or whatever else there is)

local Workspace = game:GetService("Workspace")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local char = script.Parent
local hum = char:WaitForChild("Humanoid")
local hrp = char:WaitForChild("HumanoidRootPart")
local swim

local waterPart = {}

ReplicatedStorage.PlayerReady.OnClientEvent:Connect(function()
	for i, part in pairs(Workspace:GetDescendants()) do
		if part.Name == "Water" and part:IsA("BasePart") then
			table.insert(waterPart, part)
			part.Touched:Connect(function() end)
		end
	end
end)


RunService.Heartbeat:Connect(function()
	local isSwimming = 0
	local isFloating = 0
	for i=1, #waterPart do
		local touching = waterPart[i]:GetTouchingParts()
		for i=1, #touching do
			for _, part in pairs(char:GetDescendants()) do
				if part:IsA("BasePart") and touching[i] == part then
					isFloating += 1
				end
			end
			if touching[i] == hrp then
				isSwimming += 1
				break
			end
		end
	end
		
		if isSwimming >= 1 then
			if not swim then
				swim = Instance.new("BodyVelocity")
				swim.Parent = hrp
			end
			swim.Velocity = hum.MoveDirection * hum.WalkSpeed + Vector3.new(0,4,0)
			if hum:GetState() ~= Enum.HumanoidStateType.Swimming then
				hum:SetStateEnabled(Enum.HumanoidStateType.GettingUp, false)
				hum:ChangeState(Enum.HumanoidStateType.Swimming)
			end
		elseif swim and isSwimming == 0 and isFloating >= 1 then
			swim.Velocity = hum.MoveDirection * hum.WalkSpeed * Vector3.new(1,0,1)
		elseif swim and isSwimming >= 1 then
			swim.Velocity = hum.MoveDirection * hum.WalkSpeed
		elseif swim and isFloating == 0 then
			swim:Destroy()
			swim = nil
			hum:SetStateEnabled(Enum.HumanoidStateType.GettingUp, true)
		end
end)
6 Likes

You can add a part A that goes up when they touch another (non-collidable) part so it looks buoyant. Then play around with the speed of part A

1 Like

Sorry I don’t think I quite understand what you mean by this, could you elaborate a little more

1 Like

You can make two fully transparent parts. Name one partA and name the second one partB (make partB non collidable so it can fire an event but the player wouldn’t feel it). Make partB just below the surface and part A a little bit below Part B and script it so once part B is touched part A goes up and pushes the player up and then part A goes back down. Ask me for something u dont get.

1 Like

This is just a lot of steps that makes it tedious if u dont want that just make a fully transparent part below the surface. If u either do this or the other step make sure all parts are anchored!!! I was just recommending the other thing because in physics there is something called a buoyant force which is an upward for so in real life because you have oxygen in your body (oxygen being highly buoyant) when you try and swim down it pushes you back up.

Just tried this, it messes up the animation bc the arms are colliding with the part. It also prevents players from actually getting into the water since their hrp has to be submerged completely

I think an easier solution would just be to edit the actual velocity of the player when they are moving (but idk how to do that)

I think you are on the right track here in the OP. I’ll do some experimenting and get back to you.

2 Likes

Make sure partA (or partC if ur doing the other way) isn’t too close to the surface. I would say a little bit below how it is when u swim

If you do go this route, make sure the part has no friction or the player will catch on it causing the interruptions in animation and/or movement.

Yeah I did this but the part has to be at a certain depth below the player that it’s too low to stop all vertical movement. Anything lower would cause the hrp to not be submerged

(For reference, the pool of water I have is only 4.75 studs tall)

Ok so I tried changing the humanoid’s WalkSpeed to 0 while in water then having the BodyVelocity move the player, with it tracking Humanoid.MoveDirection and just ignoring the Y value. But now the character won’t rotate to lie flat while swimming and will stay upright. The player will also still move slower when the camera is pointed upwards/downwards.
Only solution to the first issue if I take this route is finding a way to manually orient the player based on what direction they’re moving but again idk how to do that lol.

You can use a BodyGyro to force the character to maintain the correct angle while still allowing them to rotate. As for the speed with camera looking down/up, movement is tied to camera by default but you can force it to be tied to the X or Z axis if you want to try that. Otherwise you can just translate whichever inputs are acting to be fully inputted so that ‘partial’ inputs read as ‘full’ inputs. I did this for my build system top-down camera in Castaway and it works well, but does prevent moving slowly by slightly pressing the stick.

1 Like

Could you elaborate on how to do both of these things? I’m still quite dumb with cframes so I can’t figure it out on my own

I have figured out an extremely simple solution for you after about an hour of messing around with the core roblox movement script.

If you haven’t already, load into a play test and copy the PlayerModule from StarterPlayerScripts, then exit the play test and paste it into StarterPlayerScripts again. When you put a core roblox script into the folder that it loads into, it overwrites the script and allows you to make edits. The only caveat is that if Roblox updates or changes the core script, yours will be unchanged. In this case, it’s probably not an issue and is pretty common practice among Roblox devs. You can always update the core script by copying it and editing it again later on.

Once you have done the above step(s), simply open the ControleModule and go to (ctrl+g) line 342. This function “calculateRawMoveVector” is the handler for calculating the move vector for the character based on their camera orientation. When a character is walking on land, their movement only affects the X and Z axis no matter what angle the camera is facing. However, when swimming, Roblox incoporates the Y axis so that you can swim up and down. We are simply going to comment out this part and have it treat land and water the same way.

This should give you the results you are looking for! :smiley:

2 Likes

goated :sunglasses: this literally solves all the issues

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.