humanoid.Died() firing multiple times (no loops)

I am making a spectating system. I realized that if a person dies while you spectate them, the system continues to spectate the corpse. To remedy this, I made it so that whenever the Subject (AKA, player who is being spectated) dies, the CurrentCamera’s subject would switch to the respawned character.
I ran into issues when the arrows to flip through the players stopped working upon doing this. Even if I remove the old dead character and add the new, respawned character to the table of players who are allowed to be spectated, it gives me an error that says:

Attempted to perform Add (arithmetic) on nil and number

You’ll understand better if I send you the script.
IMPORTANT: An npc is part of the table of subjects by default.

local left = script.Parent.ScreenGui.Spectating.L_Arrow
	local right = script.Parent.ScreenGui.Spectating.R_Arrow
	local nameplate = script.Parent.ScreenGui.Spectating.SubjectName
	local function spectate(sub :Model)
		cam.CameraSubject = sub
		left.Visible = true
		right.Visible = true
		nameplate.Visible = true
		nameplate.Text = sub.Name
		print("POV changed")
		local hum = sub:FindFirstChildOfClass("Humanoid")
		if hum then
			print("hum found")
			hum.Died:Connect(function()
				table.remove(chrs, table.find(chrs, sub))
				print(chrs)                               --first time Died fires, it works as intended, leaving only the npc in the table if the server has two players. Second time, it removes the npc from the table as well!
				print("subject died")
				sub = game.Players:GetPlayerFromCharacter(sub).CharacterAdded:Wait()
				print("new chr added" .. sub.Name)
				cam.CameraSubject = sub
				print("spectating new chr")
				table.insert(chrs, sub)
				print(chrs)   --first time Died fires, it adds one PLayer1 to the table. The second time, I see two Player1's in the table.
			end)
		end
		return sub
	end

	--first player that gets spectated
	local subject = chrs[1]
	spectate(subject)

	left.MouseButton1Click:Connect(function()
		local prev = table.find(chrs, subject) - 1         --here is the error
		if prev == 0 then                    --so that "previous" doesn't become less than 0
			prev = #chrs
		end
		subject = chrs[prev]
		spectate(subject)
		--print("left")
		print(chrs)
	end)
	right.MouseButton1Click:Connect(function()
		local following = table.find(chrs, subject) + 1        --here is the error
		if following > #chrs then             --so that "following" doesn't exceed len(chrs)
			following = 1
		end
		subject = chrs[following]
		spectate(subject)
		--print("right")
		print(chrs)
	end)

chrs = table of spectatable characters
subject = player/npc that is being spectated
left = left button to cycle through players
right = similar to left, but in the opposite direction
nameplate = shows username of subject on the screen
prev = previous player
folllowing = next player

The error occurs because .Died() happens twice for one death. Somehow, this leads to the chrs table having two instances of the player that died. Somehow this means it can’t find the subject??? I’m awfully confused, all help is greatly appreciated.

1 Like

If anyone needs the whole script, then I can send it.

The problem is most likely because you use the last subject to find the next/previous one, but if the last subject, such as the player, dies, not only does their character, aka your subject, get destroyed, you also remove it from the chrs table as far as I understood. And then, once either button is pressed, you are attempting to find the last subject in the table, even tho you just removed it from there. You should probably separately keep track of the current index instead of directly relying on the last subject to find the next/previous one.

so I should save the current index in the table rather than redefining it using table.find right?

You should save it separately as a variable, not in a table. You don’t need a table for the index.

Use

humanoid.Died:Once(function()

end)

I changed the code to use a variable “N” insted of “previous” and "following

	local function spectate(sub :Model)
		cam.CameraSubject = sub
		left.Visible = true
		right.Visible = true
		nameplate.Visible = true
		nameplate.Text = sub.Name
		print("POV changed")
		local hum = sub:FindFirstChildOfClass("Humanoid")
		if hum then
			print("hum found")
			hum.Died:Connect(function()
				table.remove(chrs, table.find(chrs, sub))
				print(chrs)
				print("subject died")
				sub = game.Players:GetPlayerFromCharacter(sub).CharacterAdded:Wait()
				print("new chr added" .. sub.Name)
				cam.CameraSubject = sub
				print("spectating new chr")
				table.insert(chrs, sub)
				print(chrs)
			end)
		end
		return sub
	end

	--first player that gets spectated
	local n = 1
	local subject = chrs[n]
	spectate(subject)

	left.MouseButton1Click:Connect(function()
		n -= 1
		if n == 0 then
			n = #chrs
		end
		subject = chrs[n]
		spectate(subject)
		--print("left")
		print(chrs)
	end)
	right.MouseButton1Click:Connect(function()
		n += 1
		if n > #chrs then
			n = 1
		end
		subject = chrs[n]
		spectate(subject)
		--print("right")
		print(chrs)
	end)

It works perfectly… but we have a new issue now:
It still removes the NPC from the table, and it adds 7-8 copies of “player1” in Local Server testing

Any clue why?

After the changes in my previous reply and also using :Once() instead, it now:

  1. removes the npc (not good)
  2. adds two instances of Player1 to my chrs table…

Thanks for telling me about :once() though

Well, I never would have thought I’d use dead as a variable but, here we are…
You’re seeing Humanoid.Died fire multiple times because you’re calling spectate(subject) each time a button is clicked, each call connects another hum.Died.

local dead = nil

local function spectate(sub :Model)
	cam.CameraSubject = sub
	left.Visible = true
	right.Visible = true
	nameplate.Visible = true
	nameplate.Text = sub.Name
	print("POV changed")
	local hum = sub:FindFirstChildOfClass("Humanoid")
	if hum then
		if dead then
			dead:Disconnect()
		end
		print("hum found")
		dead = hum.Died:Connect(function()
			table.remove(chrs, table.find(chrs, sub))
			print(chrs)
			print("subject died")
			sub = game.Players:GetPlayerFromCharacter(sub).CharacterAdded:Wait()
			print("new chr added" .. sub.Name)
			cam.CameraSubject = sub
			print("spectating new chr")
			table.insert(chrs, sub)
			print(chrs)
		end)
	end
	return sub
end

Really don’t like the way this works or looks… but, that is most likely your problem.

1 Like

It’s my first time making a spectating system, so it is probably really inefficient.
I think your solution has worked!
If I don’t experience any problems starting 2 days from now I’ll mark yours as the solution. Thank you!

This script was made to reflect your script. I really don’t like adding a :Connect(function() in the middle of some other function like this…

So… what do you suggest I do instead? How else am I supposed to know if the player dies?

Idk… kind of painted into a corner now.
Just don’t like anything that has to do something like this;

if dead then
	dead:Disconnect()
end

It’s like the over all structure is now leaving no choice.
(I have been known to be too picky however)