Respawning an NPC freezes the game

What do you want to achieve?
I’ve already made a combat system for npcs which follows the nearby player and attacks. Now, I want the npc to respawn once it has been killed after a couple of seconds.

What is the issue?
When the function for respawning the character is fired, the game freezes for a couple of seconds. As you go on to killing the npc the freezing times get longer.

What solutions have you tried so far?
I’ve tried removing absolutely every single part of what you can consider calling “map” since its a pretty empty baseplate for now. I’ve tried cloning the npc from replicated storage and firing a bindable function instead. I’ve changed the npc rig to a r6 one and the only thing that changes is the fact that the respawn times are shorter to start with, however, the bug persists. I’ve considered moving the npcs to the client rather than the server however that would act weirdly when a new player would join.

Heres my code:

local Char = script.Parent.Parent
local Hum = Char:FindFirstChild("Humanoid") or nil
local Humrp = Char:FindFirstChild("HumanoidRootPart") or nil 

local npc = script.Parent.Parent:Clone()

function Died()
	game.ReplicatedStorage.Events.HandlerFireClient:FireAllClients("MainHandler","EnemyNPCDeath",Humrp.Position,Char)
	--// Respawn
	wait(2)
	local startTime = os.clock()
	npc.Parent = script.Parent.Parent.Parent
	npc.Humanoid.Health = npc.Humanoid.MaxHealth
	script.Parent.Parent:Destroy()
	local deltaTime = os.clock() - startTime
	warn(("Respawn took %.2f seconds"):format(deltaTime))
end

--// On Death
Hum.Died:Connect(Died)

Any advice/help would be great.

Have you looked at script performance? That probably isn’t necessary though as this script seems to be the cause.

I think it might be because the way it respawns seems to be “recursive”. The script, upon launch, copies Char, and itself, which in turn also copies itself etc. Consider changing the script to copy the original NPC, “hide” it and instead show a copy of the NPC.

Also,

Your or doesn’t do anything here, so it may be removed. The only time the or will be used is if Char:FindFirstChild() is nil, and in that case you have the statement nil or nil which is just nil. So you can just have Char:FindFirstChild().

Thanks for the advice. I’ll make sure to fix the “or nil”. When you say, “copy the original NPC and hide it” you mean to hide the original somewhere in the workspace and when a player loads in copy the NPC, which is hidden, and place it where it should go and do so each time a player kills the NPC? or do you mean hide it in replicated storage or server storage, if that’s the case, I’ve already tried cloning from replicated storage and it still gives the same issue.

Currently you got the script inside the NPC, which means the script is cloned whenever the NPC is. To fix this you could still have this structure, but you will need to add this:

local NPC = script.Parent
script.Parent = NPC.Parent
NPC.Parent = game.ServerStorage

function spawnNPC()
	local startTime = os.clock()
	local clone = NPC:Clone()
	clone.Parent = script.Parent
	clone.Humanoid.Died:Connect(function()
		spawnNPC()
		clone:Destroy()
	end)
	local deltaTime = os.clock() - startTime
	warn(("Respawn took %.2f seconds"):format(deltaTime))
end

spawnNPC()
------- OUTPUT:
-- Respawn took 0.00 seconds

I tried this code, and it worked perfectly for me. Hopefully it works for you as well!

It worked like a charm! The output now warns 0.00 seconds on each respawn. Thank you so much.

No problem! Be mindful when cloning things with scripts as descendants, it may cause undesirable side-effects.

PS: Don’t forget to mark this as solved so others facing a similar problem may easily find the solution!

Will do! One question. If I were to have a combat script inside the NPC, just like in this picture:
image

What would I have to do with it? I was considering moving the whole thing into serverscriptservice and manage it through there, but I am not sure how to make it work considering NPCS will be respawned every now and then.

I don’t know what is the best structure honestly, I usually only make Systems (such as HardpointSystem etc) which means I’m not really exposed to a “full” structure.

If you are doing the same thing in many bases you might want to move the code to a ModuleScript, always good to not repeat yourself. But if it is base-specific (as in not just different data, but a completely different one-of-a-kind script, then you should probably have the script in the model). I don’t know the overall structure you have.

I don’t know exactly what those scripts are, nor what information they require etc. But if it is the script I’ve seen then you could have it in ServerScriptService and do the previously mentioned code for each and every NPC.

The system basically tracks the closes player to the NPC which it is parented to and if near range it will start attacking. Iam not sure how I could make it work via serverscriptservice though.

Usually a good starting point is to just create a function housing the entirety of that script, and then apply the function on all relevant items in the workspace/wherever it is.

I’m guessing it is keeping track using events? Much better than using a while task.wait() loop performance wise.

An example skeleton:

local bases = workspace.Bases:GetChildren()

function handleAggro(npc, trigger)
	...
end

function handleBase(base)
	...
	local npcs = base.NPCs:GetChildren()
	for _, npc in ipairs(npcs) do
		NPC.AggroArea.Touched:Connect(function(hitBy)
			handleAggro(npc, hitBy)
		end)
	end
	...		
end


for _, base in ipairs(bases) do
	handleBase(base)
end

Yes, I use magnitude and constantly check it if a player is near the NPC and if it is I use Humanoid:MoveTo() to move the NPC to the players position. I also update this MoveTo() as the player changes position. Finally, I use a while wait() do to check if the NPC is within 6 studs of the player to start attacking.

Consider creating a part (no-collided, but canTouch) which represents the area which it should be aggroed in, events are almost always better to use than a while-wait loop. Even though they do a little to impact performance alone, if enough of them exist they will make themselves known eventually…

You could potentially also create a sphere welded to HRP of the NPC, which upon .Touched causes the NPC to attack, but it’s not that necessary since you will need to constantly do checks to move anyways.

Adding this code into handleAggro should probably do the trick.

Alright, thank you for everything I’ll try to do that.

1 Like