Humanoid ignoring new path? A* Algorithm

Hi, It looks like my humanoid is ignoring when the goal route changes , the path should change too, but it doesn’t, is there any way around this?, since i don’t want to remove MoveToFinished:wait() , i am using NodeGraphV2 Plugin, anyone knows why this happens ?

My script :

task.wait(.1)
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Nodegraphs = require(ReplicatedStorage:WaitForChild("NodeGraphs"))

local character = script.Parent
local humanoid: Humanoid = character:FindFirstChildWhichIsA("Humanoid")

local EndPart = workspace:WaitForChild("endd")
local path
local destructor
local pos: Vector3
local goal: Vector3
pos = character:GetPivot().Position

local previousIndexCount: number

while task.wait(.1) do

	if destructor then
		destructor()
	end
	
	pos = character:GetPivot().Position
	goal = EndPart.Position
	
	path = Nodegraphs.NewGraph:getPath(goal,pos)
	if path then
		previousIndexCount = #path.path
	else
		previousIndexCount = 1
	end
	
	destructor = Nodegraphs.NewGraph:drawPath(path)

	--humanoid:MoveTo(path.path[1].Position)
	--humanoid.MoveToFinished:Wait()
	if path then
		for index,node in pairs(path.path) do
			if index > 1 then 
				if index <= #path.path then
				--	print(index, node.Position)
					humanoid:MoveTo(path.path[tonumber(index)].Position)	
				end
				humanoid.MoveToFinished:Wait()
				if index >= previousIndexCount then
					humanoid:MoveTo(path.path[tonumber(#path.path)].Position)
				end
				previousIndexCount = #path.path
			else
				humanoid:MoveTo(goal)
			end
		end
		warn("Goal Finished")
	else
		humanoid:MoveTo(goal)
	end
end

NodeGraphs Module :

--[[ NodeGraphs 

	Automatically populate NodeGraphs with tagged NodeGraph folders
	
	Example:

	local nodeGraphs = require(PATH)
	local nodeGraph = nodeGraphs.NewGraph


	
	Optional helper API with fast A* pathfinding:

		nodeGraph:draw() 
		
			-> draws the nodegraph, same as what you see in the plugin

		nodeGraph:getPath(startPosition : Vector3,endPosition: Vector3)
			-> returns the closest path
	
			example:
			
				local path = nodeGraph:getPath(START,END)
				for index,node in path.path do
					print(index,node.Position)
				end
		
		nodeGraph:drawPath(path)
			-> draws a path and returns a destructor
			
			example:
					
					local path = nodeGraph:getPath(START,END)
					Maid:Add(nodeGraph:drawPath(path))
					Maid:Cleanup()
		
		nodeGraph:getClosestNode(position : Vector3)
			-> returns the closest node
]]





local CollectionService = game:GetService("CollectionService")
local HttpService = game:GetService("HttpService")

local TAG = "NodeGraph"

local NodeGraphs = {}
local Pathfinder = require(script.Pathfinder)
local Resources = script.Resources

for i,folder in CollectionService:GetTagged(TAG) do 
	--[[ Init nodes ]]
	local newNodeGraph = {
		nodes = {},
		getPath = function(self,...)
			return Pathfinder:_getPath(self,...)
		end,
		drawPath = function(self,path)
			return Pathfinder:_drawPath(path)
		end,
		getClosestNode = function(self,pos)
			local lastdist=999
			local closest
			for i,node in self.nodes do
				local dist = (node.Position - pos).magnitude
				if dist<lastdist then
					lastdist=dist
					closest=node
				end
			end
			return closest
		end,
		draw = function(self)
			if self.Drawn then
				return
			end
			self.Drawn = true
			for i,dataNode in folder.Data.Nodes:GetChildren() do 
				local placementNode = Resources.Node:Clone()
				local placedNode = placementNode:Clone()
				placedNode.Name = dataNode.Name
				placedNode.Position = dataNode:GetAttribute("Position")
				placedNode.Parent = folder.Working.Nodes
			end
			for i,dataLine in folder.Data.Lines:GetChildren() do 
				local nodeA = dataLine:GetAttribute("NodeA")
				local nodeB = dataLine:GetAttribute("NodeB")
				local nodeAObject = folder.Working.Nodes:FindFirstChild(nodeA)
				local nodeBObject = folder.Working.Nodes:FindFirstChild(nodeB)
				if nodeAObject and nodeBObject then
					local line = Resources.Line:Clone()
					line.Name = dataLine.Name
					local dist = (nodeBObject.Position - nodeAObject.Position).Magnitude
					local size = Vector3.new(0.1,0.1,dist)
					local cf = CFrame.lookAt(nodeAObject.Position,nodeBObject.Position,Vector3.yAxis) * CFrame.new(0,0,-dist/2)
					line.CFrame = cf 
					line.Size = size 
					line.Parent = folder.Working.Lines
				end
			end
		end,
	}
	
	for i,nodeFolder in folder.Data.Nodes:GetChildren() do
		local node = {
			Position = nodeFolder:GetAttribute("Position"),
			Connections = {}
		}
		newNodeGraph.nodes[nodeFolder.Name] = node
	end
	--
	
	--[[ Init connections ]]
	
	
	for i,lineFolder in folder.Data.Lines:GetChildren() do 
		local nodeA = lineFolder:GetAttribute("NodeA")
		local nodeB = lineFolder:GetAttribute("NodeB")
		local nodeAObj = newNodeGraph.nodes[nodeA]
		local nodeBObj = newNodeGraph.nodes[nodeB]
		if nodeAObj and nodeBObj then
			table.insert(nodeAObj.Connections,nodeBObj)
			table.insert(nodeBObj.Connections,nodeAObj)
		end
	end
	
	if NodeGraphs[folder.Name] then
		folder.Name = "NodeGraph"..HttpService:GenerateGUID(false)
	end
	NodeGraphs[folder.Name] = newNodeGraph	
end


return NodeGraphs

Pathfinder Module :

--[[ A* Pathfinder for Nodegraphs ]]


local Pathfinder = {
	PathCache = {}
}
local Heap = require(script.Heap)

function Pathfinder:_init(nodeGraph)
	for i,node in nodeGraph.nodes do 
		node.gCost = 0
		node.hCost = 0
		node.Enabled = true
	end
	nodeGraph.PathfinderInit = true
end
function Pathfinder:_getClosestNode(nodeGraph,pos)
	if not nodeGraph.PathfinderInit then
		self:_init(nodeGraph)
	end
	local lastdist=999
	local closest
	for i,node in nodeGraph.nodes do
		if node.Enabled then
			local dist = (node.Position - pos).magnitude
			if dist<lastdist then
				lastdist=dist
				closest=node
			end
		end
	end
	return closest
end
function Pathfinder:_drawPath(path)
	if not path then
		return
	end
	local nodes = path.path 
	
	local lines = {}
	local function drawLine(posA,posB)
		local nodepos = posB
		local lastnodePos = posA
		local dist = (nodepos - lastnodePos).magnitude
		local line = Instance.new("Part",workspace.Terrain)
		line.Anchored = true
		line.CanCollide = false
		line.Transparency = 0.5
		line.Color = Color3.new(0.333333, 0.333333, 1)
		line.Material = "Neon"

		local thickness = 0.85
		line.Size = Vector3.new(thickness,thickness,dist)
		line.CFrame = CFrame.new(lastnodePos,nodepos) * CFrame.new(0,self.DebugYOffset,-dist/2)
		table.insert(lines,line)
	end

	local lastnode

	for i = 1,#nodes do
		local node = nodes[i]
		if lastnode then
			drawLine(lastnode.Position,node.Position)
		end
		lastnode = node
	end
	return function()
		for i,line in lines do 
			line:Destroy()
		end
	end
end
function Pathfinder:_getPath(nodeGraph,fromPos,toPos)
	if not nodeGraph.PathfinderInit then
		self:_init(nodeGraph)
	end
	
	local fromNode = self:_getClosestNode(nodeGraph,fromPos)
	local toNode = self:_getClosestNode(nodeGraph,toPos)
	if not fromNode or not toNode then
		return
	end
	
	local Cache = self.PathCache
	if not Cache[fromNode] then
		Cache[fromNode] = {}
	end

	local path = Cache[fromNode][toNode]

	if path == nil then
		path = self:_pathfind(fromNode,toNode)
		Cache[fromNode][toNode] = path		
	end
	return path
end
function Pathfinder:_pathfind(fromNode,toNode)	
	if not fromNode or not toNode then
		return
	end
	local gCosts = {}
	local hCosts = {}
	local parents = {}

	gCosts[fromNode] = 0
	hCosts[fromNode] = (toNode.Position - fromNode.Position).Magnitude

	local function retracePath()
		local path = {}

		local target = toNode
		local angleSum = 0

		while target ~= fromNode do
			path[#path + 1] = target

			local parent = parents[target]
			local grandParent = parent and parents[parent]

			if parent and grandParent then
				local dir0 = (parent.Position - grandParent.Position).Unit
				local dir1 = (target.Position - parent.Position).Unit
				angleSum += math.acos(dir0:Dot(dir1))
			end

			target = parent
		end

		path[#path + 1] = fromNode

		return {
			path = path,

			StartNode = fromNode,
			EndNode = toNode,

			Cost = gCosts[toNode],
			AngleSum = angleSum
		}
	end

	local function getFCost(node)
		-- Greedy BFS
		local gCostInfluence = 1
		return (gCosts[node] * gCostInfluence) + hCosts[node] 
	end

	if fromNode == toNode then
		return retracePath()
	end

	local Open = Heap.new()
	local Closed = {}

	Open:Add(fromNode, getFCost(fromNode))
	

	while Open.Size > 0 do
		local currentNode = Open:Pop()

		if currentNode == toNode then
			return retracePath()
		end

		Closed[currentNode] = true
		local currentCost = gCosts[currentNode]

		for _,adjacentNode in currentNode.Connections do			
						
			if Closed[adjacentNode] or not adjacentNode.Enabled then
				continue
			end
					

			local costToNeighbor = currentCost + (adjacentNode.Position - currentNode.Position).Magnitude 
			local previousCost = gCosts[adjacentNode]

			if not previousCost or costToNeighbor < previousCost then
				gCosts[adjacentNode] = costToNeighbor
				parents[adjacentNode] = currentNode

				if not previousCost then
					hCosts[adjacentNode] = (toNode.Position - adjacentNode.Position).Magnitude 
				end 

				Open:Add(adjacentNode, getFCost(adjacentNode))
			end
		end
	end
end



return Pathfinder

This is happening because you never explicitly check if the EndPart position has changed during the pathing.

Compare the current position of the part to the goal position every time the MoveToFinish:Wait() ends, if it changes: recalculate the path.

The goal value doesn’t update when you move the part. If the part is at 0, 1, 0 and you set goal to part.Position: goal is going to be at 0, 1, 0 until goal is assigned to again, even if you move the original part you got the position from.

Hope this helps.

1 Like

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