Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » A* without tiles

Credits go to Arthur Kalliokoski, gnolam, james_lohr, Jonatan Hedborg, OICW, and SiegeLord for helping out!
This thread is locked; no one can reply to it. rss feed Print
 1   2 
A* without tiles
Trent Gamblin
Member #261
April 2000
avatar

So, my game uses connected line segments for collision detection with the map. Maps are hand drawn and using bitmasks for collision makes it too difficult to get a nice "slide" effect (sliding along lines as you bump against them).. Now I need to implement some pathfinding given these constraints, and I'm looking for ideas. My first idea was to divide into tile-sized blocks and "draw" lines into them to find if they're passible. The problem with that is, there could be any number (well, maybe 3-4 max) lines crossing any given tile.

So I'm kind of stuck here. And just to make sure I don't get a bunch of answers on how to implement A*, I know how, it's just finding if a tile is passible and WHERE on that tile is passible that I'm having problems with. I could have a tile like this:

+--------+
|     |  |
|     |  |
|\    |  |
| \   |  |
+----|--+

It might be passible in between those lines if the characters collision box is small enough.

And maybe tiles is the completely wrong way to go, maybe even A* is the wrong way to go. Help would be appreciated...

[edit]
Sorry, the diagram somehow messes up. Imagine a tile with a line on either side with an open path up the middle.

Arthur Kalliokoski
Second in Command
February 2005
avatar

Can't you just have a memory bitmap with pixels of a certain color where the object is allowed to go? If the object has an extended shape such as a circle, simply trim off the edges of the "allowed" pixels so the center of the circle can be used instead of comparing the entire circumference. You might need some sort of "collision compiler" program to do this.

They all watch too much MSNBC... they get ideas.

Trent Gamblin
Member #261
April 2000
avatar

Ya, but I don't think it's that simple. Imagine a tile with a L joint shape in it... I guess it's going to take a little more modification of the A* algorithm. I'll have to add X number of intermediate points per tile.

Jonatan Hedborg
Member #4,886
July 2004
avatar

A* has nothing to do with tiles. It's just a state search algorithm with a heuristic. What I would do it probably place manual nodes at all "corners" (so that all nodes that should be connected "see" each other), and then connect them into a node graph at load (maybe with some further information, like "you have to be this small to use this node).

Trent Gamblin
Member #261
April 2000
avatar

Thanks but I've avoiding a manual approach. Presently we have dozens of large maps, and there will be hundreds more. There's already a lot of processing that goes into each map. I need some way to generate nodes automatically.

SiegeLord
Member #7,827
October 2006
avatar

I'd use a navigation mesh (look it up, there is a nice discussion and some links towards the end of this). You can generate that mesh from your level via triangularization, perhaps.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

OICW
Member #4,069
November 2003
avatar

The real pitfall is the fact that your map is hand-drawn as a whole. There are two possible solutions I can think of at the moment:

1) You manually create a pathnode graph for each map (kinda same way you have to do it for maps in UT for example). This will give you large control over the pathfinding - say you can have edges weighed by distance and also have additional information, e.g. how big objects can pass through.

There are several drawbacks: for large map with large open space areas you'll need to do lots of nodes in order to prevent bad looking movement. Also for nicer results more nodes should be created and thus more time will be spent.

2) As you mentioned above, you could overlay your map with square grid. The smoother the better, but not insanely smooth. Then put a path node into the middle of each square. During preprocessing of the map you'll build the graph via a simple algorithm: for each node you'll cast ray into all directions (4 or 8[1]) and check whether the ray collides with the environment or not. If not then you can add the edge into the graph.

In both cases you can then use any graph algorithm for pathfinding. If you expect dynamic changes in the graph (like some paths being blocked by objects etc.) I'd advice to use the A*, if the graph will be static, you could use Floyd-Warshall to precompute paths and then poll them in constant time.

References

  1. Actually only 2 or 4 will be sufficient to get rid of redundancy

[My website][CppReference][Pixelate][Allegators worldwide][Who's online]
"Final Fantasy XIV, I feel that anything I could say will be repeating myself, so I'm just gonna express my feelings with a strangled noise from the back of my throat. Graaarghhhh..." - Yahtzee
"Uhm... this is a.cc. Did you honestly think this thread WOULDN'T be derailed and ruined?" - BAF
"You can discuss it, you can dislike it, you can disagree with it, but that's all what you can do with it"

james_lohr
Member #1,947
February 2002

Tiles are the wrong way to go, and I'm not so sure (judging from what you are askig) that you fully understand A*.

A* operates on an arbitrary placed set of nodes, each with an arbitrary number of neighbors. People seem to think that, just because the usual (and most simplistic) use of A* involves a grid, A* is some how bound to a grid. This is simply not true, and your problem is precisely where you need to use A* in its general form.

Covering your map with regularly distributed nodes (what you are calling tiles) won't do. You need to cleverly distribute nodes so as to accurately reflect your environment in few enough nodes to allow A* to run reasonably quickly.

[edit]

SiegeLord said:

navigation mesh

This.
[/edit]

To do this there are probably all sorts of tricks you could employ to generate your node set from your vector environement.

Just to reiterate this point: when using a grid, each node's position and neighbors are implicity defined; However, in the general case (which you need to be using) you will need to define the position of each node and each node's neighbors and respective edge weights.

How you actually go about building this is the interesting bit. Off the top of my head, I would start with something like this:

(I will refer to your environemnt/obstacles as simply "polygons")

1) Begin with a regularly distributed set of nodes  (i.e. a grid)
2) For each node which intercepts a polygon:
    a) If possible, move node a short distance to avoid collision
    b) If a) is not possible, delete the node
4) For any two nodes which are near each other and which are in line of sight, add an edge
5) If nodes are near each other but no line of sight exists, attempt to add a new node somewhere between the two nodes without colliding with a polygon.

Repeat 4,5 until some arbitrary level of detail is reached.

Trent Gamblin
Member #261
April 2000
avatar

Obviously, if you look at the title of this thread, you should be able to tell that I know A* doesn't require tiles :P... Anyhow, thanks for all of the info. I was googling around for navigation meshes, and it turns out there's a section in Game Programming Gems 1 about it, and I own that book, but haven't read most of it. So I'll give that a read. Thanks.

EDIT:

I tried 2 different algorithms (that I came up with myself...) to try and triangulate an area. Both of them had problems and I was growing tired of trying to fix them after 4 straight days of working on them. So I switched to a brute force method. The problem is, the results aren't pretty... My impression after reading about navigation meshes was that it doesn't matter what the triangles look like. But I wanted to ask to be sure... this is a screenshot of what a small area looks like after triangulating it with the brute force algorithm:

{"name":"ss.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/7\/37e33d62c8c2d88454dcd3a1b5933b6d.png","w":600,"h":622,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/7\/37e33d62c8c2d88454dcd3a1b5933b6d"}ss.png

Will that be ok for a navigation mesh?

Arthur Kalliokoski
Second in Command
February 2005
avatar

Those narrow triangles look bad somehow. Can you show the original border, or the outer lines are the border?

They all watch too much MSNBC... they get ideas.

Trent Gamblin
Member #261
April 2000
avatar

The outer lines are the border.

gnolam
Member #2,030
March 2002
avatar

I tried 2 different algorithms (that I came up with myself...) to try and triangulate an area. Both of them had problems and I was growing tired of trying to fix them after 4 straight days of working on them. So I switched to a brute force method.

Triangulation By Ear Clipping. It's the easiest method I know of.

(The trapezoidization method described by Fournier et al is also reasonably easy to understand, but harder to implement. And paywalled.)

--
Move to the Democratic People's Republic of Vivendi Universal (formerly known as Sweden) - officially democracy- and privacy-free since 2008-06-18!

Arthur Kalliokoski
Second in Command
February 2005
avatar

I was thinking of connecting vertices that had an interior angle of 270 degrees or so, unless the line segments connecting them were shorter than some small value. The rest could be triangulated by adding more interior vertices (the rectangular areas on the left already have these) when a triangle has too small of an angle somewhere. Kind of thinking out loud here.

{"name":"602141","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/b\/e\/beb3238732b5f2a8e4972226ce42536a.png","w":600,"h":622,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/b\/e\/beb3238732b5f2a8e4972226ce42536a"}602141

They all watch too much MSNBC... they get ideas.

Trent Gamblin
Member #261
April 2000
avatar

Thanks guys. After reading the first few pages on ear clipping, it sounds very similar to what I was trying to do with my second attempt. I'm going to finish reading and hopefully that will make it click.

james_lohr
Member #1,947
February 2002

Trent you may be slightly missing the point. You are not trying to solve the triangulation problem which is:

Quote:

Decompose a simple polygon into a collection of triangles whose vertices are only those of the simple polygon.

This is not what you are trying to do because this will only allow for navigation between vertices of your environment. As I've already mentioned, you should be adding vertices to navigate between. This is, in many ways, easier than the strict triangulation/tesselation problem.

SiegeLord
Member #7,827
October 2006
avatar

Navigation meshes do not need any additional vertices added.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Trent Gamblin
Member #261
April 2000
avatar

Here's what I've got now:

{"name":"ss2.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/d\/5d527061ac4e8635127d0cff7b6cabbe.png","w":600,"h":622,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/d\/5d527061ac4e8635127d0cff7b6cabbe"}ss2.png

The only problem seems to be some overlap in the bottom left corner. Not sure how I'm going to fix it, but it's late. I might come up with something tomorrow. This is the basic ear cutting method described in the document Gnolam linked (I had to improvise some because it seems to leave out important details).

EDIT:

I took the holes out of the left side until I get it right without holes.

EDIT:

I think I made some improvement... but now there are errors in the top right O_O... here's what it looks like:

{"name":"602144","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/7\/9\/79c907afd4da0d60bda6b75becd3792a.png","w":600,"h":622,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/7\/9\/79c907afd4da0d60bda6b75becd3792a"}602144

EDIT:

It's working, at least for this one set of test data:

{"name":"602145","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/7\/37fc455b1414967e48ca210a4bd9f2e0.png","w":600,"h":622,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/7\/37fc455b1414967e48ca210a4bd9f2e0"}602145

Some areas look suspect but I scaled the points 3x and the thin triangles are distinct and correct. Thanks everyone.

SiegeLord
Member #7,827
October 2006
avatar

How rubust is your triangulizer? Does it handle holes? I always wanted to add a triangulizer function to the primitives addon.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Trent Gamblin
Member #261
April 2000
avatar

EDITED: updated link

It doesn't, but I plan on adding support for holes in the next day or two (or however long it takes). I don't know how good it is. I'll find out once I try it on some more levels. The source code for what I've got now is here, anyway: http://trent.gamblin.ca/triangulate. There is room for improvement, notably the part where it checks every pixel on a line to see if it's outside the polygon, as I didn't know how to do that any better way.

Arthur Kalliokoski
Second in Command
February 2005
avatar

nvm

They all watch too much MSNBC... they get ideas.

Trent Gamblin
Member #261
April 2000
avatar

Updated the post right after first adding it. :P

EDIT:

I'm not sure about this, but is it possible to pick separate polygons out of a stream with no markers? I'm guessing it's not. If that's the case then I'll either put markers in or have the user specify the start indices of each polygon (I'm talking about the outer polygon and the holes inside it). Which of those is preferable?

EDIT:

Back to this thread again, with some problems adding holes. I tried a bunch of different things, but mainly trying to stick with the ear clipping document. I create a 0 size "gap" leading to the nearest edge corner to the right. I inline the hole where it should appear in the main polygon, adding two extra vertices leading out of the hole, where it continues on as if the hole wasn't there. This is supposed to "just work" I guess, because it keeps the polygon simple. Well I'm just not getting anything to fill in triangles around the holes (or just a few). At best I had all of the triangles filled except 11, but I think this screenshot I'm going to post is my best effort so far, even though it has less fill ratio:

{"name":"602165","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/d\/5d0d31325ee9c25288b409ab557a855d.png","w":600,"h":622,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/d\/5d0d31325ee9c25288b409ab557a855d"}602165

The green lines are the outline of the polygon (you can see the "gaps" I put in, the first one is obvious and the other two are going to the same corner of the rectangular area. I don't know what I'm doing wrong, so I'm going to mull it over a bit without looking at the code... if anyone has experience with this and has some ideas on what might be wrong that would be great. I'll attach the source code. The "DL_List" stuff is just a simple doubly linked list I made up for this, if you want the code for that to test this I will upload it.

On a positive note, what you see is generated in 1 ms or a little above as opposed to 10 ms of my other attempt. I learned a few math tricks :P.

P.S.: I used a wide terminal to edit it, as it would have gotten down right ugly trying to fit it into 80 characters.

You'll see some markings "method 1" and "method 2" which are two slightly different approaches I'm playing with right now.

EDIT:

I think I finally got it. No telling until I start trying some pathfinding later on today. But it's all triangulated:

{"name":"triangulation-working.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/4\/2\/423684268376000fe93bc2e6d1580325.png","w":600,"h":622,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/4\/2\/423684268376000fe93bc2e6d1580325"}triangulation-working.png

If you're interested, the problem was twofold but both related. One of the "splits" I put it between a hole and the outside edge was going in where another split already went, so wires were crossed. Simple, just find the next exact same point further down (after the hole that was inserted.) The other one was related, it was also two lines crossing over each other but in the image there was no telling since it was a straight line for a triangle. A little investigation found these two errors out.

The final image took about 2.5 ms to generate triangles for. It can be optimized a lot, you could probably get near or under 1 ms without too much work. The source code is available here: http://trent.gamblin.ca/triangulate/

james_lohr
Member #1,947
February 2002

What's the plan for optimising the path - will you simply cut corners using a LoS check? For example:
{"name":"602191","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/b\/eb9e02afef131ef16e3eba9fc71c7883.jpg","w":588,"h":577,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/b\/eb9e02afef131ef16e3eba9fc71c7883"}602191

Trent Gamblin
Member #261
April 2000
avatar

Yes, LoS check is the plan.

EDIT:

I found a bug after trying some new data. Also, new timing info: Was compiling with -g, no optimization. With optimization the same point set takes about 1.5 ms to triangulate.

I'll be updating the software as I find bugs or to optimize things. Probably not posting much in this thread unless someone responds. The web page will be kept up to date with bug fixes etc: http://trent.gamblin.ca/triangulate.

Funnily, I emailed the author of a triangulator about a commercial license around the time I started mine. I got an email response from him now that it costs $2500 for a license of the software :O. I guess I saved myself a bit of money and had fun/learned some things in the process. God, thank you for giving me a lot of free time so I can do these things on my own schedule 8-)

GullRaDriel
Member #3,861
September 2003
avatar

Trent said:

Probably not posting much in this thread unless someone responds.

There is a glitch in both dllist.h && cpp: // end namepsace should be // end namespace

;-)

"Code is like shit - it only smells if it is not yours"
Allegro Wiki, full of examples and articles !!

Trent Gamblin
Member #261
April 2000
avatar

Haha. I'm not sure how to fix this one. It's beyond my capabilities.

 1   2 


Go to: