Ha ha, you spawned in the village, correct?
Turn around and there will be a tower, go behind it and you will find a secret staircase. Leap off the staircase and into the water
@Hazania I’ll join you
The reason why your original script would do damage is because again, your if statement was implicitly passing as true due to Enum.HumanoidStateType.Swimming not being falsy. Adding in the repeat loop was a ticket to botching the code as well.
You’ll need to do some explicit checking here to determine if the player is in water. Something I would recommend is to create a region off of the HumanoidRootPart’s upper and lower bounds, then expand the region up to the terrain grir and read the voxels in that area. You could also shoot a ray downwards and get the material that the ray intersects.
No, the enum only passes true after swimming is proved to be true. So basically, it’s dealing 2 damage instead of 1, but not affecting the code in any other way.
I just tested it. It deals damage inconsistently, sometimes it seems to be working perfectly, but if you stop and start a few times you will be able to get it to stop while you are still in the water.
As I said in my first response:
I have no idea what you’re talking about. I don’t know whether you’re not understanding what I’m writing or if you haven’t looked over the code properly. I am specifically talking about this line of code:
if Enum.HumanoidStateType.Swimming then
The loop itself will not do anything each iteration until GetState returns a value equivalent to Enum.HumanoidStateType.Swimming, as per the equivalency operator. The if statement above however will implicitly pass true because Enum.HumanoidStateType.Swimming is not false or nil.
If statements work by evaluating everything between the if and the then. The only thing that’s there is the Swimming state, thus if (Enum.HumanoidStateType.Swimming)
is not false. It doesn’t perform a check against the Humanoid’s current state as swimming here.
I’m very aware of this:
What I’m saying is, that had no affect on the original if statement passing damage, as I said, it’s basically just forcing an extra point of damage when the first if statement passes true.
So far, I’ve been looking at a few potential solutions. One that I’ve found that works is reading terrain voxels. The only problem is that the determined regions end up being larger than expected, considering you can only read up to a resolution of 4 minimum AFAIK.
As you can see, due to the way that the regions are being created (the white parts are there for the sake of visualisation), this could produce some largely inaccurate results and cause damage to be done while outside of water.
Another solution I proposed earlier was to simply shoot a raycast downwards and get the material on which the raycast had entered. That’s right out: a raycast from within water wouldn’t return the water material because the raycast is within and this might provide inaccuracies.
@LandofLee1620 How important is the body of water you’re trying to access? Does it need to handle arbitrary regions of water (as in, any water will damage the player) or do you just need it to occur across a specific area?
If the area is small enough and only happens in a small amount of areas, what you could do is create a proxy part across the bodies of water and check if the HumanoidRootPart’s CFrame is in them; that, or check if the RootPart is a returned part from a Region3 across the water part bounds.
If you explicitly need to check for water, you’ll have to figure something out with terrain voxels. A bit of searching that I’ve done shows that this is virtually the only way to accomplish this in terms of arbitrary water detection.
Did you see my original solution to this problem? I don’t think checking voxels is necessary.
Edit: I will write up this solution as code right now so the OP can test it.
StateChanged is capable of producing inaccuracies given how many times it can fire off as well as accounting for player behaviour (players often attempt to stay above water, meaning states will be changing rapidly). The event is primarily meant for animations.
Reading voxels is not unnecessary: it is a valid solution. StateChanged may also work and nail the use case, but for working with terrain, reading voxels is as accurate as you can get, minus the fact that the grid size is 4x4x4 (meaning you can’t go under resolution for a more hardpoint result).
local swimming = false
script.Parent.Humanoid.StateChanged:Connect(function(state)
if state == Enum.HumanoidStateType.Swimming and not swimming then
swimming = true
repeat script.Parent.Humanoid:TakeDamage(1) wait(1) until not swimming
else
swimming = false
end
end)
Please let me know if this works. If any of it does not make sense, please let me know!
This code doesn’t work, or rather it has an unintended side effect. I ended up fixing it.
Test run with your code, untampered (unsuccessful)
Repro file:
WaterDetectionStateChanged2 Repro.rbxl (224.9 KB)
Code:
local swimming = false
script.Parent.Humanoid.StateChanged:Connect(function(state)
if state == Enum.HumanoidStateType.Swimming and not swimming then
swimming = true
repeat script.Parent.Humanoid:TakeDamage(1) wait(1) until not swimming
else
swimming = false
end
end)
I upped the damage value to 5 to see if it was working properly: every time I jumped out of water, it would hurt me. This is because you are checking the wrong value: StateChanged passes both the old AND the new state.
I changed your code a bit so that it would account for the new state. This ended up working much better than the old version, but there are still inaccuracies as I pointed out. Because of the way swimming and StateChanged works, it is still possible to spam jump and avoid damage. If this is negligible, then by all means a ding-ding-ding solution right here.
Your code using newState instead (successful)
Repro file:
WaterDetectionStateChanged3 Repro.rbxl (224.9 KB)
Code:
local swimming = false
script.Parent.Humanoid.StateChanged:Connect(function(oldstate, newstate)
if newstate == Enum.HumanoidStateType.Swimming and not swimming then
swimming = true
repeat script.Parent.Humanoid:TakeDamage(10) wait(1) until not swimming
else
swimming = false
end
end)
In light of this, because my solution is apparently unnecessary, I also tried to implement StateChanged into a solution and temporarily resign from ReadVoxels.
My first run of StateChanged (successful)
Repro file:
WaterDetectionStateChanged Repro.rbxl (225.0 KB)
Code:
local Character = script.Parent
local Humanoid = Character.Humanoid
local Swimming = false
Humanoid.StateChanged:Connect(function (oldState, newState)
if newState == Enum.HumanoidStateType.Swimming then
print("Swim")
Swimming = true
elseif oldState == Enum.HumanoidStateType.Swimming and newState ~= Enum.HumanoidStateType.Jumping then
print("No swim")
Swimming = false
elseif oldState ~= Enum.HumanoidStateType.Swimming and oldState ~= newState then
print("No swim")
Swimming = false
end
while Swimming do
Humanoid:TakeDamage(5)
if Swimming then
wait(1)
end
end
end)
It’s like I said: StateChanged produces inaccuracies. Terrain is voxel-based, therefore you should be using voxel-based solutions as well. ReadVoxels is the canonical and single most accurate way to detect what cells a player is occupying, minus the cell resolution minimum.
If you align your water well with respect to the 4-resolution minimum and it’s not arbitrary, you can create a solution that utilises the HumanoidRootPart’s current CFrame or a region across the proxy part and check the CFrames relative to each other. A flat plain of water can also use a CFrame-based solution that would be far more accurate than anything currently on this thread.
I’d appreciate if, in the future, you didn’t doubt my information without proper citations for it.
Edit: Toned down some passive aggressiveness.
He posted the fixed code, I simply missed a parameter in the StateChanged function.
@colbert2677
So, I tried your code using new state and it worked for a second but it took a while for the script to start running. Then I jumped out of the water and jumped back in and this happened:
My life isn’t getting taken away
Edit: I was inflicting 1 damage. I changed it to 10 and it worked
I was wrong! My bad, your code does work. I ended up using the last code you suggested.
That might be because the damage values are too low. In my test, I amped them to 5 to counteract the Health regeneration script and it worked fine. Even then though, I wasn’t very keen on relying on the inaccuracies of StateChanged with the Swimming state, but it may be irrelevant in your case.
Discarded reply
Do you have outer map boundaries where there’s a flat body of water that you need damage to be taken against? In this scenario, it’d probably be much more worth your time to make a simple solution where you compare the CFrame of the HumanoidRootPart against a part that spans all the water. If your water extends really far, you may also want to consider invisible walls so players don’t go too far out.
The “water” is a chocolate river that winds and turns throughout an area of the map. It is made of terrain water. It won’t be possible to do that without a large amount of parts and a large amount of position checks.
Actually the “water” is also a chocolate ocean that surrounds the entire island. But for now StateChanged works
Oh, that’s pretty cool! I didn’t see that, you must have a pretty large map!
It’s huge! I had candies in the ocean that gave you a lot of money and I wanted to make them harder to attain through making the water deadly. Thanks for your help! @colbert2677 and @Hazania
You can do this with just one part if it’s a flat plain, per my suggestion. The maximum a part can extend up to is 2048 studs on any axis.
In terms of arbitrary shapes, requiring multiple parts or position checks is a non-problem: this kind of computation will have negligible expense and you can drop it down by adding other kinds of gatekeeping checks rather than running it across all regions indiscriminately.
You could always just script a kill brick, and put that in the water.