Game Development Community

dev|Pro Game Development Curriculum

Flexible A* Pathfinding System

by Dan Keller · 04/08/2008 (2:15 pm) · 203 comments

1.8.1 version here!

Download

Note: The TGEA version in the download is newer than the TGE version. Most people are using TGEA now, anyways. If you want the newer version in TGE, you can port it yourself, it shouldn't be too bad. If there is some interest in it and someone does a port, just send it to me and I'll put it up here.

Installation - TGEA

Add aStar.h, aStar.cpp, aStarIO.cpp, and aStarMesh.cpp to your project (in T3D). Replace or merge aiPlayer.cpp and .h with the files in in the zip.

Add #include "T3D/aStar.h" on top of sceneState.cpp

Add the commented part in SceneState::renderCurrentImages():
PROFILE_START(RenderInstManagerRender);
   gRenderInstManager.render();
   PROFILE_END();
//aStar
#ifdef AI_RENDER
   AStar::Get()->render(this);
#endif
//aStar
   gRenderInstManager.clear();

   mInteriorList.clear();


Add this in game/tools/missionEditor/scripts/editors/creator.ed.cs line 146:
%Mission_Item[6] = "NavMesh";

Add the following function at the end of game/tools/missionEditor/gui/objectBuilderGui.ed.gui:
function ObjectBuilderGui::buildNavMesh(%this)
{
	%this.className = "NavMesh";
	%this.process();
}

In [game]/scriptsAndAssets/server/scripts/game.cs, at the end of startGame(), add
readPaths();

At the end of endGame(), add
deletePaths();

before resetMission();

For an example, add aiDemo.mis.


Installation - TGE

Add the aStar.cc and aStar.h files to your project. Replace or merge aiPlayer.cc and .h with the files in in the zip.

In sceneState.cc, after the includes, add
#include "game/aStar.h"

in renderCurrentImages(), around line 481, add the commented code
for (i = 0; i < mTranslucentEndImages.size(); i++)
      renderImage(this, mTranslucentEndImages[i]);
//aStar
#ifdef AI_RENDER
   gAStar.render(this);
#endif
//aStar
   glMatrixMode(GL_MODELVIEW);
   glPopMatrix();

Save and compile.

In creator/editor/EditorGui.cs, at line 1306, change
%Mission_Item[3] = "Trigger";
   %Mission_Item[4] = "PhysicalZone";
   %Mission_Item[5] = "Camera";

to

%Mission_Item[3] = "NavMesh";
   %Mission_Item[4] = "Trigger";
   %Mission_Item[5] = "PhysicalZone";
   %Mission_Item[6] = "Camera";

In creator/editor/objectBuilderGui.gui, at line 643, add
function ObjectBuilderGui::buildNavMesh(%this)
{
	%this.className = "NavMesh";
	%this.process();
}

In [game]/server/scripts/game.cs, at the end of startGame(), add
readPaths();

At the end of endGame(), add
deletePaths();

before resetMission();

For an example, add aiDemo.mis.

Use

To add navigation points, open the mission editor creator. NavMesh objects are under Mission Objects / Mission. The properties are:
Interval - distance between points
XSize - number of points in x direction
YSize - number of points in y direction
Height - how far up or down the object will look to place points

There are two global preferences you can change. One is $Pref::AStar::Render. It controls whether or not the navigation mesh is rendered. Turn it on when editing the mesh, turn it off otherwise. The other is $Pref::AStar::Clearence. This controls the minimum horizontal clearance between two nodes when building paths. If you change this after creating points, you need to call buildPaths for it to effect those points.

There are three functions for path caching. Do not call any of these while the mission editor is running, it may get all messed up. They are
- deletePaths Deletes all NavMesh objects and all nodes.
- writePaths Writes all nodes to a file (called [mission name].as) that is loaded automatically on mission startup. This is the only function you need to call manually; call it once you're done editing the nav mesh.
- readPaths Reads nodes from file. Returns true on success, false on faliure.

Also there is
- buildPaths Manually rebuilds navigation information, for example if an object has been moved in the way of some nodes.

There is also a compiler switch, AI_RENDER, at the top of aStar.h. Comment this out when you distribute the game to remove all the nav mesh rendering code.

There are five functions that can be called on the AIPlayer object. They will return 0 on success, -1 on failure, and 1 if the bot is already where you want it to go. They are
- findPathTo This takes either a point or an object as an argument, and simply finds a way to the point.
- findCoverFrom This also takes a point or object, and finds a place hidden from it.
- searchCover This takes no arguments, and makes the bot go to the nearest place it can't see.
- sneakUpOn This takes either a point and a vector or an object as an argument. It finds a path to the point or the object that can't be seen by it.
- wander This takes a distance as an argument and wanders around for this distance.
#21
04/10/2008 (2:43 pm)
Ok, not what I was hoping to have done with this, but another hack for editing with 10000+ nodes (I'm currently at 104,500 nodes). If you just want to edit your navMeshes, and don't want to wait for the navNet to build after every ajustment, do this:
void AStar::linkMeshes ()
{
	if (Con::getBoolVariable("$AStar::Editing", false))
		return;
...
}

Then while you're editing, set $AStar::Editing to true in the console, and you won't have to watch it build every time.


Still working on getting the navNet saved to a file, but it might be a while still; I don't have that much free time.
#22
04/10/2008 (10:20 pm)
Nathan, what about memory usage with so many nodes?
#23
04/11/2008 (4:41 am)
While building, it takes up half my RAM, takes about 46 minutes to load (that's why I want to be able to save the navNet to a file, I don't think many players would wait 46 minutes for one level, especialy in an RPG), but preforms amazingly well when it comes to the path finding. The AI can find and/or sneak up on me from the other side of my navMesh, with very little path finding time.

This should be easy, but another thing I think would make this better, is the option to limit nodes based on their verticle angle to eachother, or declare them to be one-way. I've got a couple of really steep inclines in my level, and the AI sometimes go down it (because I have nodes there) and try to get up, but can't. If you could say that the nodes have to be within a 45 degree angle of eachother, then that would solve the problem.
#24
04/12/2008 (6:34 pm)
If this works I may use this for my ai resource....script is just not working out for me and I can't really grasp C++. Looks like a good resource, I'll try it out sometime. Would this work with 1.4.2?
#25
04/13/2008 (1:26 pm)
Here's the code to get it running in TGEA 1.7.0.

Using batch rendering should be the proper solution I guess but to me it seems to be tricky to convert the existing code. I'm not sure if I'll go on trying to do that job.

The files include all engine changes mentioned above.

Installation instructions:

1. Backup aiPlayer.h and aiPlayer.cpp

2. Copy all files go into engine\source\T3D

3. Add aStar.h and aStar.cpp to your VS project (engine/T3D)

4. Add #include "T3D/aStar.h" on top of sceneState.cpp

5. Add the bold part in SceneState::renderCurrentImages():
PROFILE_START(RenderInstManagerRender);
   gRenderInstManager.render();
   PROFILE_END();
[b]
#ifdef AI_RENDER
   gAStar.render(this);
#endif
[/b]
   gRenderInstManager.clear();

   mInteriorList.clear();

6. Add this in game/tools/missionEditor/scripts/editors/creator.ed.cs line 146:
%Mission_Item[6] = "NavMesh";

7. Add the following function at the end of game/tools/missionEditor/gui/objectBuilderGui.ed.gui:
[b]
function ObjectBuilderGui::buildNavMesh(%this)
{
	%this.className = "NavMesh";
	%this.process();
}
[/b]

8. Add this function call at the end of startGame() in game/scriptsAndAssets/server/scripts/game.cs:
BuildPaths();
#26
04/13/2008 (6:54 pm)
Stefan, you rawk! I greatly appreciate this port to TGE 1.7.

eidt: I'm having a problem with most of the functions returning -1. Is anyone else encountering this? I've used Stefan's code that he posted, and made the the required changes listed on a stock install of TGEA 1.7. I've double checked everything, and even triple checked everything to make sure I did it properly. The nav mesh is placed and builds properly. When I use .wander(#); the bot will start to move, or will walk a little ways and then stop. All other functions return -1. Anyone know what the dealio is?
#27
04/15/2008 (1:47 pm)
@Nathan: It already does and angle check, but there is a bug. On line 332 in aStar.cc, where it says
if (cur->adjs[direc])
                continue;

            if (tmp->loc.z - cur->loc.z < tmp->mesh->interval && tmpVec.len() < cur->mesh->interval*(direc&1 ? 1.5f : 1.1f))
            {
Change to
if (cur->adjs[direc])
                continue;

            if ([b]mFabs([/b]tmp->loc.z - cur->loc.z[b])[/b] < tmp->mesh->interval && tmpVec.len() < cur->mesh->interval*(direc&1 ? 1.5f : 1.1f))
            {

EDIT: I was wrong. Change it back.
#28
04/16/2008 (5:38 am)
Still need's some work, but that's an improvement!
#29
04/16/2008 (1:42 pm)
I found the issue to my problem that I mentioned aboved. For some reason the navmesh isn't being built unless I set its interval to 1 first before I change it to anything else. Thus I have to edit it in the mission editor everytime the mission loads. I'll see if I can find whats up with this.. BuildPaths() IS being called correctly in game.cs. I am also having an issue with the bots getting stuck near corners. This usually happens when theres others bots that are either in the way, or headed to the same place. If its just a bot by itself it manages to slide its way down the wall and around the corner. I've tried all kinds of different interval settings. It seems that sometimes the nodes are too close to a wall so the bot collides with the wall and isn't actually reaching or registering that it reached the next node. I should also mention that I am using nothing but an interior.

Other than that this resource is really cool; thank you for all the hard work, Dan!
#30
04/17/2008 (1:06 pm)
I know this is a dumb question but please bear with me as I am fairly new at scripting. I have implemented the code in TGEA 1.7 and load the AI demo mission. I can see the navmesh and there is a bot just standing there...how do I tell him to go forth?

Thanks,
Charlie
#31
04/17/2008 (3:24 pm)
@Charles: Press ~ to bring up the console, and enter the command you want (ie [id].wander(50) ) the id is the number that appears over the bot's head.
#32
04/17/2008 (4:01 pm)
@Dan:

I did as you instructed and the bot does an about face...he never leaves that particular node though.
#33
04/17/2008 (5:18 pm)
Okay, I got everything sorted out that I was having problems with. I had to re-arrange some of my AI stuff because it was causing complications with this resource. Everything is working as intended, and it is really badass. I have 50 bots all running around chasing me with no gag, and no lag.

Dan, brah, this is some gnarly shit. Nothin' but kudos.

img209.imageshack.us/img209/3503/navnetworkingrd0.jpg
#34
04/17/2008 (5:24 pm)
Charles, try going to aiplayer.cs and removing all the code that is in AIManager::think() or just add a return; at the beginning of the function. This way you know for sure there is nothing in the scripts that is trying to do something with your bot. You will have to manually spawn a bot with the console tho.

If your navmesh is just some green lines sticking up and there are no blue lines connecting the green lines then your navmesh has not been built, try typing in buildpaths(); into the console.
#35
04/18/2008 (5:18 am)
I have a navmesh caching system done that works fairly well. I should have that (as well as some other fixes and a TGEA version) up this afternoon.

EDIT: It's up now.
#36
04/18/2008 (12:21 pm)
@Charles: Your bot move speed may be too high, also.
#37
04/18/2008 (5:16 pm)
Thanks for getting a caching system done, I've been to busy to work on it at all! I could probably find this out just by looking through the code, but I'm to lazy (=P), so what are the other changes you made?
#38
04/18/2008 (6:41 pm)
Path smoothing, not rebuildng the mesh on exit, and a few minor cosmetic things.


Ok, I just realized that the "bug" I mentioned above was actually an intentional feaure that I forgot about. The idea was that bots could go down a steep slope, but not back up. So you might want to change it back to the way it was before. I'll be away from my computer for a while, so I can't upload the fix yet.
#39
04/20/2008 (4:21 am)
I'm getting a problem with RawData, it's an undeclared identifier. I think you forgot to #include something, or you haven't tested it on stock torque. I have found a RawData class, but you didn't #include the right file (core/coreRes.h), and when I do, I get a whole load of errors.
#40
04/20/2008 (8:24 am)
I wrote that part in tgea, then backported it, so thats probably why it didn't work.

I'll take a look at it later today when I get home.

EDIT: Ok, I fixed it.