High performance water: Swimmable part 2.0

Its a bug where the player can hold space and jump forever essentially. (at the top of water)

1 Like

Seems like a very nice module. I modified it to use CollectionService exclusivly and made it so tags can be added during runtime (and not have a single tag beforehand). However, I noticed one little error with the module


Where there is the code for detecting when a tag is added or removed, it listens to the “water_parts” tag instead of using the tag variable. This means that for people who use collection service and a custom tag, if a tag is added to a part during runtime, the script will not detect it

2 Likes

Thanks for pointing that out. It is fixed now.

1 Like

Important update: The system now works with gamepad. It should have worked before but since I couldn’t test it I wasn’t able to know.

This system doesn’t seem to be working as of now. Similar issue to the infinite jumping bug but in my case the swim animations don’t start at all. It just allows the player to fly by holding space after touching a water part. Even while outside of the water part.

1 Like

Reverted to a previous version that didn’t have this bug. The only change is that the previous version doesn’t allow jumping for xbox. It will stay like that until I find what is causing the problem on the newer one.

@AnimeWrrld @Birdoman7798

2 Likes

“Go read the whole post you lazy duck” i liked this line. its funny.

1 Like

I did tweak on it on the script, it’s not the best one since it heavily relies on timing but i hope @gpm231 can fix this properly and know the concept of the rework I did. I only change the gOut to have a debounce on the function and instead of gOut i used the 1.0 version get goOutOfWater() function on it. I havent tested it on mobile or live server, but on pc and studio it works fine so far. Heres the new code for HighperformanceWater:

--[[
Script made by gpm231 on 2022

--For info about this check the devforum post --> https://devforum.roblox.com/t/high-performance-water-swimmable-part-20/2038483

--Contact:
-Roblox: gpm231
-Twitter: @gpm231RBLX
-Discord: gpm231#9505
--]]

if not game:IsLoaded() then --wait for assets
	game.Loaded:Wait()
end

local userInputService = game:GetService("UserInputService")

local isTouchScreen = userInputService.TouchEnabled

local swimModule = require(script:WaitForChild("Libraries"):WaitForChild("SwimModule"))
local zonePlus = require(script:WaitForChild("Libraries"):WaitForChild("Zone"))
local lightingModule = require(script:WaitForChild("Libraries"):WaitForChild("UnderwaterLighting"))

local plr = game:GetService("Players").LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()

repeat task.wait() until char:FindFirstChildOfClass("Humanoid")
local hum = char:FindFirstChildOfClass("Humanoid")

local hrp = char:WaitForChild("HumanoidRootPart")

local tag = script.CollectionServiceTag.Value
local folder = script.PartFolder.Value

local container = folder or game:GetService("CollectionService"):GetTagged(tag)
if typeof(container) == "table" then if #container < 1 then container = nil end end
if not container then error('Please set a folder that contains the parts for the player to swim in OR tag your parts using collection service') end

local zone = zonePlus.new(container)

local gotOut = false

local defaultForce = hrp.AssemblyMass * workspace.Gravity

local tapCon --tap event connection

local debounce = false --for mobile devices to allow jumping out of water

local function folderChanged() --if you want to update the water parts this tracks the change
	zone = zonePlus.new(container)
end

if script.PartFolder.Value then
	container.ChildAdded:Connect(folderChanged)
	container.ChildRemoved:Connect(folderChanged)
else
	game:GetService("CollectionService"):GetInstanceAddedSignal(script.CollectionServiceTag.Value):Connect(folderChanged)
	game:GetService("CollectionService"):GetInstanceRemovedSignal(script.CollectionServiceTag.Value):Connect(folderChanged)
end

local function getKeycodesPressed()
	local data = {}
	local a = userInputService:GetKeysPressed()
	
	for _, obj in a do
		table.insert(data, obj.KeyCode)
	end
	
	return data
end

local function goOutOfWater()
	swimModule:Stop()
	
	hum:SetStateEnabled(Enum.HumanoidStateType.Jumping, true)
	hum:ChangeState(Enum.HumanoidStateType.Jumping)
	hum:SetStateEnabled(Enum.HumanoidStateType.GettingUp, true)
	--[[
		hum:SetStateEnabled(Enum.HumanoidStateType.Jumping, false)
	hum:SetStateEnabled(Enum.HumanoidStateType.GettingUp, false)
	]]
	wait(0.8)
end

local gOutDB = false --modified by me
local con = game:GetService("RunService").Heartbeat:Connect(function()
	if debounce then return end

	if tapCon then
		tapCon:Disconnect()
		tapCon = nil
	end

	local UpperDetectorPos = hrp.CFrame:ToWorldSpace(CFrame.new(0, 1, -0.75))
	local LowerDetectorPos = hrp.CFrame:ToWorldSpace(CFrame.new(0, -2.572, -0.75))
	local HeadDetectorPos = hrp.CFrame:ToWorldSpace(CFrame.new(0, 1.322, -0.75))

	local isUpperIn = zone:findPoint(UpperDetectorPos)
	local isLowerIn = zone:findPoint(LowerDetectorPos)
	local isHeadIn = zone:findPoint(HeadDetectorPos)
	local isCameraIn = zone:findPoint(workspace.CurrentCamera.CFrame.Position)

	--Getting out of water
	local function gOut()
		if not isTouchScreen then
			swimModule:GetOut()
			swimModule:Stop()
		else
			for i = 1, 20 do --weird stuff mobile needs
				swimModule:GetOut()
				swimModule:Stop()
			end

			debounce = true
			task.wait(0.25) --weird stuff mobile needs
			debounce = false
		end
	end
	
	if table.find(getKeycodesPressed(), Enum.KeyCode.Space or table.find(getKeycodesPressed(), Enum.KeyCode.ButtonA)) and not isHeadIn and isLowerIn then
		if gOutDB == false then --modified by me
			gOutDB = true
			goOutOfWater()
			--gOut() 
			wait(.4)
			gOutDB = false
		end
	end

	--Exception handlers
	if isUpperIn and isLowerIn then
		swimModule:Start()
		if gotOut then --for when the player goes back into the water after jumping out
			swimModule:CreateAntiGrav()
			gotOut = false
		end
	elseif not isUpperIn and not isLowerIn then
		swimModule:Stop()
	elseif not isUpperIn and isLowerIn then
		swimModule:ClearAntiGrav()
		gotOut = true
	end

	--Going up forces
	if table.find(getKeycodesPressed(), Enum.KeyCode.Space) or table.find(getKeycodesPressed(), Enum.KeyCode.ButtonA) then
		local force = hrp:FindFirstChildOfClass("VectorForce")
		if force then
			force.Force = Vector3.new(0, defaultForce * script:WaitForChild("Configuration"):GetAttribute("CharDensityUp"), 0) --force that make you go up by pressing space
		end
	else
		local force = hrp:FindFirstChildOfClass("VectorForce")
		if force then
			if hum.MoveDirection.Magnitude == 0 then
				if isHeadIn then
					force.Force = Vector3.new(0, defaultForce * script:WaitForChild("Configuration"):GetAttribute("CharDensity"), 0) --force that makes you go up by being idle
				end
			else
				force.Force = Vector3.new(0, defaultForce, 0) --force that makes you stay still when floating in the surface
			end
		end
	end

	--Mobile tap detection
	tapCon = game:GetService("UserInputService").TouchTapInWorld:Connect(function()
		if not swimModule.Enabled or isHeadIn or not isLowerIn then return end
		gOut()
	end)

	--Underwater lighting
	if isCameraIn then
		lightingModule:Add()
	else
		lightingModule:Remove()
	end
end)

--Player died handler
local function died()
	lightingModule:Remove()
	con:Disconnect()
end
local humCon = hum.Died:Connect(died)

And here is a short clip of the infinite jump bug spam fixed:
robloxapp-20231025-0239138.wmv (2.4 MB)

3 Likes

Hello! I have one issue, how do you make the player use the swimmable part when they’re seated? It jumps out my seated player when they enter the water.

Honestly right now I don’t remember. I’ll have a look at the code and tell you when I have some spare time.

1 Like

I’ll have a look at this when I have the time

3 Likes

Aren’t you supposed to update the container for CollectionService on CollectionService tag added?

local function folderChanged() --if you want to update the water parts this tracks the change
	container = folder or game:GetService("CollectionService"):GetTagged(tag)
	zone = zonePlus.new(container)
end

if script.PartFolder.Value then
	container.ChildAdded:Connect(folderChanged)
	container.ChildRemoved:Connect(folderChanged)
else
	game:GetService("CollectionService"):GetInstanceAddedSignal(script.CollectionServiceTag.Value):Connect(folderChanged)
	game:GetService("CollectionService"):GetInstanceRemovedSignal(script.CollectionServiceTag.Value):Connect(folderChanged)
end

folderChanged() works both for the folder and collectionService

1 Like

I added this:

container = folder or game:GetService("CollectionService"):GetTagged(tag)

Inside the FolderChanged() function.

Shouldn’t that line also be added in your local script?

If someone doesn’t mind can they help me with this problem: How can I use parallel lua scripting to improve performance of terrain generation

Oh, I thought it was written like that on my script. I’ll change it. Thanks

2 Likes

Is there a way to let it work with mesh water that is client-side (duplicated from replicated storage to workspace on client-side)?
I have tried using tags and other ways but nothing worked.

The whole system is client sided meaning it should technically work

But it doesnt no matter what i did, but after having a good sleep ill try again

Thanks for the resource and tutorial!

Unfortunately, something prevents my character model from swimming - up, down, left or right. The jump mechanism is the only feature that works.

Here is an image of my character. Despite its unconventional design, it is fully rigged and is compatible with Roblox.

Here is a video of the issue:

What would be the issue here? I’ve tried changing CharDensity and CharDensityUp, but the problem hasn’t been fixed.

(Ignore the first person setting - I’ve toggled it on and off, and there’s been no change to the issue)