SimplePath - Pathfinding Module

so what event does this module fire when it finished moving?

The Path.Reached event fires after the goal is reached.

Zombie AI [Sorry, if I am repeating points, but Review is always good anyway…]

ROBLOX PATHFINDING:

  • The Path (Waypoints) are the least important bit of Pathfinding, as one is going to only use the first 1 or 2 Waypoints, before getting a new Path;
    unless one wants an AI to “patrol” some Path, before switching to a follow Player routine…

  • Pathfinding may have changed, but last I scripted it, one is going to only use every-other waypoint to allow for diagonal movement of the zombie.

  • The important bit is to check for an error after getting Path; then handling the error and getting another Path.
    This code somewhat handles the two most important errors [Hacky, because I’m bad at math; Logic is my strong suit]

FailFinishNotEmpty is returned from Path, when the endpoint is unobtainable (Maybe the endpoint is inside of a Part, or there just is no Path). I just move the EndPoint closer to the AI, and try again. .

The others are when Startpoint is no good. Maybe the AI is already standing too close to a wall. I move the StartPoint to different places around the AI, and try again…

function ReturnPath(start, finish, MaxDist)

local path = pathfinding:ComputeRawPathAsync(start, finish, MaxDist)
print (path.Status)

if path.Status == Enum.PathStatus.FailFinishNotEmpty then
	local dist = (finish - start).magnitude
	while dist > 8 and path.Status == Enum.PathStatus.FailFinishNotEmpty do

wait()
finish = start + (finish - start)/1.5
path = pathfinding:ComputeRawPathAsync(start, finish, MaxDist)
dist = (finish - start).magnitude

		local part = Instance.new("Part", Workspace)

	part.FormFactor = Enum.FormFactor.Custom
	part.Size = Vector3.new(4,4,4)
	part.CFrame = CFrame.new(finish)
	part.Shape = "Ball"
	part.Anchored = true
	part.CanCollide = false
	part.BrickColor = BrickColor.Red() 
	table.insert(Showed, part)

	end -- room to manuveur?
	Bot.ShowPath (start, finish)
end -- finish error

if path.Status == Enum.PathStatus.FailStartNotEmpty
or 	   path.Status == Enum.PathStatus.ClosestNoPath	then

wait()
start = start + Vector3.new(0,0,4)
path = pathfinding:ComputeRawPathAsync(start, finish, MaxDist)

	if  path.Status == Enum.PathStatus.FailStartNotEmpty
	or  path.Status == Enum.PathStatus.ClosestNoPath then
		start = start + Vector3.new(4,0,-4)
		path = pathfinding:ComputeRawPathAsync(start, finish, MaxDist)
	end
	if  path.Status == Enum.PathStatus.FailStartNotEmpty
		or  path.Status == Enum.PathStatus.ClosestNoPath  then
		start = start + Vector3.new(-4,0,-4)
		path = pathfinding:ComputeRawPathAsync(start, finish, MaxDist)
	end
		
	if  path.Status == Enum.PathStatus.FailStartNotEmpty
		or  path.Status == Enum.PathStatus.ClosestNoPath  then
		start = start + Vector3.new(-4,0,-4)
		path = pathfinding:ComputeRawPathAsync(start, finish, MaxDist)
	end
	
	Hum:MoveTo(start)

wait()

end -- start error?

return path
end–Return Path

You will probably come-up with better ways to handle these errors…

Other methods work as well or better:

RAYCASTING

Wall Hugging to get out of a maze:
Just shooting one Ray in the direction of Nearest Player and checking for obstruction and then turning left continually recursively, and always checking (after, maybe after a half second or so) if one can get out of the recursion by turning right; works great;
with the disadvantage that the zombie will not take a diagonal tact, until actually reaching the wall.

This model used to do that; also fired a ray diagonally upwards to see if he could jump obstacle; fired a Ray in front and directly down to check for high cliffs or the edge of the world; checked, if a ladder was the obstacle and then just kept walking “Forward” (He will automatically climb).
*It’s important to just keep walking forward, until a “We are not getting anywhere” check (Time-out or Positionally) is True…

Speed AI by RealAi Inc. - Brains 4 Zombies - Roblox

  • BREAD CRUMBS

Surprisingly; The shortest Script, which actually looks great (Zombie looks like an actual Player) and works very well at close or medium distances; is having the Nearest Torso drop “bread crumbs” continually of his Position; then having the zombie just go from one “crumb” to the next.

Importants:

  • Player only drops a new crumb, after moving far enough away from the current crumb (OR: I just treated the World like a 3D checkerboard of, maybe (3 or 4, 3,3), and only dropped a new crumb, when Player moved to another square.)

EDIT: Bad Explanation: A “crumb” actually holds the co-ordinates of the next crumb. And in my Checkerboard method, the crumbs were actual anchored, invisible Parts, I think, which deleted themselves after a while, and could be treated non-sequentially: A NEW Nearest Torso (Player), or even the same Player might cross the path of the old crumbs, and replace the old info (of “Next Crumb”), with newer info. The entire exercise was just Too Fun…

  • When checking for “Are we there yet” before going to next crumb; strip the Y value from the .Magnitude check; turning the goal from a sphere, into a cylinder of infinite height.
    (Almost everyday, I see a post, which says “.Magnitude is not accurate, and I can never ask if I am closer than 3 studs from the target”, That is because they are comparing the Player’s Torso to a Goal, on the on the baseplate, which is ALWAYS at least 3 studs away in height form the Torso

  • To jump; just check if the next crumb is in a higher square…

It’s important to use two or three of these methods in the same AI. If one fails; move on to the next.
Also; if zombie is very close to the Player, skip them and just go straight at him.; else zombie may take a detour and run from Player…

[I may add to this if I remember more…]

GL

P.S. If you want to learn to program, or are a really good artist; join me. I’m making the first wargame om the net, which is on an actual globe, instead of a donut-shaped (Toroid) planet… (which is the actual shape if you let the player wrap from the right of the map to the left, and from the N. Pole to S. Pole)
It’s gonna a be wic…, and has nothing to do with Pathfinding (except on a sphere of coarse)…

1 Like

Heya, I’m having trouble with the SimplePath module.

It seems like the path doesn’t compute the path correctly and it creates some funky stuff with it.
When I set AgentCanJump to true, it computes normally.
Though when I set AgentCanJump to false, it almost doesn’t even compute a path at all.

AgentCanJump true
AgentCanJump false

Is the script that I wrote the problem here?
Is it just the PathfindingService bugging out?

Thanks for reading :slight_smile:

This is the first time I’ve seen something like what you’re experiencing. Do you mind sharing just your pathfinding code to analyze the problem?

It’s a script made for a Follow & Kill AI in my game, it’s not finished though.
I think it might be a problem with the script itself though, not the pathfinding, but I’m just checking just in case.

local function PathToTarget()
	local PreviousTime	=	tick()
	local Cooldown		=	Tech_Settings:GetAttribute("FollowCooldown")
	local Attempts		=	Tech_Settings:GetAttribute("FollowAttempts")
	
	if Cooldown > 0 and math.floor(Attempts) > 0 then
		spawn(function()
			while Target_RootPart and not Target_Visible do
				local CurrentTime = tick()
				
				if CurrentTime < PreviousTime+Cooldown and Attempts > 0 then
					PreviousTime = CurrentTime
					
					SimplePath:Run(Target_RootPart)
					Attempts -= 1
				elseif CurrentTime >= PreviousTime+Cooldown and Attempts <= 0 then
					break
				else
					Services.Run.Heartbeat:Wait()
				end
				
			end

			if not Target_Visible then
				Target_RootPart = nil
			else
				SimplePath:Stop()
			end
		end)
	end
end

By the way just mentioning, thanks a ton for trying to help :slight_smile:

Update, I used the Path.Error event and apparently it’s a problem with the path computing thing.
It returns ComputationError and sometimes LimitReached.

You can refine your script by using the return value of Path:Run(). The return value is false if there is an error in the path computation or otherwise. You can directly handle this in your code. Also, make use of the Path.LastError if required. Furthermore, the module automatically yields between consecutive computations. You don’t need to add a cooldown yourself. I hope your problem is fixed. If not, feel free to contact me further and I can look into it in more detail.

The problem is that I don’t know how to fix the path computations.
I mean, there is a way that the AI can get to the given position, but it just doesn’t do that correctly and makes a wrong path.

PM me the problem and we can look into it in more detail.

I’m trying to recreate the little Humanoid demo provided in the examples, however even though I’ve copied the code verbatim I can’t get it to work. I’ve added a basic R6 dummy to test with but it won’t move at all. Error event always returns TargetUnreachable for some reason no matter where I move the goal. I’m working in a fresh baseplate with no obstacles. I tried both methods, like I mentioned when using the events method the Error event returns TargetUnreachable. When using the loop method the dummy also doesn’t move.

*EDIT: Geez, I’ve figured out why my dummy wasn’t moving. Its root was anchored lol

3 Likes

Lovely module, I’ve had a great time using. Just a tiny problem, stopping the movement of the humanoid with path:Stop() doesn’t really work? I have mine in WaypointReached, stopping the pathfinding after it reaches a certain distance away from the goal, yet it keeps running either way.

EDIT: Exactly 2 seconds after I posted this I realized I left a path:Run() right after stopping. I am sorry. Please keep up the good work with the module!

I’m creating a Right Click to Follow mechanic where you can right click on another player and have an option to follow them. I’ve got it working, I can follow the player and unfollow (by pressing WASD or Space).

I’m storing each players Path and Heartbeat connection in a data table which I use to :Stop() and :Run() existing paths. I also :Stop() and :Disconnect() the connections when a player is not following.

The issue I’m experiencing is, only after a player stops following, their movement becomes kinda choppy. I was wondering if anybody had any ideas as to what’s causing this behavior.

Here’s the code which handles the pathfinding:

rEvt.OnServerEvent:Connect(function(player, action, targetChar)
	if action == "Follow" then
		for _, v in pairs(data) do
			if player == v.Player then
				-- Data already exists, change path goal
				v.Path:Stop()
				v.Path:Run(targetChar.HumanoidRootPart.Position - Vector3.new(0, 0, 5))
				
				player:SetAttribute("IsFollowing", true)
				
				return -- Return to make sure new data is not assigned
			end
		end
		
		-- Assign new data
		local Path = SimplePath.new(player.Character)

		local connection = game:GetService("RunService").Heartbeat:Connect(function()
			Path:Run(targetChar.HumanoidRootPart.Position - Vector3.new(0, 0, 5))
			task.wait()
		end)
		
		player:SetAttribute("IsFollowing", true)

		table.insert(data, {
			["Player"] = player,
			["Path"] = Path,
			["HeartbeatConn"] = connection;
		})
	else
		if player:GetAttribute("IsFollowing") then
			for i, v in pairs(data) do
				if player == v.Player then
					-- Stop path and disable connection
					v.Path:Stop()
					v.HeartbeatConn:Disconnect()
					player:SetAttribute("IsFollowing", nil)
				end
			end
		end
	end
end)

When the character resets the choppiness of the movement goes away.

I’m not sure what could be the root cause but my theory is it’s got to do with how I’m stopping/running the path? Or could it be that this mechanic is not possible with this module because of something internal?

Nice pathfinding code lol
I like it a lot.

this is really cool!
i’m relatively new to scripting, i just wanna ask if there’s a way to make the NPC wander around? would i have to make several waypoints?

PM me what is happening visually so I get a better understanding of the problem.

You would have to get a random position anywhere on the surface of the part and pathfind to that position then repeat.

Edit: You can even place parts around the map and have those as the final targets. You would randomly select one and pathfind to it.

1 Like

awesome, thank you very much, i’ll do the multiple-part waypoints!

Update 2.1

  • Fixed error when Path is destroyed
  • Fixed error if Path is still referenced after being destroyed
1 Like

Hello! how do I correctly use Path:Stop() ?
I assume while pathfinding and running towards to a point I can stop the path and make the agent stop.
for some reason pathfinding still detect as idle?