As part of a larger project before I peace out again, I needed to create a node network system for custom pathfinding. I couldn’t use the Pathfinding service because I needed to restrict the network to certain areas. I also wanted to have multiple networked systems in the map.
And so I went a little crazy and built a plugin to do this for me. It lets me create nodes, connect them, and then import a pathfinder module to use for it.
The pathfinder “serializes” the nodes into Vector3 objects, with ObjectValues pointing to connected nodes.
I decided that I might as well release it because why not. I’m using this for an NPC system, but the below video shows a simplified use of it:
Remember that in that particular instance I had broken the link to the right side, so it was forced left.
Also it is using a very basic heuristic (linear distance to end point). So I’m sure that could play into it too. Wouldn’t be hard to adjust the heuristic. (This is A* btw.)
Linear heuristics in A* like to stick to walls that are closest to the end point.
Sorry to bother @sleitnick , but seems like after an update your plugin stopped working by an error with the custom Pathfinder module. I downloaded the version 13 and the custom module worked fine. Great plugin btw!
I tried to use this but it gives me this error on the pathfinding module:
attempt to perform arithmetic (sub) on number and Vector3
function FindNearestNode(position: Vector3): Node
local dist, nearest = math.huge, nil
for _,node in ipairs(pathNodes) do
local d = (position - node.Position).Magnitude ← it happens here
if (d < dist) then
dist, nearest = d, node
end
end
return nearest
end
If you place all of the nodes down and then decide that you need to move the map than all the path data is useless.
This is a little script I wrote to allows you to move the paths with the map. Not particularly complicated but could save someone a little bit of time.
--Paste this code into the command bar BEFORE you move map
--RefPoint is a part that you put somewhere on the map
--When you move the map make sure that RefPoint moves with the map
--When the movement is completed toggle the anchored property of RefPoint and Delete it
--Set NodeMapName to the same name as the node map in the plugin
local RefPoint = game.Workspace.RefPoint;
local NodeMapName = "Pathfinding"
local NodeMap = game.ReplicatedStorage.NodeMapProjects[NodeMapName];
for _,v in ipairs(NodeMap:GetChildren()) do
local OrginalCFrame = CFrame.new(v:GetAttribute("NodePosition"));
local RelativeCF = RefPoint.CFrame:ToObjectSpace(OrginalCFrame);
local Connection;
Connection = RefPoint:GetPropertyChangedSignal("Anchored"):Connect(function()
Connection:Disconnect();
v:SetAttribute("NodePosition",RefPoint.CFrame:ToWorldSpace(RelativeCF).Position);
end)
end
Hey @sleitnick I made a modification of this plugin by adding a grid option, I really liked your work but this feature was missing so I implemented myself! Feel free to use or upload this version since you are the owner of the project but pls give credits to me NodeMapPluginEdited.rbxm (23.3 KB)
Then still in plugin, hit the Import Pathfinder(this will make a module script using that folder’s name).
Make a script in Server scripts and put that module script in it(parent module to script).
Put code in the server script.
This code was taken from the Video above. Credits to sleitnick.
Code
local path = require(script.Pathfinder) -- get the pathfinder module script
local map = game.Workspace:WaitForChild("Map") -- change to Folder's name (with the nodes from plugin)
path:Init() -- do this first (important)
local gol = map:GetChildren()[math.random(1,#map:GetChildren())] -- gets random Node
local sta= -- start Position(change to humanoidRootPart I recon)
local gol = gol.Value -- Uses saved position in node (I have not tested this, so it might not work)
local rout = path:FindPath(sta,gol) -- calculates closest(not fastest) from start position to goal position
-- rout returns as a table with nodes' positions in it
if (rout) then -- shows path found (not necessary)
print(rout)
table.insert(rout,1,sta)
table.insert(rout,gol)
local seePFo = Instance.new("Folder",game.Workspace)
seePFo.Name = "SeePathF"
for i = 2,#rout do -- starts at two instead of one
local p1 = rout[i - 1] -- goes back one to get previous
local p2 = rout[i] -- current position
local p = Instance.new("Part",seePFo)
p.Anchored = true
p.TopSurface, p.BottomSurface = Enum.SurfaceType.Smooth, Enum.SurfaceType.Smooth
--p.Material = Enum.Material.SmoothPlastic
p.BrickColor = BrickColor.new("Neon orange")
p.Size = Vector3.new(1,1,(p2-p1).Magnitude)
p.CFrame = CFrame.new(p1:lerp(p2,0.5),p2)
end
else
print("no rout")
end
After that, Edit the code to work with a NPC.
something like:
local npc
local pathWay = require(script.Pathfinder)
for i = 1,#rout do -- starts at two instead of one
local p1 = rout[i]
npc:MoveTo(p1)
npc.MoveToFinished:Wait(2)
end
Take Note:exclamation: Code above may not work. If it does I’ll be shocked.
(short of like a little tutorial)
I hope this helps you, and Have a Blessed Day!