{"id":3302,"date":"2014-08-10T20:21:30","date_gmt":"2014-08-10T19:21:30","guid":{"rendered":"http:\/\/t-machine.org\/?p=3302"},"modified":"2014-08-11T00:04:00","modified_gmt":"2014-08-10T23:04:00","slug":"debugging-a-pathfinding-in-unity","status":"publish","type":"post","link":"http:\/\/new.t-machine.org\/index.php\/2014\/08\/10\/debugging-a-pathfinding-in-unity\/","title":{"rendered":"Debugging A* Pathfinding in Unity"},"content":{"rendered":"<p>I needed fast, efficient, &#8230; but powerful, adaptable &#8230; pathfinding for my hobby project. My minions have to fly, walk, crawl, teleport, tunnel their way across a procedural landscape. Getting to that point has been tricky, but the journey&#8217;s been interesting.<\/p>\n<p>We want something that intelligently routes creatures around obstacles, and picks &#8220;shallow&#8221; routes instead of scaling cliffs (or vice versa for Giant Spiders etc). Something like this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-20.09.36.png\" alt=\"Screen Shot 2014-08-10 at 20.09.36\" width=\"849\" height=\"525\" class=\"aligncenter size-full wp-image-3306\" srcset=\"https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-20.09.36.png 849w, https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-20.09.36-150x92.png 150w, https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-20.09.36-300x185.png 300w, https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-20.09.36-624x385.png 624w\" sizes=\"(max-width: 849px) 100vw, 849px\" \/><\/p>\n<p><!--more--><\/p>\n<h2>Step 1: Unity Asset Store<\/h2>\n<p>With anything in Unity, this should always be your first stop.<\/p>\n<p>I researched a handful of the top Pathfinding assets, and directly tested those that had WebPlayer demos and\/or free &#8220;demo&#8221; versions for you to test.<\/p>\n<p>There was some nice stuff there, but overall I was disappointed. Performance was fine, and most of them were using Co-routines or background threads to control the CPU usage. This is a great thing! (relatively easy to do yourself, to be honest &#8211; it&#8217;s one of A*&#8217;s top 2 features).<\/p>\n<p>&#8230; but the user-interfaces were crummy (I couldn&#8217;t understand them), or &#8230; features had hardcoded limits that blocked the algorithm, or &#8230; the feature-set was missing core elements of A-Star (e.g. dynamic edge-costs: the other <strong>main reason<\/strong> for using it in games) &#8230; or &#8230; etc.<\/p>\n<p>For ultra-simple uses, most of them are fine. Or for derivative games. And there was one in a 24 hour Asset sale that looked promising, with a big team behind it &#8211; but they were cagey on too many details (documentation was only available AFTER you purchase!), and it appeared relatively hardcoded \/ hard to customize.<\/p>\n<p>In the end, I gave up. And it occurred to me:<\/p>\n<blockquote><p>\nMaybe &#8230; every innovative game requires you to re-implement A* to support\/enable your game&#8217;s specific novel features?\n<\/p><\/blockquote>\n<p>(A* is not hard to implement. And there are so many performance choices to make that this might be a case where reinventing the wheel is actually a good thing. Maybe)<\/p>\n<h2>What is A*, how does it work, how to implement it?<\/h2>\n<p>Here I hand you over to the esteemed Amit Patel &#8230;<\/p>\n<ul>\n<li><a href=\"http:\/\/www.redblobgames.com\/pathfinding\/a-star\/introduction.html\">2014: AmitP&#8217;s interactive A-star explanation<\/a> &#8212; easier to understand, only covers page 1 of the next item\n<li><a href=\"http:\/\/theory.stanford.edu\/~amitp\/GameProgramming\/AStarComparison.html\">2009: AmitP&#8217;s original A-star explanation (non-interactive)<\/a> &#8212; used as reference by a lot of people for implementing themselves, has many pages of info\n<\/ul>\n<p>This leaves us with <a href=\"http:\/\/theory.stanford.edu\/~amitp\/GameProgramming\/ImplementationNotes.html\">a base algorithm<\/a>:<\/p>\n<pre>\r\nOPEN = priority queue containing START\r\nCLOSED = empty set\r\nwhile lowest rank in OPEN is not the GOAL:\r\n  current = remove lowest rank item from OPEN\r\n  add current to CLOSED\r\n  for neighbors of current:\r\n    cost = g(current) + movementcost(current, neighbor)\r\n    if neighbor in OPEN and cost less than g(neighbor):\r\n      remove neighbor from OPEN, because new path is better\r\n    if neighbor in CLOSED and cost less than g(neighbor): **\r\n      remove neighbor from CLOSED\r\n    if neighbor not in OPEN and neighbor not in CLOSED:\r\n      set g(neighbor) to cost\r\n      add neighbor to OPEN\r\n      set priority queue rank to g(neighbor) + h(neighbor)\r\n      set neighbor's parent to current\r\n\r\nreconstruct reverse path from goal to start\r\nby following parent pointers\r\n<\/pre>\n<h2>Implementing this in C# &#8230; in Unity<\/h2>\n<p>First of all: <strong>Unity is broken in how it uses some of the C# syntax<\/strong>. Without going into detail, some valid C# will corrupt when used in Unity &#8211; most obviously here: multi-dimensional arrays. You can patch Unity to fix this yourself, but it&#8217;s non-trivial. Instead, simply re-implement with crappier, lower-level structures (sad, but practical).<\/p>\n<p>Secondly: Microsoft&#8217;s C#\/.NET standard library has a &#8220;SortedList&#8221; built-in, but IMHO it&#8217;s misnamed: you can only have one element for each position (that&#8217;s a sorted Map, or a Set-sorted List, not a sorted List). It&#8217;s the wrong structure for our case here, although you CAN customize it to work, by making each &#8220;item&#8221; a List, and moving nodes from list to list. Ugh. Complicated. Again: sad, but practical. Personally I made my own SortedList that does what it pretends to do ;).<\/p>\n<h3>Make a Node class<\/h3>\n<p>Probably something you already have in your game, e.g.:<\/p>\n<p>[csharp]<br \/>\npublic class Node<br \/>\n{<br \/>\npublic int x, int y;<br \/>\n}<br \/>\n[\/csharp]<\/p>\n<h3>And a graph \/ grid class<\/h3>\n<p>e.g.<\/p>\n<p>[csharp]<br \/>\npublic class Grid<br \/>\n{<br \/>\npublic Node[] allNodes;<br \/>\npublic int nodesAcross, nodesDown; \/\/ needed to workaround Unity bugs<br \/>\npublic Node NodeAt( int x, int y ) \/\/ needed to workaround Unity bugs<br \/>\n{<br \/>\nreturn allNodes[ x + y*nodesAcross];<br \/>\n}<br \/>\npublic void SetNodeAt( int x, int y, Node n ) \/\/ needed to workaround Unity bugs<br \/>\n{<br \/>\nallNodes[ x + y*nodesAcross] = n;<br \/>\n}<br \/>\n[\/csharp]<\/p>\n<h3>Make your own SortedList<\/h3>\n<p>Fairly easy, but I screwed this up first time around, because I was unfamiliar with some neat tricks C# does internally with foreach loops. I doubt you&#8217;ll have problems. For A*, you only need to make a class with these methods:<\/p>\n<p>[csharp]<br \/>\npublic class MySortedList&lt;T&gt; where T: class<br \/>\n{<br \/>\n\tpublic MySortedList();<br \/>\n\tpublic int Count();<br \/>\n\tpublic T GetFirst();<br \/>\n\tpublic T RemoveFirst();<br \/>\n\tpublic void RemoveItem( T victimItem );<br \/>\n\tpublic bool Contains( T searchItem );<br \/>\n\tpublic int AddWithValue( T newItem, float newValue );<br \/>\n}<br \/>\n[\/csharp]<\/p>\n<h3>Sanity-checking your algorithm<\/h3>\n<p>This is a data-sensitive algorithm, and we&#8217;ve created some custom data-structures (OK, not dangerous), and &#8230; a custom container (hmm: dangerous! Easy to get wrong in subtle ways).<\/p>\n<p>I strongly recommend you write unit tests for MySortedList. I didn&#8217;t (fool! I haven&#8217;t adopted a good UnitTesting framework in Unity yet), and regretted it. In particular, focus on:<\/p>\n<ol>\n<li>Testing the &#8220;Contains&#8221; check is accurate\n<li>Testing the &#8220;Remove&#8221; methods actually remove items\n<\/ol>\n<p>&#8230;miss either of those and A-star will go into an infinite loop, because it keeps adding more nodes to the list. Especially subtle (and an obvious gotcha): the Contains check, and how it checks for equality &#8212; make sure you implemented Equals and GetHashcode on your custom classes!<\/p>\n<h2>Debugging your algorithm<\/h2>\n<p>Now to the fun stuff. This is a DATA HEAVY algorithm: in real-game use, it will be processing many thousands of nodes on each run, and up to tens of thousands of edges. Debugging that is hell. Don&#8217;t do it.<\/p>\n<p>Instead, your first thought should be:<\/p>\n<blockquote><p>\nHow can I make a visualization for this, so that debugging vast datasets becomes quick and easy?\n<\/p><\/blockquote>\n<p>My route:<\/p>\n<ol>\n<li>Make a Component for &#8220;Calculate A Path&#8221;\n<ol>\n<li>Put start node in there, end node\n<li>Write an Editor \/ CustomEditor class for this that adds a button &#8220;Calculate Path&#8221;\n<\/ol>\n<li>A* outputs a &#8220;Path&#8221; object\n<li>My &#8220;Calculate Path&#8221; takes the output\n<ol>\n<li>creates a new GameObject\n<li>Adds that as a child\n<li>Attaches a &#8220;PathVisualizer&#8221; script\/component\n<\/ol>\n<li>PathVisualizer has an OnGizmos() implementation that:\n<ol>\n<li>Draws a cube at every node in the path\n<li>Draws a different coloured cube at start and end nodes\n<li>Draws lines between each node showing the order \/ direction of travel along the path\n<\/ol>\n<\/ol>\n<p>Things to note here:<\/p>\n<ul>\n<li>Because each path is added to a GameObject, you can tweak your A* implementation, re-run it in Editor, and compare the two output paths, see how well it&#8217;s changed from your previous attempt. Awesome!\n<li>I didn&#8217;t want the overhead of making my Nodes or Paths be Unity MonoBehaviours; no need: I made a visualization-behaviour instead that holds a reference to &#8220;Path that you want me to visualize&#8221;\n<\/ul>\n<p>See image at top of post for my first-draft simple visualization.<\/p>\n<h3>Visualize the Graph, and the Costs<\/h3>\n<p>I also did some work on visualizing the graph itself:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-20.00.49.png\" alt=\"Screen Shot 2014-08-10 at 20.00.49\" width=\"850\" height=\"631\" class=\"aligncenter size-full wp-image-3303\" srcset=\"https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-20.00.49.png 850w, https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-20.00.49-150x111.png 150w, https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-20.00.49-300x222.png 300w, https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-20.00.49-624x463.png 624w\" sizes=\"(max-width: 850px) 100vw, 850px\" \/><\/p>\n<p>And the Costs (note: I have implemented my Costs to be bidirectional: it is &#8220;cheap&#8221; to move downhill, and &#8220;expensive&#8221; to move uphill. Not by same amount! So each edge has two colours, one for uphill cost, one for downhill):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-19.59.53.png\" alt=\"Screen Shot 2014-08-10 at 19.59.53\" width=\"831\" height=\"636\" class=\"aligncenter size-full wp-image-3304\" srcset=\"https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-19.59.53.png 831w, https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-19.59.53-150x114.png 150w, https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-19.59.53-300x229.png 300w, https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-19.59.53-624x477.png 624w\" sizes=\"(max-width: 831px) 100vw, 831px\" \/><\/p>\n<h3>Making it less painful to run<\/h3>\n<p>Unoptimized, your A* algorithm should run in a small fraction of a second, for any grid up to a few thousand nodes. It&#8217;s practically instant.<\/p>\n<p>HOWEVER &#8230; when you&#8217;ve got bugs, A* tends to go into infinite loops and hang Unity (Unity sadly has ZERO PROTECTION against scripts doing this).<\/p>\n<p>So, for practical purposes, you want to turn your calculation into a Co-Routine, and put some in-editor info that lets you see if its gone wrong. I decided to add progress bars for how large the Open list and Closed list were. I knew the total number of nodes, and that the two lists couldn&#8217;t get larger than that, so I could fill these up:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-20.05.21.png\" alt=\"Screen Shot 2014-08-10 at 20.05.21\" width=\"284\" height=\"110\" class=\"aligncenter size-full wp-image-3305\" srcset=\"https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-20.05.21.png 284w, https:\/\/t-machine.org\/wp-content\/uploads\/Screen-Shot-2014-08-10-at-20.05.21-150x58.png 150w\" sizes=\"(max-width: 284px) 100vw, 284px\" \/><\/p>\n<h4>Co-routines in Editor<\/h4>\n<p>Sadly, although Unity allows co-routines in the Editor (Unity&#8217;s staff have used them for the recent Editor features in 4.5), you&#8217;re not allowed to use them yourself.<\/p>\n<p>&#8230;but the coroutine implementation in Unity is super-simple, and quite easy to write yourself. So, using something like <a href=\"https:\/\/gist.github.com\/benblo\/10732554\">this short class that is a full implementation<\/a> you add that feature for yourself.<\/p>\n<p>I used the same idea (add your own callback to the &#8220;update&#8221; list, and run the IEnumerator manually), but added some bits about repainting the GUI frequently (not too frequently) and finessed some other minor features.<\/p>\n<h2>Assertions and Exceptions<\/h2>\n<p>Let me revisit an earlier point:<\/p>\n<blockquote><p>\nThis is a DATA HEAVY algorithm: in real-game use, it will be processing many thousands of nodes on each run, and up to tens of thousands of edges. Debugging that is hell. Don&#8217;t do it.\n<\/p><\/blockquote>\n<p>Assertions are your friend. Most AAA pro game developers love them, especially on Console (they&#8217;re great for saving you from writing Unit Tests for a machine that doesn&#8217;t have spare memory\/performance to run Unit Tests).<\/p>\n<p>I screwed up my implementation. With a little help from Amit, I added the following assertions to my code:<\/p>\n<ol>\n<li>Never accept a negative Cost to move from one node to another\n<ol>\n<li>This should be impossible. Even if you are doing &#8220;weird and cool things&#8221; with your Costs in your game, you do NOT want this.\n<li>&#8230;because A* can <strong>and will<\/strong> abuse this, running back and forth over your &#8220;negative&#8221; edge, getting &#8220;free energy!&#8221; to reduce its total path cost\n<li>You MIGHT want to allow zero-cost edges, sometimes &#8211; e.g. a teleporter &#8211; but be very careful: they can allow the same ping-ponging, infinite-loop problem\n<li>In general: Assert whenever you read an edge-cost and it&#8217;s &#8220;<= 0f\"\n<\/ol>\n<li>Last line of Amit&#8217;s core loop marks the &#8220;current node&#8221; as the neighbour&#8217;s predecessor. DO NOT ALLOW THIS IF that node is already pointing to the neighbour!\n<ol>\n<li>Again: this should never happen, if the algorithm is running correctly. It&#8217;s absolutely wrong!\n<li>(zero or negative costs would allow it to happen)\n<li>But worse: don&#8217;t allow a chain of links, where one item in the chain is repeated: this becomes an infinite loop later on when you try to process the path\n<li>I added a brutal &#8220;when adding a node to end of chain, check it&#8217;s not anywhere in preceding links in chain&#8221; assertion\n<\/ol>\n<li>Never allow the open list to have more than the total number of nodes\n<ol>\n<li>Shouldn&#8217;t be possible! Can&#8217;t have duplicate nodes!\n<\/ol>\n<li>Ditto with the closed list (I actually implemented closed list as a Set. But if you have a bug in your .Equals method, you could still break the standard-library&#8217;s Set implementation!)\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>I needed fast, efficient, &#8230; but powerful, adaptable &#8230; pathfinding for my hobby project. My minions have to fly, walk, crawl, teleport, tunnel their way across a procedural landscape. Getting to that point has been tricky, but the journey&#8217;s been interesting. We want something that intelligently routes creatures around obstacles, and picks &#8220;shallow&#8221; routes instead [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,20,67,69],"tags":[],"_links":{"self":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts\/3302"}],"collection":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/comments?post=3302"}],"version-history":[{"count":7,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts\/3302\/revisions"}],"predecessor-version":[{"id":3313,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts\/3302\/revisions\/3313"}],"wp:attachment":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/media?parent=3302"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/categories?post=3302"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/tags?post=3302"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}