Title is self explanatory, how do i make it so when a player is touching a part, constantly damage them and stop when they stop touching?
You can use a .touched event when the part experiences any touching.
for example:
local part = workspace.Part
local damage = 5 —the damage it would take
part.Touched:Connect(function(touched)
local human = touched.Parent:FindFirstChild(“Humanoid”)
if human then
human.Health -= damage
end
end)
I have already done that, the player will only take damage when first touching the part, not constantly
Oh alright then sorry, you didn’t mention you have tried this method.
Add a touched and a touchEnded event
https://developer.roblox.com/en-us/api-reference/event/BasePart/TouchEnded
Then just set a boolean for when they touch to true, and false for when they ended the touch.
Oh true, sorry about that too.
Try this:
script.Parent.Touched:Connect(function(hit)
local hum = hit.Parent:FindFirstChildWhichIsA("Humanoid")
if hum then
hum.Health = hum.Health - whatever health you want to minus
end)
script.Parent.TouchEnded:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChildWhichIsA("Humanoid")
if humanoid then
humanoid.Health = humanoid.Health
end
end)
That would only damage the player when they start and end touching, but not while touching
You can use Humanoid:TakeDamage(DMG)
to damage Humanoids instead of using Humanoid.Health = Humanoid.Health - DMG
.
put it in a while loop with a debounce to check if touching
Do this then:
script.Parent.Touched:Connect(function(hit)
local istouching = true
local hum = hit.Parent:FindFirstChildWhichIsA("Humanoid")
if hum then
while istouching == true do
wait()
hum.Health = hum.Health - whatever health you want to minus
if istouching == false then
break
end
end
end)
script.Parent.TouchEnded:Connect(function(hit)
istouching = false
local humanoid = hit.Parent:FindFirstChildWhichIsA("Humanoid")
if humanoid then
humanoid.Health = humanoid.Health
end
end)
Like how @DataSigh said, use the touched and touched ended like so
local cooldown = .5
local isTouching = false
local part = workspace.Part
local damage = 5 —the damage it would take
part.Touched:Connect(function(touched)
local human = touched.Parent:FindFirstChild(“Humanoid”)
isTouching = true
while isTouching do
if human then
human.Health -= damage
wait(cooldown)
end
end
end)
part.TouchEnded:Connect(function(touched)
if touched.Parent:FindFirstChild(“Humanoid”) then
isTouched = false
end
end)
I think i’ve tried that already, adding a while loop inside the .Touched event makes it so it keeps removing health even after stopped touching
ModuleScript has the code you want, all you need to do is add the debounce to check if the .touched event is true or not
local DAMAGE = 10
local INTERVAL = 1/30
local touchingHumanoids = {}
coroutine.wrap(function()
while true do
for humanoid in pairs(touchingHumanoids) do
humanoid:TakeDamage(DAMAGE)
end
wait(INTERVAL)
end
end)()
script.Parent.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then
touchingHumanoids[humanoid] = true
end
end)
script.Parent.TouchEnded:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then
touchingHumanoids[humanoid] = nil
end
end)
It might be cleaner to just connect to RunService.Heartbeat
on that coroutine, I don’t know
A coroutine is what I also had in mind, but I wanted to avoid creating another thread. Still another good way of approaching this!
Do this then:
script.Parent.Touched:Connect(function(hit)
local istouching = true
local hum = hit.Parent:FindFirstChildWhichIsA("Humanoid")
if hum then
while istouching == true do
wait()
hum.Health = hum.Health - whatever health you want to minus
if istouching == false then
break
end
end
end)
script.Parent.TouchEnded:Connect(function(hit)
istouching = false
local humanoid = hit.Parent:FindFirstChildWhichIsA("Humanoid")
if humanoid then
humanoid.Health = humanoid.Health
end
end)
You can try moving the coroutine part to the end and throwing the coroutine wrapper away?
local DAMAGE = 10
local INTERVAL = 1/30
local touchingHumanoids = {}
script.Parent.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then
touchingHumanoids[humanoid] = true
end
end)
script.Parent.TouchEnded:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then
touchingHumanoids[humanoid] = nil
end
end)
while true do
for humanoid in pairs(touchingHumanoids) do
humanoid:TakeDamage(DAMAGE)
end
task.wait(INTERVAL)
end
You’re technically creating a new thread for .Touched
and .TouchEnded
events every time it fires anyway.
Agreed and yeah I am creating new threads with those events haha my bad
I just realized this would cause problems if you e.g. poked a leg outside of the damage part, that would make the humanoid stop getting damaged even if part of the character is still inside.
Old Solution
I would either just ditch the events altogether and use :GetTouchingParts
local DAMAGE = 10
local INTERVAL = 1/30
local touchingHumanoids = {}
script.Parent.Touched:Connect(function() end)
while true do
for _, part in ipairs(script.Parent:GetTouchingParts()) do
local humanoid = part.Parent:FindFirstChild("Humanoid")
if humanoid then
touchingHumanoids[humanoid] = true
end
end
for humanoid in pairs(touchingHumanoids) do
humanoid:TakeDamage(DAMAGE)
end
table.clear(touchingHumanoids)
task.wait(INTERVAL)
end
or I would try to keep a set of all parts touching this part, extract every humanoid from all the parts and damage them.
local DAMAGE = 10
local lNTERVAL = 1/30
local touchingParts = {}
local touchingHumanoids = {}
script.Parent.Touched:Connect(function(hit)
touchingParts[hit] = true
end
script.Parent.TouchEnded:Connect(function(hit)
touchingParts[hit] = nil
end
while true do
for part in pairs(touchingParts) do
local humanoid = part.Parent:FindFirstChild("Humanoid")
if humanoid then
touchingHumanoids[humanoid] = true
end
end
for humanoid in pairs(touchingHumanoids) do
humanoid:TakeDamage(DAMAGE)
end
table.clear(touchingHumanoids)
task.wait(INTERVAL)
end
This would lead to the same pattern but more complicated, so I would just use :GetTouchingParts
.
I would use a counter and increment/decrement it whenever the kill brick is touched/no longer touched by a part respectively.
local DAMAGE = 10
local INTERVAL = 1/30
local touchingHumanoids = {}
script.Parent.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if not humanoid or humanoid.RootPart == hit then return end
if touchingHumanoids[humanoid] then
touchingHumanoids[humanoid] += 1
else
touchingHumanoids[humanoid] = 1
end
end)
script.Parent.TouchEnded:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if not humanoid or humanoid.RootPart == hit then return end
if touchingHumanoids[humanoid] then
touchingHumanoids[humanoid] -= 1
if touchingHumanoids[humanoid] == 0 then
touchingHumanoids[humanoid] = nil
end
end
end)
while true do
for humanoid in pairs(touchingHumanoids) do
-- if the humanoid is destroyed, remove it from the loop
if not humanoid:IsDescendantOf(game) then
touchingHumanoids[humanoid] = nil
continue
end
humanoid:TakeDamage(DAMAGE)
end
task.wait(INTERVAL)
end
I added the check humanoid.RootPart == hit
because the HRP can somehow register a Touched event but never unregister it after the fact. I have no idea why that happens. The torso fills that area anyway, so there should be virtually no discrepancies.