How to make it so a player constantly takes damage while touching a part

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)
1 Like

I have already done that, the player will only take damage when first touching the part, not constantly

2 Likes

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)
1 Like

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)
1 Like

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)

1 Like

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

2 Likes

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)
2 Likes

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.

11 Likes

Agreed and yeah I am creating new threads with those events haha my bad

1 Like

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.

5 Likes

appreciate you bro this was a major gameplay change in my game without you after god this couldn’t have been done