Can I use a coroutine to find if parts have stopped touching?

I have a building that is supposed to take damage once if it’s touching an enemy NPC (This one is named “Brute”).

I find using TouchEnded very buggy so I’m trying to find a more efficient way of finding if the touch has ended.

What I’m trying to do is have this keep subtracting the building’s health by 25 as long as it’s still making contact with the Brute. If the Brute stops touching it then it immediately stops doing the damage.

I have literally just started working with coroutines, so if the method I’m going for is just stupid then please let me know a better way of doing this. Thank you!

The Script

while true do
local Part = script.Parent
local Attacked = Part.Parent.Attacked
local Health = Part.Parent.Health
local Found = Instance.new("NumberValue", Part)
Found.Value = 0

local DamagingBuilding = coroutine.create(function()
Part.Touched:Connect(function(Hit)
	if Hit.Parent.Name == "Brute" then
		Attacked.Value = true
		Health.Value = Health.Value - 25
	end
end)
end)

local StoppedTouching = coroutine.wrap(function()
Found.Value = 0
local Parts = Part:GetTouchingParts()
	for i,v in pairs(Parts) do
	    if (v.Parent.Name == "Brute") then
		Found.Value = Found.Value + 1
	end
    wait(0.1)
	if Found.Value > 0 then
		Attacked.Value = true
	else
		Attacked.Value = false
		print("No longer being attacked!")
	end
	end
end)


wait(1.5)
if Attacked.Value == true then
coroutine.resume()
repeat
Health.Value = Health.Value - 25
wait(1.5)
until
Attacked.Changed and Attacked.Value == false
end
end

My use of coroutines might look completely stupid but that’s because all the research I’ve done on them have just confused me and I still don’t completely get them.

A coroutine can be used to run a function alongside the rest of your script. In this case you want a function to check whether a ‘brute’ is touching the building. While it is, you can wait before checking again. If it is not you can end that function

Another important consideration is whether a new touch means a new coroutine needs to be created. Since you are using a counter to see how many brutes are touching it, the answer is no. You need to keep track whether a coroutine is already running with each Touch (with only 1 brute this would still be required since .Touched can’t be relied on to fire only once)

Bearing this in mind, a viable solution could be

function checkTouch()
	while Attacked.Value do -- after this loop ends, the function ends and the coroutine status becomes 'dead'
		Found.Value = 0
		local Parts = Part:GetTouchingParts()
		for i,v in pairs(Parts) do
		    if (v.Parent.Name == "Brute") then
			Found.Value = Found.Value + 1
		end
	        wait(0.1)
		if Found.Value > 0 then
			Health.Value = Health.Value - 25
		else
			Attacked.Value = false
			print("No longer being attacked!")
		end
	        wait(1) -- or any other reasonable response time
	end
end

local touchRoutine
local debounce

Part.Touched:Connect(function(Hit)
	if debounce then return end
	debounce = true
	if Hit.Parent and Hit.Parent.Name == "Brute" then
		if not touchRoutine or coroutine.status(touchRoutine) == "dead" then -- check if the coroutine already exists, and if so whether it's active (not 'dead')
			Attacked.Value = true
			touchRoutine = coroutine.create(checkTouch) -- create a new coroutine
			coroutine.resume(touchRoutine) -- start running newly created routine
		end
	end
	wait(.1)
	debounce = false
end
1 Like