AI only targets one player (SOLVED)

So basically, I’ve been working on this survival-type game, but I found this major problem with the ai my friend gave me. When I test with one player, it works perfectly. When I test with two, it targets the player that joined last. Another thing I noticed was it is a bit laggy. Any help would be appreciated.

Here are the two things that I think might be causing this problem:

function GetTorso(part)
	local chars = game.Workspace:GetChildren()
	local torso = nil
	for _, v in pairs(chars) do
		if v:IsA'Model' and v ~= script.Parent and v.Name == GetPlayerNames() then
			local charRoot = v.PrimaryPart
			if (charRoot.Position - part).magnitude < SearchDistance then
				torso = charRoot
			end
		end
	end
	return torso
end
while wait() do
	local enemytorso = GetTorso(hroot.Position)
	if enemytorso ~= nil then
		isWandering = 1
		local function checkw(t)
			local ci = 3
			if ci > #t then
				ci = 3
			end
			if t[ci] == nil and ci < #t then
				repeat
					ci = ci + 1
					wait()
				until t[ci] ~= nil
				return Vector3.new(1, 0, 0) + t[ci]
			else
				ci = 3
				return t[ci]
			end
		end

		path = pfs:FindPathAsync(hroot.Position, enemytorso.Position)
		waypoint = path:GetWaypoints()
		oldpoints = waypoint
		local connection;

		local direct = Vector3.FromNormalId(Enum.NormalId.Front)
		local ncf = hroot.CFrame * CFrame.new(direct)
		direct = ncf.p.unit
		local rootr = Ray.new(hroot.Position, direct)
		local phit, ppos = game.Workspace:FindPartOnRay(rootr, hroot)

		if path and waypoint or checkw(waypoint) then
			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Walk then
				human:MoveTo( checkw(waypoint).Position )
				human.Jump = false
			end

			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Jump then
				human.Jump = true
				connection = human.Changed:connect(function()
					human.Jump = true
				end)
				human:MoveTo(checkw(waypoint).Position)
			else
				human.Jump = false
			end

			hroot.Touched:connect(function(p)
				local bodypartnames = GetPlayersBodyParts(enemytorso)
				if p:IsA'Part' and not p.Name == bodypartnames and phit and phit.Name ~= bodypartnames and phit:IsA'Part' and rootr:Distance(phit.Position) < 5 then
					connection = human.Changed:connect(function()
						human.Jump = true
					end)
				else
					human.Jump = false
				end
			end)

			if connection then
				connection:Disconnect()
			end

		else
			for i = 3, #oldpoints do
				human:MoveTo( oldpoints[i].Position )	
			end
		end
	elseif enemytorso == nil and canWander then
		isWandering = 0
		path = nil
		waypoint = nil
		--human.MoveToFinished:Wait()
	end
end
2 Likes

in the GetTorso function, it loops through all the characters and after it does, the last torso that it scans is the one that it returns, maybe you scan all of them and add them into a table, then sort the table so that table[1] is the nearest and table[whatever number is last] is the farthest. Then pick the nearest and return that.

local table1 = {20, 15, 35, 5}

table.sort(table1, function(a, b)
    return b > a
end)

print(table1[1], table1[2], table1[3], table1[4])

Output: 5 15 20 35

Use this to sort your table from lowest to highest

5 Likes

alright, since I’m a complete noob, exactly where would I implement that into the function? sorry if I’m bothering you, I’m just bad with tables.

1 Like

Ill write it out for you, give me a moment.

2 Likes
function GetTorso(part)
	local chars = game.Workspace:GetChildren()
	local torsos = {}
	for _, v in pairs(chars) do
		if v:IsA'Model' and v ~= script.Parent and v.Name == GetPlayerNames() then
			local charRoot = v.PrimaryPart
			if (charRoot.Position - part).magnitude < SearchDistance then
				table.insert(torsos, {charRoot = (charRoot.Position - part).magnitude})
			end
		end
	end
    table.sort(torsos, function (a, b) return a[2] > b[2] end)
	return torsos[1][1]
end

i cant really test it, but you can, so here go test it and tell me if it works.

1 Like

Attempt to index nil with number on this line:

return torsos[1][1]
1 Like

can you put

print(torsos[1])
local nearest = torsos[1]
print(nearest[1])

before the return

1 Like

attempt to index nil with number:

print(nearest[1])
1 Like
function GetTorso(part)
	local chars = game.Workspace:GetChildren()
	local torsos = {}
	for _, v in pairs(chars) do
		if v:IsA'Model' and v ~= script.Parent and v.Name == GetPlayerNames() then
			local charRoot = v.PrimaryPart
			if (charRoot.Position - part).magnitude < SearchDistance then
				table.insert(torsos, {charRoot, (charRoot.Position - part).magnitude})
			end
		end
	end
    table.sort(torsos, function (a, b) return a[2] > b[2] end)
    print(torsos[1])
    local nearest = torsos[1]
    print(nearest[1])
	return nearest[1]
end

try this

1 Like

attempt to index nil with number

print(nearest[1])
1 Like

I will try it out and test myself in studio then come back with a solution, first ill go eat dinner though.

1 Like

you might need the full script for that, here:
if you find any other problems with it, tell me please


	
	
local SearchDistance = math.huge

local ZombieDamage = math.huge


local canWander = false
local WanderX, WanderZ = 30, 30



function getHumanoid(model)
	for _, v in pairs(model:GetChildren()) do
		if v:IsA'Humanoid' then
			return v
		end
	end
end


local zombie = script.Parent
local human = getHumanoid(zombie)
local hroot = zombie.HumanoidRootPart
local zspeed = hroot.Velocity.magnitude


local pfs = game:GetService("PathfindingService")

function GetPlayerNames()
	local players = game:GetService('Players'):GetChildren()
	local name = nil
	for _, v in pairs(players) do
		if v:IsA'Player' then
			name = tostring(v.Name)
		end
	end
	return name
end

function GetPlayersBodyParts(t)
	--local torso = t
	--if torso then
	--	local figure = torso.Parent
	--	for _, v in pairs(figure:GetChildren()) do
	--		if v:IsA'Part' then
	--			return v.Name
	--		end
	--	end
	--else
	--	return "HumanoidRootPart"
	--end
end


function GetTorso(part)
	local chars = game.Workspace:GetChildren()
	local torsos = {}
	for _, v in pairs(chars) do
		if v:IsA'Model' and v ~= script.Parent and v.Name == GetPlayerNames() then
			local charRoot = v.PrimaryPart
			if (charRoot.Position - part).magnitude < SearchDistance then
				table.insert(torsos, {charRoot, (charRoot.Position - part).magnitude})
			end
		end
	end
	table.sort(torsos, function (a, b) return a[2] > b[2] end)
	print(torsos[1])
	local nearest = torsos[1]
	print(nearest[1])
	return nearest[1]
end

for _, zambieparts in pairs(zombie:GetChildren()) do
	if zambieparts:IsA'Part' then
		zambieparts.Touched:connect(function(p)
			if p.Parent.Name == GetPlayerNames() and p.Parent.Name ~= zombie.Name then -- damage
				local enemy = p.Parent
				local enemyhuman = getHumanoid(enemy)
				enemyhuman:TakeDamage(ZombieDamage)
			end
		end)
	end
end

local path
local waypoint
local oldpoints
local isWandering = 0

if canWander then
	spawn(function()
		while isWandering == 0 do
			isWandering = 1
			local desgx, desgz = hroot.Position.x + math.random(-WanderX, WanderX), hroot.Position.z + math.random(-WanderZ, WanderZ)
			human:MoveTo( Vector3.new(desgx, 0, desgz) )
			wait(math.random(4, 6))
			isWandering = 0
		end
	end)
end

while wait() do
	local enemytorso = GetTorso(hroot.Position)
	if enemytorso ~= nil then
		isWandering = 1
		local function checkw(t)
			local ci = 3
			if ci > #t then
				ci = 3
			end
			if t[ci] == nil and ci < #t then
				repeat
					ci = ci + 1
					wait()
				until t[ci] ~= nil
				return Vector3.new(1, 0, 0) + t[ci]
			else
				ci = 3
				return t[ci]
			end
		end

		path = pfs:FindPathAsync(hroot.Position, enemytorso.Position)
		waypoint = path:GetWaypoints()
		oldpoints = waypoint
		local connection;

		local direct = Vector3.FromNormalId(Enum.NormalId.Front)
		local ncf = hroot.CFrame * CFrame.new(direct)
		direct = ncf.p.unit
		local rootr = Ray.new(hroot.Position, direct)
		local phit, ppos = game.Workspace:FindPartOnRay(rootr, hroot)

		if path and waypoint or checkw(waypoint) then
			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Walk then
				human:MoveTo( checkw(waypoint).Position )
				human.Jump = false
			end

			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Jump then
				human.Jump = true
				connection = human.Changed:connect(function()
					human.Jump = true
				end)
				human:MoveTo(checkw(waypoint).Position)
			else
				human.Jump = false
			end

			hroot.Touched:connect(function(p)
				local bodypartnames = GetPlayersBodyParts(enemytorso)
				if p:IsA'Part' and not p.Name == bodypartnames and phit and phit.Name ~= bodypartnames and phit:IsA'Part' and rootr:Distance(phit.Position) < 5 then
					connection = human.Changed:connect(function()
						human.Jump = true
					end)
				else
					human.Jump = false
				end
			end)

			if connection then
				connection:Disconnect()
			end

		else
			for i = 3, #oldpoints do
				human:MoveTo( oldpoints[i].Position )	
			end
		end
	elseif enemytorso == nil and canWander then
		isWandering = 0
		path = nil
		waypoint = nil
		--human.MoveToFinished:Wait()
	end
end
1 Like

The reason it says this is because when you are the only person in the game and you just join it, the script is already running while your character isnt loaded in yet, which means it just errors.

You can solve this by putting a pcall function, which skips errors and keeps the code running.
for the GetTorso() function put this:

function GetTorso(part)
	local chars = game.Workspace:GetChildren()
	local torsos = {}
	for _, v in pairs(chars) do
		if v:IsA'Model' and v ~= script.Parent and v.Name == GetPlayerNames() then
			local charRoot = v.PrimaryPart
			if (charRoot.Position - part).magnitude < SearchDistance then
				table.insert(torsos, {charRoot, (charRoot.Position - part).magnitude})
			end
		end
	end
	table.sort(torsos, function (a, b) return a[2] > b[2] end)
	pcall(function()
		print(torsos[1])
		local nearest = torsos[1]
		if nearest[1] ~= nil then
			print(nearest[1])
			return nearest[1]
		end
	end)
end

and for the loop do this:

while wait() do
	pcall(function()
	local enemytorso = GetTorso(hroot.Position)
	if enemytorso ~= nil then
		isWandering = 1
		local function checkw(t)
			local ci = 3
			if ci > #t then
				ci = 3
			end
			if t[ci] == nil and ci < #t then
				repeat
					ci = ci + 1
					wait()
				until t[ci] ~= nil
				return Vector3.new(1, 0, 0) + t[ci]
			else
				ci = 3
				return t[ci]
			end
		end

		path = pfs:FindPathAsync(hroot.Position, enemytorso.Position)
		waypoint = path:GetWaypoints()
		oldpoints = waypoint
		local connection;

		local direct = Vector3.FromNormalId(Enum.NormalId.Front)
		local ncf = hroot.CFrame * CFrame.new(direct)
		direct = ncf.p.unit
		local rootr = Ray.new(hroot.Position, direct)
		local phit, ppos = game.Workspace:FindPartOnRay(rootr, hroot)

		if path and waypoint or checkw(waypoint) then
			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Walk then
				human:MoveTo( checkw(waypoint).Position )
				human.Jump = false
			end

			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Jump then
				human.Jump = true
				connection = human.Changed:connect(function()
					human.Jump = true
				end)
				human:MoveTo(checkw(waypoint).Position)
			else
				human.Jump = false
			end

			hroot.Touched:connect(function(p)
				local bodypartnames = GetPlayersBodyParts(enemytorso)
				if p:IsA'Part' and not p.Name == bodypartnames and phit and phit.Name ~= bodypartnames and phit:IsA'Part' and rootr:Distance(phit.Position) < 5 then
					connection = human.Changed:connect(function()
						human.Jump = true
					end)
				else
					human.Jump = false
				end
			end)

			if connection then
				connection:Disconnect()
			end

		else
			for i = 3, #oldpoints do
				human:MoveTo( oldpoints[i].Position )	
			end
		end
	elseif enemytorso == nil and canWander then
		isWandering = 0
		path = nil
		waypoint = nil
		--human.MoveToFinished:Wait()
		end
	end)
end

It should work, if it doesn’t, reply to this message.

1 Like

well now the npc just doesn’t move
no errors, just not moving
its printing a ton from here:

	if nearest[1] ~= nil then
			print(nearest[1])
			return nearest[1]

but thats just a print that shows the nearest torso, correct?

1 Like

Thats weird, I’ll look into it right now.

also yes thats correct.

1 Like

I just tested your original script and that didn’t work either, the Zombie isn’t moving. Any idea why?

1 Like

Thats because i sent the wrong version
this one should be working


	
	
local SearchDistance = math.huge

local ZombieDamage = math.huge


local canWander = false
local WanderX, WanderZ = 30, 30



function getHumanoid(model)
	for _, v in pairs(model:GetChildren()) do
		if v:IsA'Humanoid' then
			return v
		end
	end
end


local zombie = script.Parent
local human = getHumanoid(zombie)
local hroot = zombie.HumanoidRootPart
local zspeed = hroot.Velocity.magnitude


local pfs = game:GetService("PathfindingService")

function GetPlayerNames()
	local players = game:GetService('Players'):GetChildren()
	local name = nil
	for _, v in pairs(players) do
		if v:IsA'Player' then
			name = tostring(v.Name)
		end
	end
	return name
end

function GetPlayersBodyParts(t)
	--local torso = t
	--if torso then
	--	local figure = torso.Parent
	--	for _, v in pairs(figure:GetChildren()) do
	--		if v:IsA'Part' then
	--			return v.Name
	--		end
	--	end
	--else
	--	return "HumanoidRootPart"
	--end
end


function GetTorso(part)
	local chars = game.Workspace:GetChildren()
	local torso = nil
	for _, v in pairs(chars) do
		if v:IsA'Model' and v ~= script.Parent and v.Name == GetPlayerNames() then
			local charRoot = v.PrimaryPart
			if (charRoot.Position - part).magnitude < SearchDistance then
				torso = charRoot
			end
		end
	end
	return torso
end

for _, zambieparts in pairs(zombie:GetChildren()) do
	if zambieparts:IsA'Part' then
		zambieparts.Touched:connect(function(p)
			if p.Parent.Name == GetPlayerNames() and p.Parent.Name ~= zombie.Name then -- damage
				local enemy = p.Parent
				local enemyhuman = getHumanoid(enemy)
				enemyhuman:TakeDamage(ZombieDamage)
			end
		end)
	end
end

local path
local waypoint
local oldpoints
local isWandering = 0

if canWander then
	spawn(function()
		while isWandering == 0 do
			isWandering = 1
			local desgx, desgz = hroot.Position.x + math.random(-WanderX, WanderX), hroot.Position.z + math.random(-WanderZ, WanderZ)
			human:MoveTo( Vector3.new(desgx, 0, desgz) )
			wait(math.random(4, 6))
			isWandering = 0
		end
	end)
end

while wait() do
	local enemytorso = GetTorso(hroot.Position)
	if enemytorso ~= nil then
		isWandering = 1
		local function checkw(t)
			local ci = 3
			if ci > #t then
				ci = 3
			end
			if t[ci] == nil and ci < #t then
				repeat
					ci = ci + 1
					wait()
				until t[ci] ~= nil
				return Vector3.new(1, 0, 0) + t[ci]
			else
				ci = 3
				return t[ci]
			end
		end

		path = pfs:FindPathAsync(hroot.Position, enemytorso.Position)
		waypoint = path:GetWaypoints()
		oldpoints = waypoint
		local connection;

		local direct = Vector3.FromNormalId(Enum.NormalId.Front)
		local ncf = hroot.CFrame * CFrame.new(direct)
		direct = ncf.p.unit
		local rootr = Ray.new(hroot.Position, direct)
		local phit, ppos = game.Workspace:FindPartOnRay(rootr, hroot)

		if path and waypoint or checkw(waypoint) then
			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Walk then
				human:MoveTo( checkw(waypoint).Position )
				human.Jump = false
			end

			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Jump then
				human.Jump = true
				connection = human.Changed:connect(function()
					human.Jump = true
				end)
				human:MoveTo(checkw(waypoint).Position)
			else
				human.Jump = false
			end

			hroot.Touched:connect(function(p)
				local bodypartnames = GetPlayersBodyParts(enemytorso)
				if p:IsA'Part' and not p.Name == bodypartnames and phit and phit.Name ~= bodypartnames and phit:IsA'Part' and rootr:Distance(phit.Position) < 5 then
					connection = human.Changed:connect(function()
						human.Jump = true
					end)
				else
					human.Jump = false
				end
			end)

			if connection then
				connection:Disconnect()
			end

		else
			for i = 3, #oldpoints do
				human:MoveTo( oldpoints[i].Position )	
			end
		end
	elseif enemytorso == nil and canWander then
		isWandering = 0
		path = nil
		waypoint = nil
		--human.MoveToFinished:Wait()
	end
end
1 Like

This didn’t work either, do i need an R15 Zombie or R6? Im currently using r6. I just tested in r15, it didn’t work.

1 Like

r15 or r6 work, just spawn in a rig and insert the script into there and make sure the humanoidrootpart is unanchored

1 Like

Oh yeah i forgot that the root is anchored, it works, now ill do some testing.

1 Like