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.
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)
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.
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.
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)
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)
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.