Hello, all.
Preface
I would first like to explain that this post arises from a culmination of research and previous posts regarding the same project. As such, I will make several references to other posts, and include sites that I have come across in my own research.
What I am trying to accomplish
I am trying to create a pathfinding system for units (such as infantry and tanks) to navigate a map.
The pathfinder must be able to calculate paths very quickly, even on large-scale maps. In addition, the pathfinder must be dynamic, meaning that it will take into account obstacles, such as buildings, when they are added / removed.
Map Conditions & Unit Behaviors
In this game, a map has three possible levels of elevation, as shown in this image:
Land units may only access platforms of higher / lower elevation if there is a ramp connected to them. Cliffs (the edges between platforms of differing elevations) are marked as “blocked,” while platforms and ramps are marked as “open.”
When a building is placed on the map, any space occupied by that building is also marked as “blocked.” Mobile units do not create “blocked” space in this way; instead they push each other out of the way, similar in nature to StarCraft 2’s unit collision.
I would like for there to be different types of terrain that are easier / harder to move through. For example, units would move faster over concrete / glass, but slower in snow / water. However, I am willing to sacrifice this mechanic for the sake of optimization, if necessary.
It should be mentioned that air units do not need to use any sort of pathfinding algorithm, since they are capable of flying over obstacles. However, I need a pathfinding system for units that are unable to do so.
Units in this game do not use Humanoids, and cannot jump.
The Issue
I have made several attempts to create this particular system, and most of the criteria I have set in the previous section have been satisfied (different types of terrain have not yet been included). However, there is one singular issue that has haunted (I daresay “taunted”) me from the very beginning: pathfinding is simply not fast / efficient enough when the map is scaled to a practical size. I will attempt to explain this further in the following section.
What I have tried so far
Roblox Pathfinding Service
In the early days of the game’s development, I used the Roblox Pathfinding Service; but over time it became clear that it simply had too many difficulties for my liking. It has been a long time since I’ve used it, so unfortunately I cannot remember the specifics of why it didn’t work. What I can say for certain is that it was unreliable in some instances
A* Pathfinding with a uniform-cost grid
In my research finding an alternate solution, I found threads where developers mentioned this thing called “A* Pathfinding”[1][2]. I wasn’t sure what it meant at the time, but based on everything I had read thus far, it seemed to be the most recommended solution. I had also read posts mentioning a more efficient “JPS Pathfinding” system, but I’ll get to that in the next section.
I should make it clear that node generation goes hand-in-hand with the pathfinding algorithm, since nodes are what the algorithm examines to create a path. In this, my first custom pathfinding system, I generated what is called a “uniform-cost grid,” meaning that nodes are evenly distributed across the map.
The issue I discovered with this system was that there were just too many nodes on the map for a path to be calculated quickly[3]. Units could take several seconds (at worst) before finally reacting to their command. I considered creating a system to reduce the number of nodes generated, but I first decided to “upgrade” the algorithm…
JPS Pathfinding with a uniform-cost grid
As I mentioned earlier, I had read comments made by other developers in other forum posts that the JPS pathfinding algorithm performed much better than A*[4]. The reason I chose to work on this method first is because, in my research, it was explicitly stated that JPS required a uniform-cost grid in order to work properly; and I already had a uniform-cost grid ready to go.
Now, I’m going to make a bold statement here: those developers didn’t know what they were talking about.
I am willing to make the assumption that none of the developers I found making this recommendation had ever actually applied the algorithm in their own work-- or at least, not at this scale. I spent a lot of time trying to decipher the impenetrable fog of mathematical jargon that is the JPS Algorithm Papers[5][6][7][8], and was lucky enough to find someone’s completed version of the JPS Algorithm on GitHub[9]. I was able to convert the code into Roblox lua, and in my small-scale tests, certainly worked. However, once I implemented it in my test map, it actually performed worse than A*[10]. Even with optimizations made based on other developers’ resources[11][12], it was impossible to create a path from one point to another without the game completely freezing for at least a 5th of a second. Not even A* did that!
The only conclusion I can come to, after all this, is that Roblox lua is simply too high-level of a language for the JPS algorithm1.
Needless to say, I was heavily discouraged by this, and had to take a long break from working on this game due to burnout. However, upon returning to this project, I attempted yet another strategy: optimizing node generation.
A* Pathfinding with a QuadTree grid (current)
I did some sleuthing in the Roblox Developer Forum, and came upon a thread that discussed using a “QuadTree” to generate nodes[13]. To describe it as simply as possible, a QuadTree organizes groups of empty nodes into a single, larger node. (This is not really how it works if you looked into the code, but the final product is the same.) I did some research, and was pleasantly surprised to find that this programming method was quick to learn and apply (at least, compared to JPS or A*).
(This is the same map as shown in the previous image.)
While the first iteration of this system worked quite well, I still had to make the QuadTree dynamic. In other words, I needed to update the layout of nodes whenever an obstacle, such as a building, was placed on the map. As programming usually goes, the more I tried to make a quick fix for it, the more frustrating the issue became, and eventually I realized that an overhaul of the system was required. Long story short, I spent some time figuring out how to do it, and then did it. The QuadTree system now updates, whenever a building is placed / removed, almost instantly.
However, despite the optimization of this system, to my dismay the one issue remains the same: upon scaling the map to a more reasonable size (a size that I would imagine even a simple 1v1 map to be), pathfinding is still slow and laggy.
Post Script
I’d like to thank those who set aside the time to read this. I may make edits to this post in the near future, to add more links / images for developers to gain a better understanding of what the pathfinding looks like. If possible, I will also respond to users’ questions by editing this main post; otherwise I will reply to them directly.
As always, any thoughts / advice / critiques / additional references are appreciated.
Sources
- Custom Pathfinding System - Help and Feedback / Scripting Support - DevForum | Roblox
- Terrain Path With A* Pathfinding - Help and Feedback / Scripting Support - DevForum | Roblox
- An Update On a Game You Didn’t Ask About - Help and Feedback / Game Design Support - DevForum | Roblox
- A* Pathfinding Algorithms - Development Discussion - DevForum | Roblox
- harabor-grastien-aaai11.pdf (anu.edu.au)
- harabor-grastien-socs12.pdf (anu.edu.au)
- main.dvi (anu.edu.au)
- harabor-grastien-icaps14.pdf (anu.edu.au)
- GitHub - qiao/PathFinding.js: A comprehensive path-finding library for grid based games
- Optimization for Jump Point Search Pathfinding code - Help and Feedback / Code Review - DevForum | Roblox
- The Basics Of Basic Optimization - Resources / Community Tutorials - DevForum | Roblox
- Algorithm efficiency and alternatives to table.find - Resources / Community Tutorials - DevForum | Roblox
- QuadTree A* Pathfinding? - Help and Feedback / Scripting Support - DevForum | Roblox
Additional Sources
These are sources that I did not mention / include in the previous sections, but that I have used in my programming “journey,” so to speak. I’ve decided to put them here to save other developers the hassle of finding these sources in the first place (as well as show developers what I have already looked at whilst working on this project).
Please note that I have used some sources more than others, so I cannot guarantee that you will find all of these sources helpful.
A* pathfinding / pathfinding in general
- A* search algorithm - Wikipedia
- PathFinding.js (qiao.github.io)
- Custom Pathfinding with A* | LuaLearning.com
- Starcraft 1 Pathfinding: A technical analysis | Strike Tactics
- Gamasutra: Dru Erridge’s Blog - Group Pathfinding & Movement in RTS Style Games
- Title (springer.com)
- A* Pathfinding… or is it? I can’t tell - Help and Feedback / Code Review - DevForum | Roblox
JPS pathfinding
Footnotes
- In fact, whilst working on this forum thread, I found this post by @IdiomicLanguage explaining that JPS performs slower in lua… if only I had found this earlier.