Can’t you just put workspace.Water
? Or {workspace.Water1, workspace.Water2}
? Or even better, using CollectionService and passing something like "Tag:Water"
or just "Water"
? Is the string just detecting each part by name?
The string just tells the ray what is and isnt water pretty much, its really rather simple
Is the string just detecting each part by name?
Does this work with rotatated parts? What does the second parameter mean (“Water”)? Can’t I just do a table of parts?
I wish Roblox were to add a Water Block so the community didn’t need to write it’s own script honestly.
Yes it works with rotated parts, the second parameter is what the raycast will detect, so anything named “Water” will only work
edit: Sorry no it doesnt work with rotated parts, the top surface must always be upright
I suppose it’s adding the “Water” parts into a stored table, for connections. Why couldn’t we send that table manually?
Un no not at all, its literally for the raycast so it knows which part is water and which isnt
you can take a look at the code, its rather simple really
Took a look at the code and I modified it to work as many people want it.
Code
local player = game.Players.LocalPlayer
local character = player.Character
local root = character.HumanoidRootPart
local humanoid = character.Humanoid
local camera = workspace.CurrentCamera
local debris = game:GetService("Debris")
local params = RaycastParams.new()
params.FilterDescendantsInstances = {character}
params.FilterType = Enum.RaycastFilterType.Blacklist
local swimming = {}
swimming.__index = swimming
function swimming.new(MAX_SPEED, WaterParts, IdleAnimation, ActionAnimation, YLaunch)
local boolValue = Instance.new("BoolValue")
boolValue.Name = "Swimming"
boolValue.Value = false
boolValue.Parent = character
local self = {}
self.MAX_SPEED = MAX_SPEED
self.WaterParts = WaterParts
self.BodyOfWater = nil
self.YLaunch = YLaunch or 50
self.Idle = humanoid:LoadAnimation(IdleAnimation)
self.Action = humanoid:LoadAnimation(ActionAnimation)
game:GetService("RunService").Heartbeat:Connect(function(deltaTime)
local result = workspace:Raycast(root.Position, root.CFrame.UpVector * -3, params)
if result and table.find(self.WaterParts,result.Instance) and not boolValue.Value and result.Instance.CanCollide then
local bp = Instance.new("BodyPosition")
bp.MaxForce = Vector3.new(math.huge,math.huge,math.huge)
bp.P = 1500
bp.D = 300
bp.Parent = root
bp.Position = result.Position - Vector3.new(0,3,0)
local bg = Instance.new("BodyGyro")
bg.MaxTorque = Vector3.new(math.huge,math.huge,math.huge)
bg.P = 1500
bg.D = 300
bg.Parent = root
humanoid.PlatformStand = true
self.BodyOfWater = result.Instance
result.Instance.CanCollide = false
boolValue.Value = true
end
if boolValue.Value then
local bp : BodyPosition = root:FindFirstChild("BodyPosition")
local bg : BodyGyro = root:FindFirstChild("BodyGyro")
if humanoid.MoveDirection .Magnitude == 0 then
if self.Action.IsPlaying then
self.Action:Stop(.5)
end
if self.Idle.IsPlaying == false then
self.Idle:Play(.5)
end
bg.CFrame = camera.CFrame
else
if self.Idle.IsPlaying then
self.Idle:Stop(.5)
end
if self.Action.IsPlaying == false then
self.Action:Play(.5)
end
bp.Position += camera.CFrame.LookVector * self.MAX_SPEED * deltaTime
bg.CFrame = camera.CFrame * CFrame.Angles(-math.rad(90),0,0)
end
if bp.Position.Y >= self.BodyOfWater.Position.Y + self.BodyOfWater.Size.Y / 2 then
swimming.Stop(self,true)
elseif bp.Position.X >= self.BodyOfWater.Position.X + self.BodyOfWater.Size.X / 2 then
swimming.Stop(self)
elseif bp.Position.X <= self.BodyOfWater.Position.X + -self.BodyOfWater.Size.X / 2 then
swimming.Stop(self)
elseif bp.Position.Z >= self.BodyOfWater.Position.Z + self.BodyOfWater.Size.Z / 2 then
swimming.Stop(self)
elseif bp.Position.Z <= self.BodyOfWater.Position.Z + -self.BodyOfWater.Size.Z / 2 then
swimming.Stop(self)
end
end
end)
return setmetatable(self, {})
end
function swimming.Stop(self,isUp)
self.Action:Stop()
self.Idle:Stop()
character.Swimming.Value = false
if root:FindFirstChild("BodyPosition") then
root.BodyPosition:Destroy()
end
if root:FindFirstChild("BodyGyro") then
root.BodyGyro:Destroy()
end
if isUp then
local bv = Instance.new("BodyVelocity")
bv.MaxForce = Vector3.new(math.huge,math.huge,math.huge)
bv.P = 1500
bv.Velocity = root.CFrame.UpVector * self.YLaunch
bv.Parent = root
debris:AddItem(bv,.1)
end
humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)
task.spawn(function()
task.wait(.5)
self.BodyOfWater.CanCollide = true
self.BodyOfWater = nil
end)
end
return
Didn’t test if it works since I don’t have all the time and parameters to set up, but let me know if it doesn’t work.
There is already a different module that has existed for a while that accomplishes this and more called SwimmablePart.
Im confused what exactly did you modify?
Uh I made sure to scour the forums for any swimming systems that may have already been released i didnt find anything, could you link it?
I modified where the raycast check would happen, and the self var with the parameteter.
I think that just takes up more memory then just checking a string to be honest
For sure, the whole code has memory losses here and there. My mistake was that I made another variable instead of using the parameter. One of your mistakes was using a BoolValue instead of an Attribute.
Alright ill keep that in mind for future release, thank you.
Wait what makes attributes better than boolvalues?
Attributes esentially store a few less things which can free a little memory, while also looking clean from Value messes. It cannot store Objects, though.
Hello, cool module! I’m from the “backwards” post with dot product anyways, I’ve wanted to recommend something for your module (MAX_SPEED : number, WaterName : string, IdleAnimation : Animation, ActionAnimation : Animation, YLaunch : number)
With colon " : " so it easier to know what’s what when you are creating so it’s easier for people to know what to type in for a second I thought "Max speed " was a vector3 instead of a number