Sign in to follow this  
Chris Burrows

A* jaggard lines?

Recommended Posts

Hello world,

Recently, I began working on an RTS. I'm using A* and it is working well, only I don't like how my paths contains long straight lines (both orthogonal and diagonal), shown in dark red below. I would prefer if the paths favoured more "jaggard" lines, as shown below in light brown.

[img]http://imageshack.us/a/img145/6460/astarblues.jpg[/img]

It seems every other implementation of A* which I have seen actually favours jaggard lines by default, but for some reason mine doens't work that way, and as far as I can tell I am not doing anything differently. I can't paste my source because I am writing the game in Multimedia Fusion which doesn't use traditional code. But, I did base my "code" on [url="http://www.policyalmanac.org/games/aStarTutorial.htm"]this article[/url], seemingly a pretty standard of version A*.

If anybody has any ideas why this is occuring, I'd love to hear about it!

Thanks in advance, Chris.

PS. [url="http://www.diybandits.com.au/Random/Update.zip"]Here[/url] is the executable if anybody is interested.

Share this post


Link to post
Share on other sites
The article you linked in turn links to this article at [url="http://www.gamasutra.com/view/feature/3096/toward_more_realistic_pathfinding.php"]Gamasutra[/url] that has possible answers to your problem, in the section on smoothing the path. Specifically, you can modify your heuristic to give a higher cost penalty to choices that require a change in direction. You can also implement the path smoothing trick discussed in that article of cutting out some of the turns along the way by trying to find the path step that is furthest along the path to which the unit can move in a straight line, thus eliminating the intervening zigs and zags.

Share this post


Link to post
Share on other sites
I think you're mistaken FLeBlanc, the zigs and zags is what I want! For the heuristic, I am using the manhatten distance. The formula itself is somewhat complicated because I am using a staggered iso grid and every other cell is offset..

(Max(Abs(2*TargetX+(TargetY mod 2)-2*CurentX-(CurrentY mod 2)), Abs(TargetY-CurrentY)))*10

The formula is spot on, plus, I was experiencing the same problem with a square grid, so it's not that. Is there a better choice than manhatten? Regarding the G cost, I have given orthogonal traversing a cost of 10, and diagonal a cost of 14. As described in the article I linked, yet his code favours the jaggard lines.

The Gamasutra article, Toward More Realistic Pathfinding, explains how to make zigs and zags into straight diagonal A to B distances, shown below...

[img]http://imageshack.us/a/img829/4708/90201812.jpg[/img]

.. but what I am experiencing is something different all together, see my original screenshot. Edited by Chris Burrows

Share this post


Link to post
Share on other sites
Ah, sorry, I misread your post and usually folks try to eliminate the zig-zags. Maybe you could try doing that article's suggestion in reverse? That is, give a cost penalty for steps that don't change direction. That might give you some zigs, and maybe a few zags as well.

The reason most people (myself included) try to smooth the zags is that when a unit follows the path, the rapid and frequent changes in direction makes the unit look epileptic. I'm not aware of any body of research into [i]increasing[/i] the jaggedness.

Share this post


Link to post
Share on other sites
Haha, I don't want excess zig zags, I'd just like the same treatment everybody else gets.

I'm a huge fan of Warcraft 2 and I'm aiming for a similiar standard. The zig zags don't bother me, but what I have currenlty is something much worse. Either lonng straight lines or lonng straight diagonals. I guess what I am asking is has anybody seen this before? and if so, do you fix it? and if not, any ideas what is causing it?

Share this post


Link to post
Share on other sites
I have seen behavior like that from my own pathfinders before, but since I always smooth the paths, it isn't really an issue.

When finding a path, if several possible node choices present the exact same heuristic cost, then the deciding factor in these cases tends to be the order in which the nodes are placed in the list for consideration. If you always visit the neighbor nodes in the same exact order, the result may be a preference given to one direction over another. So you could try randomizing the order in which you visit the neighbors at each step to see if it gives you better results.

Share this post


Link to post
Share on other sites
Maybe try using Euclidean distance as a heuristic? IIRC that created a better path on mine, but I was using non-diagonal movement. Also I removed the square-root from my heuristic.
h = cost * (abs(curNode.x - goal.x) + abs(curNode.y - goal.y));

Share this post


Link to post
Share on other sites
Seems like an issue with the heuristic to me. I would suggest trying diagonal distance. Here's some more detailed examinations of heuristics and their various properties: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html Edited by snowmanZOMG

Share this post


Link to post
Share on other sites
I'd suggest simple Euclidean distance. Think about how the cost estimate works. You take the distance traveled so far plus the heuristic of the rest of the distance. If you use Manhattan distance and a cost of 14 for diagonal, this is how it pans out:[list]
[*]Travelling 1 unit horizontally towards the target costs 10 and gets you 10 closer.
[*]Travelling 1 unit vertically towards the target costs 10 and gets you 10 closer.
[*]Travelling 1 unit diagonally towards the target costs 14 and gets you 20 closer.
[/list]
So of course it would pick diagonal paths first.

Edit: Also I believe that using the Manhattan distance if a unit can travel diagonally actually makes it an inadmissible heuristic. The heuristic should never overestimate the remaining cost. Not that I'm morally against inadmissible heuristics - they can cut down the search space nicely. But they aren't good if you're doing them by accident. Edited by jefferytitan

Share this post


Link to post
Share on other sites
Thanks everybody for your help, it seems it is the Manhattan heuristic after all. I found a program called [url="http://www.programmersheaven.com/download/1001/download.aspx"]PathDemo[/url] which allows you to test and modify a variety of path finding algorithms. The image below shows A* using both the Euclidien and Manhattan distances for a heuristic.

[img]http://imageshack.us/a/img826/7922/distanced.jpg[/img]

The path found using the Euclidiean distance is definitely more natural. So that's solves one problem, but presents another: How do I caculate the Euclidean distance? Traditionally it is simply sqr((ax-bx)^2+(ay-by)^2), but that won't work for me because I am using a staggered iso grid in which each X grid co-ordinate actually represents 2 different tiles depending on if the Y co-ordinate is odd of even.

[img]http://imageshack.us/a/img221/7870/mapiso.gif[/img]

I just can't seem to crack it. Any help would be greatly appreciated! Edited by Chris Burrows

Share this post


Link to post
Share on other sites
Oh dear, the way you're handling X values is... unusual. I would use co-ordinates as if the tiles were flat and axis aligned. You could convert it before getting distance, e.g:

yadjusted = y;
xadjusted = x - floor(y/2); // can go negative.

Hopefully my sleepy brain got that right. ;)

Share this post


Link to post
Share on other sites
Thanks Jefferytitan,

The formular for xadjusted is correct, but the yadjusted is a little off.

[img]http://imageshack.us/a/img69/1778/mapiso2.gif[/img]

Both red tiles are 2 below the yellow tile. But, according to your formular, the leftmost tile is 3 below (2 - 5 = -3). However, I noticed a correlation between your two formulars, and this works:

x adjusted = x - floor(y/2)

x distance = (A.x - floor(A.y/2)) - (B.x - floor(B.y/2))
y distance = (A.x - floor(A.y/2)) - (B.x - floor(B.y/2)) - (A.y - B.y)

So, using pythagoras, I calculated the correct Euclidiean distance, but the strange thing is, it hasn't changed my paths. They still favour the same long straight lines shown in my original screenshot (in dark red). I've decided the staggered iso grid only adds an extra layer of compexity to my problem, so I will re-write the code for a square grid and see if I can achieve my desired zig zags there.

On a side note, Jefferytitan, you mentioned my staggered grid is unusual. The alternative of a titled square grid means that if I want the entire screen to be filled with tiles, then some squares with have negative co-ordinates, shown below (the top left and bottom left white regions).

[img]http://imageshack.us/a/img27/7569/negativem.jpg[/img]

And seeing as there are no negative array cells, this was not an option.

Thanks again! I'm open to all suggestions.

Share this post


Link to post
Share on other sites
[size=8][b]VICTORY![/b][/size]

I re-implemented A* from scratch and found the problem!

I am using a 3d array to store the G, H, F, ParentX and ParentY values for each tile during the search. But the thing is, in Multimedia Fusion 2, arrays are unable to store floating point values. All non-whole numbers are rounded down to the closest integer. The Heuristic is rarely a whole number and that was the direct cause of my A* blues.

My solution was to use a cost of 1000 orthogonal and 1400 diagonal as opposed to 10 and 14 allowing for the equivalent of 2 decimal places of percision.

However, if you examine the G, H and F values for each square, you will notice they don't quite match 1000 and 1400. This is because I am multiplying every G value by 0.9925 and every H value by 0.0075 to break the tie between F values which would otherwise be the same for multiple tiles. This causes A* to favour tiles which are closer to the target.

[img]http://imageshack.us/a/img707/5151/victoryz.jpg[/img]

Thank you everybody for your help.

[img]http://www.sega-16.com/forum/images/icons/Pirate%20Smiley.gif[/img] Yarrr! Until next tyme me hearty!

Share this post


Link to post
Share on other sites
Not in Multimedia Fusion 2. The closest would be a string array, in which you would convert the float to a string before you store it, and then back to a float when you retrieve it. Obviously, this is not very efficient.

But, now I've solved the problem, I will re-write the code in c++, although that would have worked from the beginning. Silly integer arrays!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this