NPC will stop damaging at random

Hello, when my NPC’s touch this part, they are supposed to take damage away from it. But when I run the game the GUI seems to stop working and stops decreasing at a random number.

I’m new to scripting I’m thinking it maybe needs to keep checking something in the script but I’m not sure how to do it.

Screenshot 2022-08-04 114705

Here’s the script from inside the part.

local health = 10000
script.Parent.Touched:Connect(function(hit)
	if hit.Parent.Name == "Noob" then
		health = health - 100
		if health <= 0 then
			script.Parent:Destroy()
		end
		script.Parent.BillboardGui.TextLabel.Text = "Health:"..health.."/10,000"
	end
end)

Thanks for any help.

This is because the Touched event is not fully reliable as well as this code only runs when the part is touched not while it’s touching like on a loop.

To combat both of these I’d probably cast a ray constantly towards the part from the npc’s HumanoidRootPart at a fixed distance, although if you have a lot of them in the game this might prove to be heavy on performance. To tackle that you may want to add a check that makes sure the npc’s are within an area of the part to then start casting rays.

I could draft an example script if you’d like, not sure how long it’d take me.

If you don’t mind making an example that would be great, becuase I have no idea what any of that means

Sure, no problem. I’m guessing it’ll take me between 20 minutes to an hour.

If you are unsure of what things mean I would recommend searching keywords up on the developer hub, or just on google. It helped me a lot in the past.

1 Like


Took me a little bit, but I’ve come up with this:

local run = game:GetService("RunService")
local lastTicks = {} -- Used for the cooldown check.

run.Heartbeat:connect(function() -- Runs every frame after physics.
	for i,npc in ipairs(workspace.NPCs:GetChildren()) do -- Look through all npcs checking if they are close enough.
		if npc:FindFirstChild("HumanoidRootPart") then
			local dist = (script.Parent.Position-npc.HumanoidRootPart.Position).Magnitude
			if dist<script.Parent.Size.X+15 then -- The part is 27 studs on the x and z, would recommend tweaking this to your liking.
				if not lastTicks[npc] then lastTicks[npc]=0 end -- If this is a new npc set their cooldown to 0.
				if lastTicks[npc]+3<tick() then -- Check if 3 seconds have elapsed since the last time the below code block ran.
					lastTicks[npc]=tick() -- Set the time to be used in the cooldown.
					-- The npc is close enough and not on cooldown, we can cast a ray (imaginary line) and see if it reaches the part.
					local unit = (script.Parent.Position-npc.HumanoidRootPart.Position).Unit -- The direction the part is relative to the npc.
					local params = RaycastParams.new()
					params.FilterDescendantsInstances={workspace.NPCs}
					params.FilterType=Enum.RaycastFilterType.Blacklist
					local result = workspace:Raycast(npc.HumanoidRootPart.Position,unit*2) -- Casting the ray, this return a RaycastResult. unit*2 where we set the fixed distance for the line (ray).
					if result and result.Instance==script.Parent then -- Checking if it hit something and if that object was this part.
						-- Damage part here
						local hp = (script.Parent:GetAttribute("Health") or 10000)
						script.Parent:SetAttribute("Health",hp-100)
					end
				end
			end
		end
	end
end)

This is a server script parented to the big part. I hope the comments make what’s happening a little clearer. There are a few things to tweak: the distance the npc has to be, the number of seconds to wait as a cooldown, and the damage to be dealt.

Also the npcs in this case have to be parented to a folder named NPCs.

1 Like

Thank you for this, I’ll look through it thouroughly and see what’s happening

No problem, I did forget to mention that where it says “unit*2” the 2 is the number of studs the imaginary line (the ray) is. If you have any questions about that code please ask.

I made my own version of exactly what you did, and this worked:

local cubeHealth = 10000
local cube = script.Parent
local cubeHealthText = cube.BillboardGui.TextLabel

cubeHealthText.Text = "Health: "..cubeHealth.."/10,000"

function DestroyCube()
	while game:GetService("RunService").Heartbeat:Wait() do
		if cubeHealth <= 0 then
			cube:Destroy()
			break
		end
	end
end

coroutine.wrap(DestroyCube)()


cube.Touched:Connect(function(hit)
	if hit.Parent.Name == "Noob" then
		cubeHealth -= 100
		print(cubeHealth)
		cubeHealthText.Text = "Health: "..cubeHealth.."/10,000"
	end
end)
1 Like

You might need to change some variables depending on where the items are in your own game.

1 Like

For me it’s just doing the exact thing my previous script was, but I’ve noticed if a second npc comes along it will start working again

That’s weird - I don’t really know why thats happening…

It might be helpful if you show a video

Ok, I just also found out that if I push the npc’s while they are touching the cube the gui updates so that might be the problem

That shouldn’t be a problem, everytime the the cube gets touched the gui updates, so it’s working fine.
While pushing the npcs they touch the cube.

robloxapp-20220804-1429376.wmv (2.8 MB)

So you want to add a cooldown? Because the script is working as it should

This was the whole problem I had in the first place haha, the gui stops updating after a while but the cube seems to still take damage and eventually destroy itself when it runs out of health.

local cubeHealth = 10000
local cube = script.Parent
local cubeHealthText = cube.BillboardGui.TextLabel

local npcsHit = {}

cubeHealthText.Text = "Health: "..cubeHealth.."/10,000"

function DestroyCube()
	while game:GetService("RunService").Heartbeat:Wait() do
		if cubeHealth <= 0 then
			cube:Destroy()
			break
		end
	end
end

coroutine.wrap(DestroyCube)()


cube.Touched:Connect(function(hit)
	if hit.Parent.Name == "Noob" then
		if npcsHit[hit.Parent] then return end

		cubeHealth -= 100
		print(cubeHealth)
		cubeHealthText.Text = "Health: "..cubeHealth.."/10,000"
		npcsHit[hit.Parent] = true
		wait(1)
		npcsHit[hit.Parent] = false
	end
end)
1 Like

Try that, although I’m not so sure it will work because the hit.Parent all have the same name as “Noob”. If it were players, i could do npcsHit[game.Player.Name] = true, and that would work because all players have diffrent names.

1 Like