How do I stop NPCs from stacking onto other npcs heads?

Hi. Im making a battle game and there is two teams, blue and red. Sometimes i spawn more soldiers and enemies, they stacking onto other npcs heads. Here is the video: Video

Btw im using pathfinding.

3 Likes

is there a way to stop this? because its really annoying and sometimes npcs standing on enemies head and cant kill

1 Like

Also i disabled npc’s jump so npcs cant jump but they are still stacking onto other npcs heads

1 Like

Without code snippet I can’t 100% tell you but it looks to me as if you are pathfinding directly to the enemies HumanoidRootPart(the npc wants to go directly to the center position of the enemy). If you want the NPC to stay distant from the enemy you could calculate the magnitude between the path waypoints position and the enemy before you do the :MoveTo() and only move the npc if it isn’t too close and else not.

1 Like

You can do this with magnitude,

if (npcHumanoidRootPart.Position - target.HumanoidRootPart.Position).Magnitude > 5 then
	--move to function, wont fire function if the target is within 5 studs
end
1 Like

here is my code

local soldier = script.Parent
local basePart = soldier.Torso
local humanoid = soldier.Humanoid
local damageDelay = 0.5
local pathfindingservice = game:GetService("PathfindingService")
local runservice = game:GetService("RunService")
soldier.PrimaryPart:SetNetworkOwner(nil)



local function createPath(destination)
	local pathfindingservice = game:GetService("PathfindingService")
	local pathParameters = {
		AgentHeight = 5.162,
		AgentRadius = 2,
		AgentCanJump = false
		
		
	}
	local path = pathfindingservice:CreatePath(pathParameters)
	path:ComputeAsync(basePart.Position, destination.Torso.Position)
	
	for i, w in pairs(workspace.waypoints:GetChildren()) do
		w:Destroy()
	end
	local waypoints = path:GetWaypoints()
	for i, w in pairs(waypoints) do
		local waypoint = Instance.new("Part")
		waypoint.Name = tostring(table.find(waypoints,w))
		waypoint.Position = w.Position
		waypoint.CanCollide = false
		waypoint.Parent = workspace.waypoints
		waypoint.Shape = "Ball"
		waypoint.Size = Vector3.new(1, 1, 1)
		waypoint.Anchored = true
	end

	return path
end
local blockedTargets = {}
local target 



local function findTarget(blockedTargets)
	local targets = workspace.RedTeamSoldiers:GetChildren()
	
	if #targets ~= 0 then
		local maxDistance = math.huge
		local nearestTarget
		if targets then
			
			for i, t in pairs(targets) do
				if t.Humanoid.Health > 0 then
					if not table.find(blockedTargets,t) then
						if t.Torso then
							local distance = (basePart.Position - t.Torso.Position).Magnitude
							if distance < maxDistance then
								nearestTarget = t
								maxDistance = distance
							end	
						end
					else	
						local pathfindingservice = game:GetService("PathfindingService")
						local pathParameters = {
							AgentHeight = 5.162,
							AgentRadius = 2,
							AgentCanJump = false


						}
						local path = pathfindingservice:CreatePath(pathParameters)
						path:ComputeAsync(basePart.Position, t.Torso.Position)
						if path.Status == Enum.PathStatus.Success then
							table.remove(blockedTargets,table.find(blockedTargets,t))

						end
					end
				else
					t.Parent = workspace.DiedSoldiers
				end

			end
		else	
			nearestTarget = nil
		end
		return nearestTarget
	end
end
local result = {}
local damaged = Instance.new("BoolValue")
damaged.Value = false
damaged:GetPropertyChangedSignal("Value"):Connect(function()
	if damaged.Value == true then
		wait(damageDelay)
		damaged.Value = false
	end	
end)

function rotateTowards(target, this)
	local targetPosition = target.Position
	local thisPosition = this.Position

	local rotationTowards = CFrame.new(thisPosition, targetPosition)

	this.CFrame = this.CFrame:Lerp(rotationTowards, 0.5)
end

local function walkTo(destination)
	local path = nil
	local waypoints = nil
	while true do
		target = findTarget(blockedTargets)
		if destination ~= target then
			break
		end
		local distance = (basePart.Position - destination.Torso.Position).Magnitude

		if destination.Humanoid.Health > 0 then
			path = createPath(destination)
			waypoints = path:GetWaypoints() 
			if waypoints[2] and distance > 4 then
				
				humanoid:MoveTo(waypoints[2].Position)
				
			end
			if distance < 4 and damaged.Value == false then
				rotateTowards(destination.Torso,basePart)
				local anim = humanoid:LoadAnimation(script.SwordAnim)
				anim:Play()
			end
			soldier.SwordHandle.Touched:Connect(function(hit)
				if hit.Parent == destination  and damaged.Value == false then
					destination.Humanoid.Health -= 10
					damaged.Value = true
				end
				
			end)

		else
			break
		end
		if path.Status == Enum.PathStatus.NoPath then
			table.insert(blockedTargets,destination)
			return blockedTargets
		else
			for i, t in pairs(blockedTargets) do
				if t == destination then
					table.remove(blockedTargets,table.find(blockedTargets,t))
					if path.Status == Enum.PathStatus.NoPath then
						break
					end
				end
			end

		end
	end
	return blockedTargets

end

local function patrol()
	local targetList = workspace.RedTeamSoldiers:GetChildren()
	if #targetList > 0 then
		target = findTarget(result)

		if target then
			result = walkTo(target)
			
		end
	else

	end
	wait(0.00001)
end

while true do
	patrol()
end


oh by the way, im using torso to move enemies. Because before i use humanoidrootpart i got many errors (like “humanoidrootpart is not valid member of enemy”) so i decided to use torso.

im already using this to calculate distance

Why dont you just use Humanoid:MoveTo()? Is this a local script? (you should use :WaitForChild() so it doesnt say “HumanoidRootPart” is not a valid member of enemy

im using it, look at the walkto function, i used this. its local script.

but if i change torso to humanoidrootpart does this fix it?

I don’t understand this, why are you directly indexing one waypoint and not doing it with a for loop?

Because when i use loop, it doesnt update path everytime. I created a post before about that and 5uphi said follow next waypoint. Here is post: movetofinished is delaying

It sucks to say this about others people code, (since my code is also a bit rusty at some times),
but you should maybe rewrite it.

You can use this module to rewrite your pathfinding. It is more reliable than robloxs’ default pathfinding service (as it is easy to use and wont mess with your code, and i saw the post you mentioned, it updates with the characters position as well).

Ok I understand, if you recompute it it makes sense. The distance > 4 you are using is the distance before the NPC was moved but you need to calculate the distance the NPC would have if it went to the waypoint because else it would be too late.

Basically if the position between the NPC and enemy was 6 Studs but the waypoint is 1 stud away from the enemy it would move the NPC to the waypoint but afterwards there’s only 1 Stud between NPC and enemy.

if waypoints[2] then
local waypoint = waypoints[2]
local waypointDistance = (waypoint.Position - destination.Torso.Position).Magnitude

if waypointDistance < 4 then
	humanoid:MoveTo(waypoint.Position)
end
end

Something like this, also I recommend you try avoid nesting (many If’s/Indentions in each other) then your code looks much cleaner and is easy to undertand. Basically you want to do the opposite first like

if #targets == 0 then return end
local maxDistance = math.huge
-- etc
if not targets then 
   nearestTarget = nil
   return
end
-- rest which would be in the if targets

Well… just so you know, you should be using server scripts for NPCs, and local scripts for players. (There is an exception if you need to use remote events, but im pretty sure local scripts are made just for client, or the player, not server, or NPCs because a NPC is not an actual player)

I changed distance to waypoint distance but it still not working they are stacking onto other npcs heads

here is my updated code

local soldier = script.Parent
local basePart = soldier.Torso
local humanoid = soldier.Humanoid
local damageDelay = 0.5
local pathfindingservice = game:GetService("PathfindingService")
local runservice = game:GetService("RunService")
soldier.PrimaryPart:SetNetworkOwner(nil)



local function createPath(destination)
	local pathfindingservice = game:GetService("PathfindingService")
	local pathParameters = {
		AgentHeight = 5.162,
		AgentRadius = 2,
		AgentCanJump = false
		
		
	}
	local path = pathfindingservice:CreatePath(pathParameters)
	path:ComputeAsync(basePart.Position, destination.Torso.Position)
	
	for i, w in pairs(workspace.waypoints:GetChildren()) do
		w:Destroy()
	end
	local waypoints = path:GetWaypoints()
	for i, w in pairs(waypoints) do
		local waypoint = Instance.new("Part")
		waypoint.Name = tostring(table.find(waypoints,w))
		waypoint.Position = w.Position
		waypoint.CanCollide = false
		waypoint.Parent = workspace.waypoints
		waypoint.Shape = "Ball"
		waypoint.Size = Vector3.new(1, 1, 1)
		waypoint.Anchored = true
	end

	return path
end
local blockedTargets = {}
local target 



local function findTarget(blockedTargets)
	local targets = workspace.RedTeamSoldiers:GetChildren()
	
	if #targets ~= 0 then
		local maxDistance = math.huge
		local nearestTarget
		if targets then
			
			for i, t in pairs(targets) do
				if t.Humanoid.Health > 0 then
					if not table.find(blockedTargets,t) then
						if t.Torso then
							local distance = (basePart.Position - t.Torso.Position).Magnitude
							if distance < maxDistance then
								nearestTarget = t
								maxDistance = distance
							end	
						end
					else	
						local pathfindingservice = game:GetService("PathfindingService")
						local pathParameters = {
							AgentHeight = 5.162,
							AgentRadius = 2,
							AgentCanJump = false


						}
						local path = pathfindingservice:CreatePath(pathParameters)
						path:ComputeAsync(basePart.Position, t.Torso.Position)
						if path.Status == Enum.PathStatus.Success then
							table.remove(blockedTargets,table.find(blockedTargets,t))

						end
					end
				else
					t.Parent = workspace.DiedSoldiers
				end

			end
		else	
			nearestTarget = nil
		end
		return nearestTarget
	end
end
local result = {}
local damaged = Instance.new("BoolValue")
damaged.Value = false
damaged:GetPropertyChangedSignal("Value"):Connect(function()
	if damaged.Value == true then
		wait(damageDelay)
		damaged.Value = false
	end	
end)

function rotateTowards(target, this)
	local targetPosition = target.Position
	local thisPosition = this.Position

	local rotationTowards = CFrame.new(thisPosition, targetPosition)

	this.CFrame = this.CFrame:Lerp(rotationTowards, 0.5)
end

local function walkTo(destination)
	local path = nil
	local waypoints = nil
	while true do
		target = findTarget(blockedTargets)
		if destination ~= target then
			break
		end
		local distance
		if waypoints then
			distance = (waypoints[2].Position - destination.Torso.Position).Magnitude
		end
		if destination.Humanoid.Health > 0 then
			path = createPath(destination)
			waypoints = path:GetWaypoints() 
			if distance then
				if waypoints[2] and distance > 4 then

					humanoid:MoveTo(waypoints[2].Position)

				end
				if distance < 4 and damaged.Value == false then
					rotateTowards(destination.Torso,basePart)
					local anim = humanoid:LoadAnimation(script.SwordAnim)
					anim:Play()
				end
			end
			soldier.SwordHandle.Touched:Connect(function(hit)
				if hit.Parent == destination  and damaged.Value == false then
					destination.Humanoid.Health -= 10
					damaged.Value = true
				end
				
			end)

		else
			break
		end
		if path.Status == Enum.PathStatus.NoPath then
			table.insert(blockedTargets,destination)
			return blockedTargets
		else
			for i, t in pairs(blockedTargets) do
				if t == destination then
					table.remove(blockedTargets,table.find(blockedTargets,t))
					if path.Status == Enum.PathStatus.NoPath then
						break
					end
				end
			end

		end
	end
	return blockedTargets

end

local function patrol()
	local targetList = workspace.RedTeamSoldiers:GetChildren()
	if #targetList > 0 then
		target = findTarget(result)

		if target then
			result = walkTo(target)
			
		end
	else

	end
	wait(0.00001)
end

while true do
	patrol()
end


Mind If I ask is this totally accurate battle simulator in roblox?

yes, im making totally accurate battle simulator :smile:

You are calculating the distance from the old waypoints[2] to the enemy, calculate the distance after you’ve created the new path.