AI Pathfinding Avoiding Obstacles

local BuildingModule = require(game.ReplicatedStorage.Scripts.BuildingModule)

local Hotels = workspace.Hotels
local PathfindingService = game:GetService("PathfindingService")

local AI = {}

-- IDs for default R15 animations
local DEFAULT_WALK_ANIMATION_ID = "rbxassetid://913402848"  -- Default R15 walk animation
local DEFAULT_IDLE_ANIMATION_ID = "rbxassetid://507766666"  -- Default R15 idle animation

-- Function to create a straight-line path to the target grid part
function AI.createStraightLinePath(startPosition, targetPosition)
	local waypoints = {}

	-- Move along the X-axis first
	table.insert(waypoints, Vector3.new(targetPosition.X, startPosition.Y, startPosition.Z))

	-- Then move along the Z-axis
	table.insert(waypoints, Vector3.new(targetPosition.X, startPosition.Y, targetPosition.Z))

	return waypoints
end

-- Function to calculate distance between two positions
local function getDistance(pos1, pos2)
	return (pos1 - pos2).Magnitude
end

-- Function to find the closest grid part
local function getClosestGridPart(npc, gridParts)
	local closestPart = nil
	local minDistance = math.huge

	for _, gridPart in pairs(gridParts) do
		local distance = getDistance(npc.PrimaryPart.Position, gridPart.Position)
		if distance < minDistance then
			minDistance = distance
			closestPart = gridPart
		end
	end

	return closestPart, minDistance
end

-- Function to play animations
function AI.playAnimation(humanoid, animationId)
	local animator = humanoid:FindFirstChildOfClass("Animator")
	if animator then
		local animation = Instance.new("Animation")
		animation.AnimationId = animationId
		local animationTrack = animator:LoadAnimation(animation)
		animationTrack:Play()
		return animationTrack
	end
end

-- Function to stop animations
function AI.stopAnimation(animationTrack)
	if animationTrack then
		animationTrack:Stop()
	end
end

-- Function to move NPC through grid parts or freely
function AI.moveTo(plr, npc, targetPosition)
	local humanoid = npc:FindFirstChildWhichIsA("Humanoid")
	if not humanoid then
		warn("No humanoid found in NPC.")
		return
	end

	local floor = BuildingModule.getFloorFromObject(npc.PrimaryPart)

	if not floor then
		floor = 1
	end

	local gridParts = Hotels[plr.UserId].Floors[floor].FloorGrid.gridParts:GetChildren()
	local closestGridPart, distanceToGrid = getClosestGridPart(npc, gridParts)

	-- Define a threshold distance to decide when to switch to grid-based movement
	local gridProximityThreshold = 10  -- Adjust this value as needed

	local walkAnimTrack = AI.playAnimation(humanoid, DEFAULT_WALK_ANIMATION_ID)

	if distanceToGrid and distanceToGrid <= gridProximityThreshold then
		-- NPC is close to a grid part, move to the closest grid part and snap to it
		warn("[DEBUG] NPC is close to a grid part. Snapping to grid system.")
		npc.Humanoid:MoveTo(closestGridPart.Position)
		npc.Humanoid.MoveToFinished:Wait()

		-- Create a straight-line path to the target position in grid-like steps
		local waypoints = AI.createStraightLinePath(closestGridPart.Position, targetPosition)

		warn("[DEBUG] NPC is using the grid system to move in lines.")
		for _, waypoint in ipairs(waypoints) do
			npc.Humanoid:MoveTo(waypoint)
			npc.Humanoid.MoveToFinished:Wait()
		end
	else
		-- NPC is not close to a grid part, move freely
		warn("[DEBUG] NPC is moving freely, not using grid system.")
		npc.Humanoid:MoveTo(targetPosition)
		npc.Humanoid.MoveToFinished:Wait()
	end

	-- Stop walking animation and play idle animation
	AI.stopAnimation(walkAnimTrack)
	AI.playAnimation(humanoid, DEFAULT_IDLE_ANIMATION_ID)
end

return AI

I’ve made a AI Pathfinding system based off of waypoints I already have in my project.

The NPC’s walk in lines, however whenever there is a obstacle it fails to compute a new path.

Any advice?