Yes, you can copy and try this code for yourself.
REQUIREMENTS:
Use a Normal (Server) script.
Line12, 'Objects', a Model of all Parts / Walls in Workspace (BasePlate should not be a child of this) Line13, Just an NPC, with a 'Humanoid'
It's based on the A* algorithm.
Efficiency is NOT a requirement at the moment.
SELECTION BOX COLOUR CODES:
BLUE ' Open List '
RED 'Closed List'
Yellow 'Pathway picked'
Anyone with any knowledge on this algorithm, please let me know of anything I've missed.
PS. The *Heuristic *is on line 129
if not Workspace:FindFirstChild("BasePlate") then
BasePlate = Instance.new("Part",Workspace)
BasePlate.Name = "BasePlate"
BasePlate.FormFactor = "Custom"
BasePlate.Size = Vector3.new()
BasePlate.CanCollide = true
BasePlate.Anchored = true
BasePlate.CFrame = CFrame.new(0,-100,0)
else
BasePlate = Workspace.BasePlate
end
Objects = Workspace.Objects
char = script.Parent
char:MakeJoints()
if not Workspace:FindFirstChild("Nodes") then
Instance.new("Model",Workspace).Name = "Nodes"
end
Nodes = Workspace.Nodes
defaultNode = Instance.new("Part")
defaultNode.Name = "Node"
defaultNode.Transparency = 0
defaultNode.Anchored = true
defaultNode.CanCollide = false
defaultNode.FormFactor = "Custom"
defaultNode.Size = Vector3.new(.4,.4,.4)
--Instance.new("SelectionBox",defaultNode).Adornee = defaultNode
function isOpen(n,list)
for _,v in pairs(list) do if v == n then return true end end
return false
end
function isClosed(n,list)
for _,v in pairs(list) do
if v == n then return true end end
return false
end
castedFolder = Instance.new("Model",Workspace)
castedFolder.Name = "RayCastFolder"
rayP = Instance.new("Part")
rayP.Anchored = true
rayP.CanCollide = false
rayP.FormFactor = "Custom"
function isVisible(from,too, ignore)
ray = Ray.new(from.Position,(too.Position-from.Position).unit*999)
if not ignore then
hit,pos = Workspace:FindPartOnRayWithIgnoreList(ray, {char,castedFolder})
else
hit,pos = Workspace:FindPartOnRayWithIgnoreList(ray, {ignore,char,castedFolder})
end
local rp = rayP:Clone()
rp.Parent = castedFolder
rp.Size = Vector3.new(0,0,(pos-ray.Origin).magnitude)
rp.CFrame = CFrame.new(ray.Origin:Lerp(pos,0.5),pos)
if hit == too then return true else return false end
end
function UpdateNodes()
Nodes:ClearAllChildren()
for _,v in pairs(Objects:GetChildren()) do
if v:IsA("BasePart") and v~= Workspace.Target then
local div = 1
newNode = defaultNode:Clone()
newNode.Name = newNode.Name
newNode.Parent = Nodes
newNode.CFrame = v.CFrame * CFrame.new(v.Size.x/div,0,v.Size.z/div)
newNode = defaultNode:Clone()
newNode.Name = newNode.Name
newNode.Parent = Nodes
newNode.CFrame = v.CFrame * CFrame.new(-v.Size.x/div,0,v.Size.z/div)
newNode = defaultNode:Clone()
newNode.Name = newNode.Name
newNode.Parent = Nodes
newNode.CFrame = v.CFrame * CFrame.new(-v.Size.x/div,0,-v.Size.z/div)
newNode = defaultNode:Clone()
newNode.Name = newNode.Name
newNode.Parent = Nodes
newNode.CFrame = v.CFrame * CFrame.new(v.Size.x/div,0,-v.Size.z/div)
end
end
x = 0
nameTag = "Node"
for i,v in pairs(Nodes:GetChildren())do
if x < 10 then
v.Name = nameTag.."00"..x
elseif x >= 10 and x < 100 then
v.Name = nameTag.."0"..x
elseif x >= 100 and x < 1000 then
v.Name = nameTag..x
end
x = x + 1
end
end
function GetShortestPath(paths) print("GetShortest Path", #paths)
bestPath = nil
bestScore = math.huge
for i,path in pairs(paths) do wait(2)
castedFolder:ClearAllChildren()
score = 0
for x,p in pairs(path) do
if x == 1 then isVisible(char.Torso,p) end if x == #path and isVisible(p,Target) then end
if not lastP then lastP = p end
if isVisible(lastP,p) then
score = score + (p.Position-lastP.Position).magnitude + (p.Position-Target.Position).magnitude
-- print(score,bestScore)
end
lastP = p
end
if score > 0 and score < bestScore then
bestScore = score
bestPath = path
end
end
print("Check now")
wait(5)
if bestPath and #bestPath > 0 then
for _,n in pairs(bestPath) do wait(0.5)
local sb = Instance.new("SelectionBox",n)
sb.Adornee = n
sb.Transparency = 0
sb.Color = BrickColor.Yellow()
game.Debris:AddItem(sb,5)
end
else
bestPath = nil
end
if bestPath then --print("Returning best path", #bestPath.." steps")
return bestPath
else print("Returning nil path")
return {}
end
end
UpdateTime = 1
function Search(from,too) wait()-- processes = processes + 1 if processes > 50 then return end
local openList = {}
-- local closedList = {}
if from.Parent == Nodes then
table.insert(closedList,from)
if not from:FindFirstChild("SelectionBox") then
sb = Instance.new("SelectionBox",from)
sb.Adornee = from
sb.Color = BrickColor.Red()
game.Debris:AddItem(sb,UpdateTime)
end
end
if isVisible(from,Target,Nodes) then table.insert(newPath,from) return true end
if isVisible(too,Target,Nodes) then table.insert(newPath,too) return true end
if isVisible(from,too) then
table.insert(newPath,from)
table.insert(newPath,too)
end
for i,n in pairs(Nodes:GetChildren()) do
if not isClosed(n,closedList) and isVisible(from,n) then
table.insert(openList,n)
if not n:FindFirstChild("SelectionBox") then
sb = Instance.new("SelectionBox",n)
sb.Adornee = n
sb.Color = BrickColor.Blue()
game.Debris:AddItem(sb,UpdateTime)
end
end
end
for i,v in pairs(openList) do
for z,n in pairs(Nodes:GetChildren()) do
if n ~= v and isVisible(v,n) and not isClosed(n,closedList) then
if Search(v,n) then processes = processes + 1
print("Got "..processes.." path(s)")
table.insert(paths,newPath)
newPath = {char.Torso}
castedFolder:ClearAllChildren()
end
end
end
end
end
while true do print("Go")
castedFolder:ClearAllChildren()
Target = Workspace.Target
if isVisible(char.Torso,Target) then
FOUNDTARGET = true
char.Humanoid:MoveTo(Target.Position,BasePlate)
else
FOUNDTARGET = false
UpdateNodes()
newPath = {char.Torso}
closedList = {}
openList = {}
paths = {}
processes = 0
Search(char.Torso,Target)
print("Paths found["..#paths.."]")
-- coroutine.resume(coroutine.create(function()
if #paths> 0 then
for _,v in pairs(GetShortestPath(paths)) do wait(UpdateTime/5) print("Travelin' to ", v)
sb = Instance.new("SelectionBox",v)
sb.Adornee = v
sb.Color = BrickColor.White()
game.Debris:AddItem(sb,UpdateTime/5)
repeat wait()
castedFolder:ClearAllChildren()
overshoot = -(char.Torso.Position-v.Position).unit*4
char.Humanoid:MoveTo(v.Position + overshoot,BasePlate)
until (v.Position-char.Torso.Position).magnitude <= 4 or FOUNDTARGET or isVisible(char.Torso,Workspace.Target)
if isVisible(char.Torso,Workspace.Target) then break end
end
end
-- end))
end
wait(UpdateTime)
end

3 comments
dude 258 lines of code...
As I said, you can copy and test it.
I understand the algorithm but just post the algorithm.