How to create terrain water behavior, without terrain water

Absolutely beautiful.

You could probably use a BodyVelocity with its Velocity property set to (0, 0, 0) to simulate no gravity, and you could apply a VectorForce to move the player, as well as fiddling with some humanoid states.

Just a thought.

2 Likes

So you’re asking for a system that sets your state to Swimming?

That’s fairly easy to do, but can be a bit performance intensive if not handled correctly
Consider looking into Humanoid:ChangeState() as this allows you to arbitrarily set a humanoid’s state

1 Like

Set the Humanoid State to Swimming and have a BodyVelocity set to the Humanoids MoveDirection * WalkSpeed

This is how i’ve done it

2 Likes

This strange behavior occurs, for some reason.

Here’s my code:

local Sea = workspace.Sea --Directory of the water part
--
local RS = game:GetService("RunService")
local Character = script.Parent
local Humanoid = Character:WaitForChild("Humanoid")
local Root = Character:WaitForChild("HumanoidRootPart")
local Swim


Humanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
	if Root.Position.Y < Sea.Position.Y then
		if not Swim then
			Swim = Instance.new("BodyVelocity")
			Swim.Parent = Root
		end
		Swim.Velocity = Humanoid.MoveDirection * Humanoid.WalkSpeed
		if Humanoid:GetState() ~= Enum.HumanoidStateType.Swimming then
			Humanoid:ChangeState(Enum.HumanoidStateType.Swimming)
		end
	elseif Swim then
		Swim:Destroy()
	end
end)

you still having issues with that

well have you set it to true

like this

Humanoid:ChangeState(Enum.HumanoidStateType.Swimming, true)

i dont know if this will fix the problem

just checked

that swimming is

Swimming
4	
The Humanoid is currently swimming in Terrain water.

i think it assumes that this only works when the humanoid is swimming on terrain water?

The issue was so simple. Every time I set the Humanoid state type to Swimming, it got set to GettingUp immediately after. Here’s the finished code to prevent future scripters from struggling with this:

local Sea = workspace.Sea --Directory of the water part
--
local RS = game:GetService("RunService")
local Character = script.Parent
local Humanoid = Character:WaitForChild("Humanoid")
local Root = Character:WaitForChild("HumanoidRootPart")
local Swim


RS.Heartbeat:Connect(function()
	if Root.Position.Y < Sea.Position.Y - Root.Size.Y / 2 then
		if not Swim then
			Swim = Instance.new("BodyVelocity")
			Swim.Parent = Root
		end
		Swim.Velocity = Humanoid.MoveDirection * Humanoid.WalkSpeed + Vector3.new(0,4,0)
		if Humanoid:GetState() ~= Enum.HumanoidStateType.Swimming then
			Humanoid:SetStateEnabled(Enum.HumanoidStateType.GettingUp, false)
			Humanoid:ChangeState(Enum.HumanoidStateType.Swimming)
		end
	elseif Swim and Root.Position.Y < Sea.Position.Y then
		Swim.Velocity = Humanoid.MoveDirection * Humanoid.WalkSpeed
	elseif Swim then
		Swim:Destroy()
		Swim = nil
		Humanoid:SetStateEnabled(Enum.HumanoidStateType.GettingUp, true)
	end
end)
64 Likes

Is there a way I could put this inside of a part script instead of startercharacterscripts? I’m having problems just having it in SCS and would rather just have it in a part. Especially since I am going to have many of them.

I can’t think of a good way of going about that, as swimming is an individual action of each character, just like walking or jumping. I don’t think you should have any kind of problem putting stuff in character scripts, so my recommendation to you is to get that fixed instead.

Alright, well my problem is that the water is being detected everywhere, not just on the part.

The code I wrote is very basic. The part is considered “sea level”, which means the player will be able to swim anywhere under the part’s Y coordinate. The whole purpose of the script is to create an infinite sea, something that terrain didn’t allow and came nowhere near in performance. However, for your purpose, the script can be modified by using the ZonePlus module. What you would do is remove the parts that use the “water part” and make use of the “localPlayerEntered” event of ZonePlus.

2 Likes

Do you have any guesses why the swim state stops working completely after the character dies once?

-- original script by aorda!!

local Players = game:GetService("Players")
local player = Players.LocalPlayer
local character = player.Character

if not character or not character.Parent then
	character = player.CharacterAdded:Wait()
end

local Humanoid = character:WaitForChild("Humanoid")
local Root = character:WaitForChild("HumanoidRootPart")
local Swim

local Zone = require(game:GetService("ReplicatedStorage").Zone)
local container = game:GetService("Workspace").Testwater
local zone = Zone.new(container)

zone.localPlayerEntered:Connect(function()
	if not Swim then
		Swim = Instance.new("BodyVelocity")
		Swim.Parent = Root
		print("swim has been placed")
	end
	Swim.Velocity = Humanoid.MoveDirection * Humanoid.WalkSpeed + Vector3.new(0,5,0)
	if Humanoid:GetState() ~= Enum.HumanoidStateType.Swimming then
		print("change state")
		Humanoid:SetStateEnabled(Enum.HumanoidStateType.GettingUp, false)
		Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false)
		Humanoid:ChangeState(Enum.HumanoidStateType.Swimming)
	end
end)

zone.localPlayerExited:Connect(function()
	Swim.Velocity = Humanoid.MoveDirection * Humanoid.WalkSpeed
	Swim:Destroy()
	Swim = nil
	print("swim nil!!")
	Humanoid:SetStateEnabled(Enum.HumanoidStateType.GettingUp, true)
end)

Where is this local script? If it’s on starterplayerscripts maybe you can change to startercharacterscripts, if this doesnt works you could put an event that updates the character variable if the player died.

1 Like

placing the character variable in the localPlayerEntered function made it work, thanks for the help

1 Like


why does the player stop swimming in the middle of the block? I changed some numbers in the code, they don’t change that

Hey. I’m changing size of sea by script in a moment each second and this line

print(Sea.Position.Y - Root.Size.Y / 2)(this is just print but you understood about what line i'm talking)

giving amount less than

Root.Position.Y

3 !< -0.4(so)

If i change Sea.Position to Sea.Size then i go like 100% higher than water
image

why not just put water below a part and make it non collidable
wont work with transparent parts tho

Hey I tried to remake your script but instead, we try to find the “Sea” part in the workspace and can be located thanks to an attribute boolean called “YES”. But it doesnt seem to work, do you any idea?

local Sea
for _, child in pairs(workspace:GetChildren()) do
    if child.Name == "Sea" and child:FindFirstChild("YES") then
        Sea = child
        break
    end
end

local RS = game:GetService("RunService")
local Character = script.Parent
local Humanoid = Character:WaitForChild("Humanoid")
local Root = Character:WaitForChild("HumanoidRootPart")
local Swim

Humanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
    if Sea and Root.Position.Y < Sea.Position.Y then
        if not Swim then
            Swim = Instance.new("BodyVelocity")
            Swim.Parent = Root
        end
        Swim.Velocity = Humanoid.MoveDirection * Humanoid.WalkSpeed
        if Humanoid:GetState() ~= Enum.HumanoidStateType.Swimming then
            Humanoid:ChangeState(Enum.HumanoidStateType.Swimming)
        end
    elseif Swim then
        Swim:Destroy()
    end
end)

Well, it looks like you are looking for the child called “YES” under the Sea part, which I think does not exist as you mentioned it as an attribute, not an object. You can use Instance:GetAttribute method to achieve precisely what you want. Still, I would suggest having an ObjectValue object in a set directory pointing to the sea part to avoid looking for it using a loop for every player.

P.S. Sorry for the late reply!

1 Like