Attempt to index nil with "Character"

Im currently working on a zombie.
Its working perfectly fine when I test it in singleplayer, but when I test it in a local server with 2 players, the zombie is doing nothing.
Also, when Im not addind the CharacterAdded:Wait() in the char declaration, I become an error for the HRT. Using WaitForChild is also not working.
The code:

function findTarget()
	for i, v in pairs(game.Players:GetPlayers()) do
		local char = v.Character or v.CharacterAdded:Wait() --With only v.Character, the Script errors
		if char then
			local HRT = char.HumanoidRootPart
			local distance = (script.Parent.Torso.Position - HRT.Position).Magnitude
			
			if distance < maxDistance then
				return v
			else
				return nil
			end
		end
	end
end

image
image

2 Likes

I think it would be best to skip over the player if they have no character.

if v.Character then
    -- Rest of code
end

I’m also a bit suspicious about the returning in the loop. When return is ran, the loop basically finishes.

If the first player joins out of the range of the zombie, it should move if they go into its range. But when a second player joins and the first player goes out of range, the zombie still loops through the players in order from Player1. So if Player1 is out of range and the loop returns nil, the loop is cancelled before it can check Player2’s distance from it, making the zombie only work for Player1. Hopefully you understand what I’m getting at.

Anyway, a better approach would be to use table.sort like this:

function FindTarget()
    --// Adding everyone and their distances from the zombie to the table
    local distances = {}
    for _, player in game.Players:GetPlayers() do
        local character = player.Character
        if not character then continue end

        local root = character:FindFirstChild("HumanoidRootPart")
        if not root then continue end

        local zombieRoot = --insert its hrp here. would personally use oop lol
        local magnitude = (zombieRoot - root.Postion).Magnitude
        
        table.insert(distances, {
            Player = player,
            Magnitude = magnitude
        })
    end

    --// Sorting the table
    table.sort(distances, function(a, b)
        return a.Magnitude < b.Magnitude -- Or other arrow sign, forgor
    end)

    --// Returning the closest player (returns nil if there is nobody in range)
    local closestPlayer = distances[1]
    return closestPlayer
end
1 Like

Yeah you are right, thank you!
But I preferred using another method than you, but could that cause problems?

EDIT: After a bit testing, its not working, but why?

function findTarget()
	local distancesTab = {}
	local nearestDis = maxDistance
	local nearestPlr = nil
	
	for i, v in pairs(game.Players:GetPlayers()) do
		local char = v.Character
		if char then
			local HRT = char.HumanoidRootPart
			local distance = (script.Parent.Torso.Position - HRT.Position).Magnitude
			
			if distance < maxDistance then
				table.insert(distancesTab, {distance, v})
			end
		end
	end
	
	for i, v in pairs(distancesTab) do
		if v[1] < nearestDis then
			nearestDis = v[1]
			nearestPlr = v[2]
		end
	end
	
	if nearestPlr ~= nil then
		return nearestPlr
	end
end

1 Like

You should return out of the loop and the way you calculate distance is not right. Try this:

function findTarget()
	local closestPlayer = nil
	local minDistance = math.huge 

	for i, v in pairs(game.Players:GetPlayers()) do
		local char = v.Character or v.CharacterAdded:Wait()

		if char then
			local HRT = char.HumanoidRootPart
			local distance = (script.Parent.Torso.Position - HRT.Position).Magnitude

			if distance < minDistance then
				minDistance = distance
				closestPlayer = v
			end
		end
	end

	return closestPlayer
end
1 Like

So I came now up with this:

function findTarget()
	local distancesTab = {}
	local nearestDis = maxDistance
	local nearestPlr = nil
	
	for i, v in pairs(game.Players:GetPlayers()) do
		local char = v.Character
		if char then
			local HRT = char.HumanoidRootPart
			local distance = (script.Parent.Torso.Position - HRT.Position).Magnitude
			
			if distance < maxDistance then
				table.insert(distancesTab, {distance, v})
			end
		end
	end
	
	table.sort(distancesTab, function(a, b)
		return a[1] < b[1]
	end)
	
	if #distancesTab ~= 0 then
		nearestPlr = distancesTab[1][2]
		table.clear(distancesTab)
		return nearestPlr
	else
		return nil
	end
end

And this works now completely fine, so the Script detects which plr is the nearest. HOWEVER, if both player are getting hitted by the zombie, then if the zombie hits only one of them, both get damaged. That doesnt makes sense… Heres the Part of my Script which is dealing with that:

runService.Heartbeat:Connect(function(t)
	local target = findTarget() --Getting the target through the function
	if target ~= nil and target.Character.Humanoid.Health > 0 then --Checking if the player is valid to be followed
		local char = target.Character
		print(char) --Printing the Name of the Player (Prints what it should!!!)
		if char then --Checking if char exists
			local HRT = char.HumanoidRootPart	
			local distance = (script.Parent.Torso.Position - HRT.Position).Magnitude --Getting distance for attack
			
			if distance < attackDistance and attacking == false then --Attack block
				attacking = true
				hitTrack:Play()
				hitTrack.KeyframeReached:Connect(function(keyName) --If the KeyFrame in the Attack Animation is reached (still works fine)
					if keyName == "Hit" then
						char.Humanoid:TakeDamage(damage) --Dealing damage to the plr 
					end
				end)
				hitTrack.Ended:Connect(function()
					attacking = false --Making attacking possible again when the animation ended
				end)	
			end
			
			
			hum:MoveTo(HRT.Position, HRT) --Moving the Zombie
			if moving == false then --Here the Moving Animation Part starts
				walkTrack:Play()
				moving = true
			end	
		end
	else
		walkTrack:Stop() --If target is not valid to follow, make the zombie stop
		moving = false
		hum:MoveTo(script.Parent.Torso.Position)
	end
end)

image

1 Like

In simple terms as you open a connection to do damage:

hitTrack.KeyframeReached:Connect

It can be connected to multiple players.

I dont understand that. I mean I am only accessing the char of the plr who is the target. Cant see how that event is in connection with that bug. And if it would be so, how could I prevent it?

You must understand inside the heartbeat code is running multiple times. If both players are near the zombie you will open multiple function connections.

But the way you doing this is crazy. There are better ways to detect if the zombie arm touched the player. There is no need to find the nearest player, also I don’t know why you do this inside Hearbeat.

Just add this script inside the zombie model:

model = script.Parent

local canHit = true
local lastHit = os.time()
local damageCoolDown = 1.5 -- Set cooldown
local damageMuch = 1 -- Set how much damage

local function handleHit(other)
	if canHit then
		if other and other.Parent and other.Parent.Name ~= "Zombie" and other.Parent:FindFirstChild("Humanoid") then
			local enemy = other.Parent						
			if enemy.Humanoid.WalkSpeed > 0 then
				enemy.Humanoid.Health = enemy.Humanoid.Health - damageMuch
				canHit = false
			end
		end
	else
		local now = os.time()
		if now - lastHit > damageCoolDown then
			lastHit = now
			canHit = true
		end
	end
end	


-- Connect the function to each arm, so if player touch the arm it does damage.
local leftHitConnect, rightHitConnect
leftHitConnect = model:FindFirstChild("Left Arm").Touched:connect(handleHit)
rightHitConnect = model:FindFirstChild("Right Arm").Touched:connect(handleHit)

Im using the Hearbeat event and the distance track to make the zombie chase the player. Distance track to get the nearest player and hearbeat event to update :MoveTo.
I also have some problems with using your method. .Touched events are not that safe everytime because they often fail to fire when used often. I would also need to have track of both arms and in addition, I cant set the hitbox size.

And I have to say that I still dont understand how both players are damaged. The char which is getting damaged cant be like duplicated and the event is only fired once when the dummy attacks.

I never had problem with Touched events, but I don’t know your case. You can connect the same function to both arms. It is possible to have an invisible part as hitbox.

Anyway, if you wanna stick with your method try to print and see if multiple connections are being created.

if keyName == "Hit" then
	print("Who is damaged:",char)
	char.Humanoid:TakeDamage(damage) --Dealing damage to the plr 
end

Yeah you were right! I just had to disconnect the event and it fixed my issue. Thanks

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.