Summon Follow Code Does Not Work The Way It's Supposed to

So in my game I’m trying to make a move for one of the characters where they’ll summon an NPC to fight other players, right now I’m trying to make the code for it that makes it follow other players.
Right now the goal is to make it follow players from a 500 stud distance, while also stopping at a certain minimum distance so that way they don’t just constantly bump into the player if too close.

While I have gotten the NPC to follow people, it just does not seem to work the way I have intended; in the script I have it so that the closest person nearby will be set as the NPC’s ‘targetlock’ value so that way it will only go after that person but when that person dies it’s ‘targetlock’ npc will change to the nearest alive person instead, but it just seems to completely ignore the value and go for the closest thing instead which, that is what I want but for some reason the value itself has itself set to a completely different person on the map (I am currently testing this with multiple dummy NPCs), like every single time it would always default to this one specific NPC that wasn’t even the closest one to the summoned NPC. When I did either move that specific NPC so far away that it wouldn’t register or just flatout deleted it, it would STILL default to another random specific NPC that wasn’t even the closest one to the summoned NPC.
I haven’t even delved into the minimum distance thing yet because I’m still trying to figure out how to get the normal targetting system to even work, with the minimum distance thing in set it will go after one target before immediately switching to the other once inside the minimum distance threshold.

Here is the code for it:

function findTorso(pos)
	local torso = nil
	local dist = 500 --Maximum distance
	local mindist = 50 --Minimum distance, haven't implemented this into the code yet
	local child = workspace:children()
	for i=1, #child do
		if child[i].className == "Model" then
			local h = child[i]:FindFirstChild("Humanoid")
			if h ~= nil then
				local check = child[i]:FindFirstChild("Torso")
				if check ~= nil and check.Parent.Name ~= script.Parent.owner2.Value and check.Parent.Name ~= script.Parent.Name then --Makes it so that the NPC won't try to follow it's owner or itself
					script.Parent.targetlock.Value = check.Parent.Name --Sets it's current follow target
					if (check.Position - pos).magnitude < dist then
						torso = check
						dist = (check.Position - pos).magnitude
						if check.Parent.Humanoid.Health <= 0 then --If current target is dead then choose another one
							script.Parent.targetlock.Value = check.Parent.Name
						end
					end
				end
			end
		end
	end
	return torso
end

while true do
	task.wait()
	local torso = findTorso(script.Parent.Torso.Position)
	if torso ~= nil then
		script.Parent.Humanoid:MoveTo(torso.Position) --Makes the NPC move to it's target
	end
end

So far I have tried doing:

if torso ~= nil and torso == script.Parent.targetlock.Value then
		script.Parent.Humanoid:MoveTo(torso.Position)
	end

…but that just seems to brick the NPC’s movement completely no matter what iteration of those lines of code I tried doing.
I’ve also tried messing around with various parts of the findTorso() function itself like moving the:

check.Parent.Name == script.Parent.targetlock.Value

line of code around to various parts of the code where applicable but that didn’t seem to make a difference at all.

Overall the main issue is that it is following the closest NPC, but the ‘targetlock’ value (which is a StringValue, I’ve tried it with ObjectValue too but it seemed to have the exact same problems) keeps setting itself to a specific NPC that isn’t even the closest one to the summoned NPC.
This is a problem because if the targetlock value isn’t working, then the minimum distance thing will continue to do that thing where the summoned NPC just goes back and forth with multiple different NPCs on the map without ever locking onto one specific one that it’s trying to target.

If anybody could offer me some help with this dilemma then that would be greatly appreciated, thank you! ^ ^

1 Like

There are some things I would suggest you change and improve.

To start off with, :children method is deprecated, use GetChildren(). Next in the loop, there’s no need to iterate to the max number of children in the workspace, a better way would be simply to do:

for index, value in pairs(workspace:GetChildren()) do
end

where value returns the current value in the workspace at iteration index.

You also have a lot of nested if statements for validation checks, I would suggest you use reverse logic to avoid nesting. for example

for index, value in pairs(workspace:GetChildren()) do
    if not value:IsA("Model") then continue end

    local humanoid = value:FindFirstChildOfClass("Humanoid")
    if not humanoid then continue end

    local torso = value:FindFirstChild("Torso")
    if not torso then continue end

    -- rest of the code in similar fashion
end

Finally I would suggest a different approach to getting the next closest players character and torso:

You should loop through every player instance in game.Players:GetPlayers() and get the position of each of their root parts. do a magnitude check (player.Character.HumanoidRootPart.Position - npc.Position).Magnitude and store the current magnitude of the first player, then simply use a comparison to check the magnitude of the next player with the shortest current magnitude and repeat until all players have been checked. Store that final player value and access any information you need when trying to track, or simply directly access that players root part and return it from a function call.

local closestPlayer, closestMag

for _, player in pairs(game.Players:GetPlayers()) do
    local character = player.Character
    if not character then continue end

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

    local magnitude = (humanoidRootPart.Position - npc.Position).Magnitude

    -- No current closest player or mag because it's the first iteration so set it once.
    if not closestMag then
        closestMag = magnitude
        closestPlayer = player
    end

    -- If no new closest magnitude then skip the iteration and move onto the next player
    if magnitude >= closestMag then continue end

    closestMag = magnitude
    closestPlayer = player
end

Add any other checks you want, the continue in the script allows the loop to skip the current iteration and move onto the next. If you used return you’d break out the loop and the function so avoid that.

I would suggest you avoid calling this method too repeatedly in the while true loop because it’s a somewhat intensive check to be doing 60 times a second. maybe increase the task.wait() to 0.05 or 0.1 . The difference will barely be noticeable but lower end devices will greatly appreciate it.

Good luck with your implementation and I hope this helped!

1 Like

UPDATE: Thank you for the advice! I greatly appreciate it! ^ ^

Although I put in the things you said like the more optimized checking code and the more straightforward code for the magnitude proximity code, but when I ran it in the game the summoned NPC didn’t move at all unfortunately.

I didn’t see any errors in the console either so I am a bit confused there.
Here is what I put in for the new code, maybe there was something that I forgot along the steps?

npc = script.Parent

function findTorso(pos)
	for index, value in pairs(workspace:GetChildren()) do
		if not value:IsA("Model") then return end
		
		local humanoid = value:FindFirstChildOfClass("Humanoid")
		if not humanoid then return end
		
		local torso = value:FindFirstChild("Torso")
		if not torso then return end
		
		local closestPlayer, closestMag
		
		for _, player in pairs(game.Players:GetPlayers()) do
			local character = player.Character
			if not character then continue end
			
			local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
			if not humanoidRootPart then continue end
			
			local magnitude = (humanoidRootPart.Position - npc.Position)
			
			if not closestMag then
				closestMag = magnitude
				closestPlayer = player
			end
			
			if magnitude >= closestMag then continue end
			
			closestMag = magnitude
			closestPlayer = player
		end
	end
end

while true do
	task.wait(0.1)
	local torso = findTorso(script.Parent.Torso.Position)
	if torso ~= nil then
		script.Parent.Humanoid:MoveTo(torso.Position)
	end
end

Also I took your advice and increased the tick time on the moving code itself to 0.1 seconds, thank you for that as well! ^ ^

1 Like

Here’s how I suggest doing this:

function findTorso(pos)
	local closestPlayer, closestMag
		
	for _, player in pairs(game.Players:GetPlayers()) do
		local character = player.Character
		if not character then continue end
			
		local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
		if not humanoidRootPart then continue end
			
		local magnitude = (humanoidRootPart.Position - npc.Position).Magnitude
			
		if not closestMag then
			closestMag = magnitude
			closestPlayer = player
		end
			
		if magnitude >= closestMag then continue end
			
		closestMag = magnitude
		closestPlayer = player
	end

	local closestPlayerTorso = closestPlayer.Character:FindFirstChild("Torso")
	return closestPlayerTorso
end
1 Like

UPDATE: So I tried using the updated code you sent, but now when I do a test run it says "Attempt to index ‘nil’ with Character on line 26:

local closestPlayerTorso = closestPlayer.Character:FindFirstChild("Torso")

I tried disabling the follow script beforehand while doing an actual playtest, I turned it back on and at first it said “Position is not a valid member of Minion (Summoned NPC)”
I went back into the code and changed the npc to define the HumanoidRootPart of the summoned NPC itself rather than just the NPC, but now it’s saying “attempt to compare Vector3 <= Vector3” on line 20:

if magnitude >= closestMag then continue end

I’m not entirely sure what I’m doing wrong here? I retyped out the code basically word for word what you sent me (I didn’t wanna just copypaste it as I worry that that might be lazy) and I’m honestly not sure what the problem seems to be with the Vector3 stuff.

1 Like

In your code when you did the magnitude calculation, you compared the position of the player’s root part to the minions which is good, you just forgot the add .Magnitude to the Vectors to get the distance between them, change your line

local magnitude = (humanoidRootPart.Position - npc.Position)

to

local magnitude = (humanoidRootPart.Position - npc.Position).Magnitude

Don’t worry about it, hope this fixed the final issues with your tracking! Good luck with the project.

1 Like

It worked! The minion actually follows me now!
Though granted it might be impossible for me to test this out normally since it only follows other Players and not other NPCs so I will need to playtest and activate the script manually each time I want to test it but you did help me get through a massive roadblock! I will try to figure out the rest on my own, thank you again for helping me out with this! ^ ^

1 Like

Last tip for organizational reasons, in your workspace make a folder called NPCS or ActiveNPCS, and whenever you create a new one put the npc in that folder, then instead of having to loop through the entire workspace, you know exactly where all your npcs are. Glad I could help good luck!

1 Like

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