Dynamic Pathfinding

Published November 04, 2014 by James Wucher, posted by FuzzyBunnySlippers
Do you see issues with this article? Let us know.
Advertisement

The Goal

Given a dynamic moving field of objects, leverage the existing capabilities of the physics engine to allow an entity to navigate from any position to any other position while appearing to realistically avoid the objects that may collide with the entity. That is an awful lot to say. It is also a pretty tall order when you look at the video (below) and realize that there are dozens of objects all moving around all the time while the entity is plotting paths through them.

Why use the physics engine?

If you take apart the pieces that you need to put a game scene together where the AI has some chops, you are going to find you need:
  • A way to partition the space and make queries about what is where to reduce CPU cycles.
  • A way to make the bodies move about under your control and look reasonable while they do it.
  • A way to tell you when collisions occur and have the involved parties take action.
All of these features are present in the Box2D physics engine. The model for it is relatively easy to use and the execution speed is very good. Writing a cell space partition and basic physics system might be fun, but there are better things to do with the time (for now).

What is meant by "realistically avoid"?

When Han Solo flew the Millennium Falcon through the asteroid field in The Empire Strikes Back, he makes the famous quote "Never tell me the odds!". And as we all know, the odds are not good. So maybe "realistically avoid" is a bit of an overstatement, because if the ship can do that, it's a bit more than you should expect. The effect that we are looking for is to not overtly cheat in how the ship files around the moving obstacles and have it fly around them using the physics it is endowed with (mass, momentum, finite turn torque, etc.).
  • The collision should not be avoided by magic such as having the rocks pass through the ship or having the ship teleport around them.
  • The rocks should not have their velocity reduced to such a low speed that they are essentially fixed objects.
  • The rocks should not move in predefined paths (although this is an interesting option)...if they get bumped they need to behave with the real physics of the situation.
The entity should be able to plot a path through the rocks that it sees near it and also a "general path" towards its final goal, if it can get there. If the entity is sitting idly by and a rock comes hurtling at it, if it can move in time, it should get out of the way and escape to a safe location. Sometimes collisions should occur. The AI should not (and probably cannot) be perfect without forcing the rocks to avoid the ship or otherwise "cheating". NOTE: "Cheating" is not a bad thing, the end goal is to have the effect look good; if the player is entertained, they don't usually don't care how the effect was achieved.

The Big Picture

The diagram below shows the major components of the system. Major-Components-1024x653.png

Main Scene

The Cocos2d-x framework, along with the classes on the left side of the image, make up the "View" of the Model View Controller (MVC) model for the system. The Main Scene acts as the "Controller", taking the user input and sending it directly down to the model (the stuff on right side of the image). The Viewport approach has been discussed in other blog entries. In previous versions, the Viewport was manipulated (scrolled, pinched, zoomed) inside of the Main View. In this incarnation, a "camera" was created to hide all the guts of making this work, making it more portable to other applications. We're not going to talk about the View or Controller too much more in this article.

The General Approach

The Box2D engine allows for objects to be "solid" and have normal collision responses, but also allows for objects to pass through each other in various ways (see this link and this link for a good tutorial on this...yes, we reference other developers...they have good stuff). One special class of "intangibility" for Box2D are the "sensors". If you mark a fixture as a sensor, then everything will pass through it, but you will still get notified about each object that collides with it. Given the above, here is the general strategy:
  1. Lay out an arrangement of closed convex geometrical objects that are all marked as sensors. A "grid" of squares was chosen this time, but it could be circles, hexagons, rectangles etc., They don't have to be on a grid either, they can be in any arrangement that meets your needs. What they have to do is reasonably cover the area that you want to navigate through.
  2. Create a special contact handler that, for each sensor, counts up when a contact begins and counts down when a contact ends. If the numbers line up (and they appear to), then the space inside the shape is "empty" if the count is 0 and "not empty" otherwise.
If you watch the video, you can see that "under" the asteroids are squares that appear/disappear as the asteroids move. This is the GraphSensorContactLayer which shows the sensors that are "not empty" and hides them when they are "empty". There are also flags associated with the entities themselves that the contact listener uses to say "ignore me". This lets the ship fly around without tripping the sensor counts (this is important for the graph search). So, we have a grid of sensors and as the asteroids dance around, the spaces which are traversable are known and dynamically updated. When the sensors are created, they are placed into a graph, along with information about which sensors are adjacent to each other, and a distance between them (Euclidean). Together, this gives you a graph of the space you want to navigate across that updates using the physics engine automatically to tell you which parts of the graph can be used. The last part, is to develop a series of search algorithms that use the graph and also are aware that nodes can be "not empty" so that they are effectively skipped in the search. This is simply a "blocked" flag on the node (or the edge, though this may complicate the search when you consider which direction the edge is going).

What the Other Pieces Do

The Model part of MVC is filled in part by the Box2D engine. On top of that are these other major components, all of which play a different part. All of these are "singletons" with a well defined responsibility in the execution model. Some of them interact with only a few of the others, some are ubiquitous. Notifier The Notifier has been used in many designs (see other blog entries). This singleton is an efficient global one-to-many event communication mechanism for the system. Register for an event and your can receive them as long you derive from an abstract base class and implement the interface. EntityManager The Entity objects are the pieces of the game/system that get instantiated and derived from. This class is a combination container (with destruction responsibility) and phone directory for the entities. In a more complex (and realistic) system, pointers to entities are not held by other entities; an ID/reference is held and when you need to communicate with that entity, you send it a message by its ID. This avoids the challenge of dead/reused pointers. EntityScheduler When this code base was first being developed, the asteroids got an "update" call for their AI every frame. This lead to a lot of wasted CPU cycles since they only really need to be updated once a second or so. The EntityScheduler is a class that schedules the calling of the AI updates for the entities. Each frame, it executes the Update(...) method on all the entities scheduled for that frame. By spreading out the calls to update the asteroids across multiple frames, the overall load on the system was reduced. GraphSensorManager The GraphSensorManager loads the graph sensors into the a sensor graph and serves as the single point called by the Box2D framework (through the SystemContactListener) to update sensors about contacts. This singleton was originally created with a much larger use in mind, but has really be reduced in scope significantly. It's most important function now is to provide a mechanism to debug which sensors have been changed. It will probably be removed (or morph into something else) in future designs. The information about the GraphSensorManager has been discussed here to highlight a very important design/development consideration: Just because it is good in your head does not mean it will be good in the design. You should always look back on your design and ask yourself "Did this really give me what I wanted?" and "Can I do it another way (probably in the future) that will work out better?" SystemContactListener The last of the big components is the SystemContactListener. This is the callback used by Box2D when collisions occur to mark which items have been hit. Based on the size of the squares, there are many (or many more for smaller sensors) collisions every single update of the physics. This is where your CPU cycles go for the most part and the part of the design that really needs the most scrutiny in the future.

The Code

Get the Source Code for this article hosted on GitHub by clicking here.

Conclusion

This article presented a technique (and a video and a link where you can download the code) for using a physics engine integrated with the pathfinding system to dynamically avoid objects in a changing environment. The initial testing (and demo) is very promising for future use. One "fly in the ointment" is that the CPU load, while not intolerable, is a bit higher than I would like.

Article Update Log

30 Oct 2014: Initial release
Cancel Save
0 Likes 5 Comments

Comments

TheItalianJob71

For a contractor, i had developed a system similar to yours for robot navigation using dynamic voronoi cell generation, it worked pretty well, even if that was a rather performance costly solution i think that this will be implemented in a couple of years also in the gaming area.

November 04, 2014 08:54 PM
FuzzyBunnySlippers

For a contractor, i had developed a system similar to yours for robot navigation using dynamic voronoi cell generation, it worked pretty well, even if that was a rather performance costly solution i think that this will be implemented in a couple of years also in the gaming area.

Which solution will be implemented in a few years, the Voronoi Cell Generation or mine :)?

Thanks for the feedback. This approach was one of those "thinking in the shower" solutions. I wanted to build the spider's web out of lots of moving stuff but needed a way to make the pathfinding dynamic.

There are subtle issues to be overcome in the pathfinding, such as when the sensor size is much smaller than entity size and you want ships to not path through each other (so they have to trip the sensors). I figured out a short-term solution to that using an influence map approach coupled with the pathfinding to make the paths "stay away" from blockers. I have a picture of this in my gallery, if you are interested.

November 04, 2014 09:38 PM
TheItalianJob71

Well actually both of them seems viable, yours is already implemented in a game, diagram generators are widely used in areas not pertaining to games, but we are approaching a speed were such solutions can be afforded without compromising the gaming experience. About the diagram yes please, i'd love to have a peek at it

November 04, 2014 10:29 PM
FuzzyBunnySlippers

Well actually both of them seems viable, yours is already implemented in a game, diagram generators are widely used in areas not pertaining to games, but we are approaching a speed were such solutions can be afforded without compromising the gaming experience. About the diagram yes please, i'd love to have a peek at it

The picture is in the gallery under my profile...other users can see this (including you), right?

That picture is from a code base that is a bit further along than the code for this article. The big difference there is the influence map that is used to bias the A* search so that it doesn't get too close to objects while traveling. It keeps the paths a good distance from walls and other objects so that objects are much less likely to accidentally collide. I'll have to create an article to explain how all that works together...and an example as well...some day...

November 05, 2014 01:40 AM
Eck

Very nice sir and thank you for sharing the source code. :)

November 05, 2014 04:00 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!

Mash up your physics engine and your pathfinding engine and what do you get? Dynamic obstacle avoidance.

Advertisement

Other Tutorials by FuzzyBunnySlippers

28450 views
30565 views
Advertisement