I made an A* pathfinding algorithm and am curious as to how I can optimize it. This algorithm is based on Sebastian Lague’s video on A* (https://www.youtube.com/watch?v=-L-WgKMFuhE). If anyone sees any optimizations that can be made, please inform me.
Video of Algorithm:
(While running: green=start, red=end, blue=closed, yellow=open.
When finished: red=closed, green=open, blue=path
Recorded with the following wait
variables: local INITIAL_DELAY = 3, local WAIT = 0.05, local WAIT_INTERVAL = 10 (every ten loops it waits 0.05 seconds)
)
Code:
local grid = script.Parent
local START_COLOR = Color3.fromRGB(0, 197, 0)
local END_COLOR = Color3.fromRGB(255,0,0)
local WALL_COLOR = Color3.fromRGB(17, 17, 17)
local DEFAULT_COLOR = Color3.fromRGB(163, 162, 165)
local LABELS = true
local startPiece = nil
local endPiece = nil
local walls = {}
for _, piece in grid:GetChildren() do
if not piece:IsA("Part") then continue end
--piece.Name = "Grid Piece"
if piece.Name ~= "WALL" then
if LABELS then
local gui = Instance.new("SurfaceGui")
gui.Name = "Cost Display"
gui.Face = Enum.NormalId.Top
gui.PixelsPerStud = 40
gui.Parent = piece
local gCost = Instance.new("TextLabel")
gCost.Size = UDim2.fromScale(0.2,0.2)
gCost.Name = "GCost"
gCost.TextScaled = true
gCost.BackgroundColor3 = Color3.fromRGB(255,255,255)
gCost.Parent = gui
local hCost = Instance.new("TextLabel")
hCost.AnchorPoint = Vector2.new(1, 0)
hCost.Size = UDim2.fromScale(0.2,0.2)
hCost.Position = UDim2.fromScale(1, 0)
hCost.Name = "HCost"
hCost.TextScaled = true
hCost.BackgroundColor3 = Color3.fromRGB(255,255,255)
hCost.Parent = gui
local fCost = Instance.new("TextLabel")
fCost.AnchorPoint = Vector2.new(0.5,0.5)
fCost.Position = UDim2.fromScale(0.5, 0.5)
fCost.Size = UDim2.fromScale(0.4, 0.4)
fCost.Name = "FCost"
fCost.TextScaled = true
fCost.BackgroundColor3 = Color3.fromRGB(255,255,255)
fCost.Parent = gui
end
piece:SetAttribute("GCost", 0)
piece:SetAttribute("HCost", 0)
end
if piece.Name == "START" then
if startPiece ~= nil then error("Only one grid piece named 'START' is allowed") end
startPiece = piece
piece.Color = START_COLOR
elseif piece.Name == "END" then
if endPiece ~= nil then error("Only one grid piece named 'END' is allowed") end
endPiece = piece
piece.Color = END_COLOR
elseif piece.Name == "WALL" then
table.insert(walls, piece)
piece.Color = WALL_COLOR
else
piece.Color = DEFAULT_COLOR
end
piece.Anchored = true
end
-----------------------------------
---------===PATHFINDING===---------
-----------------------------------
local function distanceFormula(a: Vector2, b: Vector2): number
return math.sqrt((a.X - b.X)^2 + (a.Y - b.Y)^2)
end
local function rawDistance(a: Vector2, b: Vector2): number
local dx = math.abs(a.X - b.X)
local dy = math.abs(a.Y - b.Y)
local D = 10 -- cost of straight movement
local D2 = 14 -- cost of diagonal movement
return D * (dx + dy) + (D2 - 2 * D) * math.min(dx, dy)
end
local function labelPiece(piece: Part)
local gCost = piece:GetAttribute("GCost")
local hCost = piece:GetAttribute("HCost")
local gui = piece:FindFirstChildWhichIsA("SurfaceGui")
gui:FindFirstChild("GCost").Text = gCost
gui:FindFirstChild("HCost").Text = hCost
gui:FindFirstChild("FCost").Text = gCost + hCost
end
if LABELS then labelPiece(startPiece) end
local function getPieceFromIndex(index: Vector2)
for _, v in grid:GetChildren() do
if not v:IsA("Part") then continue end
if v:GetAttribute("Index") == index then
return v
end
end
return nil
end
local function getNeighbors(piece)
local neighbors = {}
local directions = {
{ 0, -1 }, { 0, 1 }, { -1, 0 }, { 1, 0 }, -- straight
{ -1, -1 }, { -1, 1 }, { 1, -1 }, { 1, 1 } -- diagonals
}
local index = piece:GetAttribute("Index")
for _, dir in directions do
local nx = index.X + dir[1]
local ny = index.Y + dir[2]
local neighbor = getPieceFromIndex(Vector2.new(nx, ny))
if neighbor and neighbor.Name ~= "WALL" then
table.insert(neighbors, neighbor)
end
end
return neighbors
end
local open = {}
local closed = {}
local cameFrom = {}
table.insert(open, startPiece)
local current = startPiece
local i = 0
local INITIAL_DELAY = 3
local WAIT = 0.05
local WAIT_INTERVAL = 10 --how many loops before it waits, 1 for every loop
WAIT_INTERVAL = math.floor(WAIT_INTERVAL)
task.wait(INITIAL_DELAY)
while WAIT_INTERVAL <= 1 and task.wait(WAIT) or true do
i += 1
if WAIT_INTERVAL >= 1 and i%WAIT_INTERVAL == 0 then
task.wait(WAIT)
end
table.remove(open, table.find(open, current))
table.insert(closed, current)
current.Color = Color3.fromRGB(0,0,255)
if current == endPiece then
print("Path found :)")
break
end
for _, neighbor: Part in getNeighbors(current) do
if table.find(closed, neighbor) then continue end
local gCost = math.floor(distanceFormula(current:GetAttribute("Index"), neighbor:GetAttribute("Index"))*10) +
current:GetAttribute("GCost")
if gCost < neighbor:GetAttribute("GCost") or not table.find(open, neighbor) then
neighbor:SetAttribute("GCost", gCost)
neighbor:SetAttribute("HCost", rawDistance(neighbor:GetAttribute("Index"), endPiece:GetAttribute("Index")))
if LABELS then labelPiece(neighbor) end
neighbor.Color = Color3.fromRGB(255,255,0)
cameFrom[neighbor] = current
end
if not table.find(open, neighbor) then table.insert(open, neighbor) end
end
local lowest = math.huge
for _, piece in open do
local gCost = piece:GetAttribute("GCost")
local fCost = gCost + piece:GetAttribute("HCost")
if fCost < lowest then
lowest = fCost
end
end
local bestPieces = {}
for _, piece in open do
local fCost = piece:GetAttribute("GCost") + piece:GetAttribute("HCost")
if fCost <= lowest then
table.insert(bestPieces, piece)
end
end
local lastCurrent = current
if bestPieces[2] then
local bestPiece = nil
lowest = math.huge
for _, piece in bestPieces do
local hCost = piece:GetAttribute("HCost")
if hCost < lowest then
lowest = hCost
bestPiece = piece
end
end
current = bestPiece
else
current = bestPieces[1]
end
end
local path = {}
current = endPiece
while current ~= nil do
table.insert(path, current)
current = cameFrom[current]
end
for _, v in closed do
v.Color = Color3.fromRGB(255,0,0)
end
for _, v in open do
v.Color = Color3.fromRGB(0,255,0)
end
for _, v in path do
v.Color = Color3.fromRGB(0,0,255)
end
Thanks.