How can I make my AI Pathfinding Smoother and Don't Stuck?

First, I’m sorry for my poor English skills. This is my first time working on AI Pathfinding, and I do not know about this. But I need to make an Enemy for my game, and I have some problems.

The problem is that Pathfinding doesn’t work as I need it to. NPC gets stuck, and the Visualize looks Messy (I use SimplePath - Pathfinding Module)

I watch a lot of YouTube tutorials, but they don’t seem to go well with my script, or I am just a dumb

This is the Code I use

-- --- {Properties}
local Properties = {
	--- Chase
	targetDistance = 50,
	CloseTargetDistance = 10,
	stopDistance = 2.5,

	--- Attack
	CloseAttackDistance = 10,
	RageAttackDistance = 17,
	AttackCooldown = 1,
}

--- {Services}
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local PathfindingService = game:GetService("PathfindingService")
local TweenService = game:GetService("TweenService")

--- {Module}
local SimplePath = require(game.ReplicatedStorage.Module.SimplePathModule)

--- {NPC Variables}
local NPC = script.Parent
local HRP = NPC.HumanoidRootPart
local Humanoid = NPC.Humanoid

HRP:SetNetworkOwner(nil)

local LastAttack = tick()

--- {Animation}
local animator = Humanoid:WaitForChild("Animator")
local idleAnim = game.ReplicatedStorage.Animation.Enemy.Test:WaitForChild("Idle")
local walkAnim = game.ReplicatedStorage.Animation.Enemy.Test:WaitForChild("Walk")
local SeekAnim = game.ReplicatedStorage.Animation.Enemy.Test:WaitForChild("Seek")

local idleTrack = animator:LoadAnimation(idleAnim)
local walkTrack = animator:LoadAnimation(walkAnim)
local SeekTrack = animator:LoadAnimation(SeekAnim)

idleTrack:Play()

--- {Pathfinding, RayCasting}
local pathParams = {
	AgentHeight = 2,
	AgentRadius = 3,
	AgentCanJump = false,
}

local rayParams = RaycastParams.new()
rayParams.FilterType = Enum.RaycastFilterType.Exclude
rayParams.FilterDescendantsInstances = {NPC}

local PathDB = false

function CanSeeTarget(Target)
	local Origin = HRP.Position
	local direction = (Target.HumanoidRootPart.Position - HRP.Position).Unit * Properties.targetDistance
	local ray = workspace:Raycast(Origin, direction, rayParams)
	
	if ray and ray.Instance then
		if ray.Instance:IsDescendantOf(Target) then
			return true
		else
			return false
		end
	else
		return false
	end
end

function NearestTarget()
	local playerList = Players:GetPlayers()
	local maxDistance = Properties.targetDistance
	local nearestPlayer
	local direction
	local distance


	for _, player in pairs(playerList) do
		if player.Character then
			local distanceVector = (player.Character.HumanoidRootPart.Position - HRP.Position)
			local target = player.Character
			local Gotdistance = (player.Character.HumanoidRootPart.Position - HRP.Position).Magnitude

			if Gotdistance < maxDistance and CanSeeTarget(target) then
				nearestPlayer = target
				distance = distanceVector.Magnitude
				direction = distanceVector.Unit
			end
		end
	end

	return nearestPlayer, distance, direction
end

local function pathFindTo(nearestPlayer)
	local path = SimplePath.new(NPC, pathParams)
	path.Visualize = true

	path:Run(nearestPlayer.HumanoidRootPart.Position)
end

function FoundTarget(nearestPlayer, distance, direction, path)
	
	pathFindTo(nearestPlayer)
	
	

	if distance <= Properties.CloseAttackDistance and tick() - LastAttack >= Properties.AttackCooldown then
		LastAttack = tick()
		print("Close")

	elseif distance > Properties.CloseAttackDistance and distance <= Properties.RageAttackDistance and tick() - LastAttack >= Properties.AttackCooldown then
		LastAttack = tick()
		print("Rage")
	end
end

function LostTarget(nearestPlayer, distance, direction)
	if walkTrack.IsPlaying then
		walkTrack:Stop()
	end

	Humanoid:Move(Vector3.new())
end

RunService.Heartbeat:Connect(function()
	local nearestPlayer, distance, direction = NearestTarget()
	
	if nearestPlayer then
		local NPCLook = NPC.Head.CFrame.LookVector
		local dotProduct = direction:Dot(NPCLook)
		
		if distance <= Properties.targetDistance and dotProduct > 0.5 and distance >= Properties.stopDistance then
			FoundTarget(nearestPlayer, distance, direction)
		elseif distance <= Properties.CloseTargetDistance and distance >= Properties.stopDistance then
			FoundTarget(nearestPlayer, distance, direction)
		else
			LostTarget(nearestPlayer, distance, direction)
		end
	end
end)

Humanoid.Running:Connect(function(movespeed)
	if movespeed <= 0 then
		if walkTrack.IsPlaying then
			walkTrack:Stop()
		end
	else
		if not walkTrack.IsPlaying then
			walkTrack:Play()
		end
	end
end)
3 Likes

Use roblox’s pathfinding not someone’s module.

1 Like

This module uses Pathfinding too, and I used Pathfinding before, But the Result was the same.

1 Like

Actually simplepath uses pathfinding and makes it much easier for beginners to get started with making AIs.

1 Like

The visualize function looks messy because you’re only supposed to create one path using the module.

local function pathFindTo(nearestPlayer)
	local path = SimplePath.new(NPC, pathParams)
	path.Visualize = true

	path:Run(nearestPlayer.HumanoidRootPart.Position)
end

here you make a new ‘pathfind’ everytime this function is fired which in the end makes it so you have a million of paths being calculated for a single npc.

Rather than doing this you instead should set the local path variable at the top of the script and only reference it in the pathFindTo function.

Like This?

local path

local function pathFindTo(nearestPlayer)
	path = SimplePath.new(NPC, pathParams)
	path.Visualize = true

	path:Run(nearestPlayer.HumanoidRootPart.Position)
end

No. As I said, SimplePath.new() returns you the ‘thing’ that you can later move and it’ll automatically pathfind. By calling SimplePath.new() every 0.001 seconds in the function you’re making a million of the ‘things’ which result in millions of paths.

You should only create ONE SimplePath.new() object per AI.

local Path_AI = SimplePath.new(NPC,pathParams)
Path_AI.Visualize = true

local function pathFindTo(nearestPlayer)
 Path_AI:Run(nearestPlayer.HumanoidRootPart)
end
1 Like

Oh, I got it now. I am so dumb. Thank you Very Much.

1 Like


whenever i use the run part of simple paths it works fine until the npc gets somewhere above me then it doesnt know what to do and just sits up there doing nothing

Isn’t the problem that when you are checking CanSeeTarget is a descendant of the target returning false when it hits a wall?

	if ray and ray.Instance then
		if ray.Instance:IsDescendantOf(Target) then
			return true
		else
			return false
		end
	else
		return false
	end

The ray shoots toward the target, but hits a wall and returns this section as false.

That’s because pathfinding can’t pathfind up or down, only if the vertical distance is less than 7 studs (so there have to be parts or trusses (?) for your npcs to jump on or jump off of), atleast from what I’ve seen and tested with my own games

Could that just be the NPC’s jump height setting in the Humanoid? What if you increase it?

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