· 5 years ago · May 16, 2020, 01:44 AM
1-- Grid-based A* Pathfinding System
2-- https://www.youtube.com/watch?v=fUiNDDcU_I4 First 3 mins of this video explains the system used
3-- https://www.roblox.com/games/5038514434/LordOfLuxurys-Place-Number-387 (May time out after a few minutes because it's looping through so quickly, the actual game will not be like this)
4
5local char = workspace["Prof. Plum"] -- Character model that is moving
6local board = workspace.FloorTiles
7
8-- Create a table of all available spots on the map to move to
9local Tiles = {}
10
11for i,v in pairs(board:GetChildren()) do
12 if v.Name == "Tile" then
13 table.insert(Tiles, v)
14 end
15end
16
17-- Function for the pathfinding
18local function CalculatePath(Tile, StepsAvailable) -- Tile is the end position, TileOn is where the character will walk from
19 local StepsUsed = 0 -- Counter for how many steps have already been used
20 local Waypoints = {} -- List of waypoints
21 --local BrokenCooldown = false
22 local IndexCounter = 0 -- What value to index it on
23
24 -- Locate the tile that the character is standing on
25 local function FindTileOn()
26 local Target = CFrame.new(char.HumanoidRootPart.Position.X, char.HumanoidRootPart.Position.Y - 4, char.HumanoidRootPart.Position.Z)
27 local distance = ((char.HumanoidRootPart.CFrame.p - Target.p).magnitude)
28
29 local ray = Ray.new(char.HumanoidRootPart.CFrame.p, (Target.p - char.HumanoidRootPart.CFrame.p).unit * distance)
30 local Tile, position = workspace:FindPartOnRay(ray, char)
31
32 return Tile
33 end
34
35 TileOn = FindTileOn()
36
37 if IndexCounter < StepsAvailable and Tile then
38 -- Create an index for the end position
39 local Index = Instance.new('IntValue')
40 Index.Parent = Tile
41 Index.Name = "Index"
42 Index.Value = IndexCounter
43
44 local TilesAdded -- Will be a table of all the tiles already indexed
45
46 if IndexCounter == 0 then -- If this is the first run
47 TilesAdded = {Tile}
48 end
49
50 local function SearchThrough(tileToSearch) -- -- Find all parts next to the tile indicated
51 local tiles = {} -- All the tiles touching
52
53 for i,v in pairs(tileToSearch:GetChildren()) do
54 if v:IsA('ObjectValue') and v.Name ~= "DoorTile" then -- Make sure it's a tile indicator
55 if v.Value then -- v.Value is the tile touching, make sure it exists
56 table.insert(tiles, v.Value) -- Put it in so it can be returned as a table
57 end
58 end
59 end
60
61 return tiles -- All the tiles touching
62 end
63
64 -- Send the search for touching tiles and then index the ones that were found accordingly
65 local function SendSearch()
66 for i,v in pairs(TilesAdded) do
67 spawn(function()
68 local TilesFound = SearchThrough(v)
69
70 for a,b in pairs(TilesFound) do
71 if not b:FindFirstChild('Index') and b ~= Tile then
72 if (v.Position - TileOn.Position).magnitude < (Tile.Position - TileOn.Position).magnitude + 5 then -- Preventing unneccesary data by making sure it's not further away from the end goal
73 -- Index
74 local Index = Instance.new('IntValue')
75 Index.Parent = b
76 Index.Name = "Index"
77 Index.Value = IndexCounter
78
79 table.insert(TilesAdded, b) -- Make sure it's not searched again
80 end
81 end
82 end
83 end)
84 end
85 IndexCounter = IndexCounter + 1
86 wait()
87 end
88
89 repeat
90 SendSearch()
91 until
92 TileOn:FindFirstChild('Index') -- Keep indexing until reaches the end goal
93
94 -- Now calculate the shortest path
95 local TileFocusing = TileOn
96
97 local NumLookingFor
98
99 local Waypoints = {}
100
101 repeat
102 NumLookingFor = TileFocusing.Index.Value - 1
103
104 local TileToNode
105 for i,v in pairs(TileFocusing:GetChildren()) do
106 if v:IsA('ObjectValue') and v.Name ~= "DoorTile" then
107 if v.Value then
108 if v.Value:FindFirstChild('Index') then
109 if v.Value.Index.Value == NumLookingFor then
110 TileToNode = v.Value -- If it's the closest one, then node it
111 end
112 end
113 end
114 end
115 end
116
117 if TileToNode then
118 TileFocusing = TileToNode -- For the next tile
119
120 -- Visualization of the node for testing purposes
121 local Node = Instance.new("Part")
122 Node.Shape = "Ball"
123 Node.Material = "Neon"
124 Node.Size = Vector3.new(0.6, 0.6, 0.6)
125 Node.Position = Vector3.new(TileToNode.Position.X, char.HumanoidRootPart.Position.Y, TileToNode.Position.Z)
126 Node.Anchored = true
127 Node.CanCollide = false
128 Node.Parent = TileToNode
129 Node.Name = "Node"
130 Node.Color = Color3.fromRGB(0, 255, 0)
131
132 table.insert(Waypoints, Node)
133 end
134
135 until
136 NumLookingFor == 0 -- 0 means it's 0 steps away in the radius (meaning it's the goal)
137
138 -- Walk to the point
139 for i, waypoint in pairs(Waypoints) do
140 if StepsUsed < StepsAvailable then
141 char.Humanoid:MoveTo(waypoint.Position)
142
143 StepsUsed = StepsUsed + 1
144
145 char.Humanoid.MoveToFinished:Wait()
146 waypoint:Destroy()
147 else
148 break -- There are no more steps available (end of turn)
149 end
150 end
151 for i, waypoint in pairs(Waypoints) do
152 if waypoint:IsA('Instance') then -- If the nodes are visualized
153 waypoint:Destroy()
154 end
155 end
156 -- Clean up the indexes for the next use
157 Tile.Color = Color3.fromRGB(99, 95, 98)
158 for i,v in pairs(TilesAdded) do
159 if v:FindFirstChild('Index') then
160 v.Index:Destroy()
161 end
162 end
163 end
164end
165
166-- Run for testing (loop across random tiles on the map)
167wait(3)
168while true do
169 wait(1)
170 local RandomTile = Tiles[math.random(#Tiles)]
171 RandomTile.Color = Color3.fromRGB(255, 0, 0)
172 CalculatePath(RandomTile, 16) -- endPos is the end tile, integer is how many steps are available
173end