First you need to place one instance of the AstarPath.cs script in your scene, this component will do all the computing.
Then on the object you want to find the path for (your player, enemy or similar), attach the Seeker script and your movement script.
Important : If you want to use Javascript to send pathfinding calls, you need to place the ‘Pathfinding’ folder inside the Standard Assets Folder, you don’t need to do this if you only want to receive completed paths
In your movement script, add this when you want to find the path, for example when the player clicks on a spot on the ground in a RTS game.
C#
(GetComponent (typeof(Seeker)) as Seeker).StartPath ( startPoint, endPoint);
Javascript
GetComponent (Seeker).StartPath ( startPoint, endPoint);
The function does not return anything, so how do we know where to go?
The script will process the request and send the completed path back as fast as it can.
To get the completed path, add this function to your code:
C#
public void PathComplete (Vector3[] points) {
//The points are all the waypoints you need to follow to get to the target
}
Javascript
function PathComplete (points : Vector3[]) {
//The points are all the waypoints you need to follow to get to the target
}
Then it is just for your enemy/droid/whatever to follow the points, an example of this can be seen in the AIFollow script in the example project.
Other messages sent by the Seeker script
If you want to know when the pathfinding returned an error, you have to set the On Error variable in the Seeker component to Error Message and add this function to your code:
C#
public void PathError () {
Debug.Log ("Oh no, the pathfinding returned an error!!");
}
Or you can set On Error to Empty Array and the script will send a PathComplete message but with an empty array.
If you have any questions please post them as a comment or mail me at aron[dot]g[at]me[dot]com
Does this work with moving objects such as a player moving through the world. Because when I tried the seeker only moved when i hit space bar. I want it to follow always. Please help?
The player should move when you click on the terrain (if the point is inside the grid), the same script executes when you press the space bar.
When you say you want it to always follow, do you mean every time you click somewhere or every time you move the mouse?
Also you should make sure the clicker.cs script is placed on your camera and that it is referring to the player.
PS: I also changed the code on the page, I had forgot that you must assign the path variable of the seeker script too (darn, big mistake), if you used that part in your code, make sure to change it to the new one since the previous code wont work.
Does this handle obstacle avoidance? I don’t know anything about A* (except that it is the most common). I need pathfinding for creatures in my game, but I need them to calculate paths around rocks and trees and such.
That’s what it’s all about, obstacle avoidance!
The script calculates a path from A to B and return it as an array of positions the creature should follow.
If you have colliders on the objects the script will find a way around them (if any).
How does it generate the grid? Does it look for something of the terrain type or can i use custom geometry to function as the level?
How is that handled?
The grid is first set up as a 2d array containing all the nodes.
Then if the follow terrain option is on, it sets the Y position of the node to the terrain height at each node.
After that it uses Physics.CheckCapsule, CheckSphere, Raycast or OverlapSphere (depending on what you have chosen) to see if there are any objects touching the node.
You can use custom geometry, if you attach colliders on the objects the script will find them. In the current version though the grid is flat if you don’t use a terrain.
@admin
Oke, so since i have custom geometry which results in a flat grid.
How does that affect movement on terrain like
http://labs.superflat.nl/wp-content/uploads/2009/09/flatville.jpg ?
Currently it spits out an error:
NullReferenceException: Object reference not set to an instance of an object
AstarPath.Scan () (at Assets\Scripts\Pathfinding\AstarPath.cs:492)
AstarPath.Start () (at Assets\Scripts\Pathfinding\AstarPath.cs:263)
Which i assume is due to the followterrain option being enabled when it isn’t even shown because there isn’t a terrain object.
@ Kevin
Hmm, weird, probably you will have to change the code in the editor script to always show the variable. I will remove this error in the next version.
Look in the AstarPathEditor.cs script and remove this if:
if (Terrain.activeTerrain) {
that would make the variable visible all the time.
Everything would work on that terrain, but the script wont be able to take angles (slopes and such things) into account.
Otherwise it will work fine.
Unfortunately that would make the character able to climb up the vertical wall in your scene.
I think I will add a function to get the height from raycasting in the next version.
@admin
But since it doesn’t work with height, won’t the raycast find the cliff to be an obstacle?
And i could just place colliders where i don’t want the player to go right? Just trying to figure out how to set this up without the heights in a grid.
@ Kevin
Well you don’t want the terrain to be an obstacle, do you?
Place the terrain in the layer ignored by the script.
Also choose the capsule option, that’s the best option in this case and choose a height which covers the whole map.
Yea, you can place colliders were you don’t want the player to go.
:Lightbulb: Place a collider where the slope is to steep (like on the cliff). That way the player wont go there.
Which is what i was thinking
Just tested it a bit, it’s still quite dodgy. The grid doesn’t show in the editor unless i’m playing the scene. Is the grid also generated from 0,0,0? Since i aligned the terrain at 0,0,0 it doesn’t line up with the grid very well.
Getting two errors still
Sorry for spamming the comments
Oh and while i’m spamming, why does the seeker stop so far off the target? I tried changing some of the variables on the seeker but it didn’t seem to effect anything.
@ Kevin
The offset variable adjusts where the grid starts, in your version you can’t adjust the offset for x and y separately, that will be changed in the next version ( it seams like I always say that).
VertexCount hasn’t anything to do with the script, that’s probably a mesh in your scene.
SetSpeed is called from the AI script, it wants to send the message to the AIAnimation script, you can remove the message if you don’t use it.
The distance is hard coded at the moment.
At line 56 in the AI script
if (Vector3.Distance (transform.position,p) <5) {
Just saw that the vertex one is a unity error it seems. Something to do with gizmo’s.
Anyhow thanks for the feedback, i could make ends meet so far and get it working. I eagerly await the next version!
I think you misunderstood or my question wasn’t to helpful. What I meant was will this work with an FPS. Say I want a robot to follow my player, can I have the robot follow it constantly and update its position every couple frames.
Yea, that will work, at least if you don’t have a lot of robots, then it might slow down the game a bit.
You can probably make it update the players position every 0.5 seconds or something, that will be more than enough. Then if the robot can see the player it will skip the pathfinding and go straight towards the player.
Does A* work with dynamic objects? Just skimming through it, it looks like it depends heavily on pre-generated nodes. What happens if objects move at runtime?
The next version will have support for changing the grid. If objects move at runtime the script wont notice unless you recalculate the grid, it’s a bit tricky but you can have objects moving at runtime.
Hi Aron,
Been playing with the new version today. When i tried to implement the new stuff i got that vertex error again. Maybe it’s not from just unity, here’s the error code
I think maybe it has to do with the grid freaking out.
@ Kevin
Hi Kevin, I have localised the error, and it seams like you don’t have a terrain object in your scene, that’s why it is throwing a null reference exception. Bad of me not to have though of that possibility and coded it differently.
If you have a terrain object in your scene and still gets this error it might be because you have a very large terrain. Although with a vertex count of 60 000 it would only require a height map 245 pixels wide, so that’s a bit strange.
Try to replace the code on line 730 with this
How would I change the update speed. I dont know where and what to change? Thank you for your help the program looks really good.
@tntxplode
The update speed for… what?
If you mean for the AI, it is hard coded in the start function, a while loop.
while (true) {FindPoint (curpoint);
yield WaitForSeconds (0.2);
}
Sorry for the AI script to be so badly written, I only used it as a test script.
@admin
Hi Aron, The errors only occur if you don’t setup the grid properly to work with the geometry instead of the terrain. I don’t recall which setting it was that caused it. The 60k vertex one occurs if the grid generated is too big. So if you set everything up right(who would have guessed) they don’t appear.
Thanks for your work btw, it’s amazing. However i have 1 question still. I got some clickable objects/characters in my scene. If you click on them A* returns that that node is unwalkable. How do i make it so it doesn’t register the click on a character and doesn’t move?
<3
Hi Kevin, I will have a look at those errors and see if I can replicate them.
What you have to do is to, in your script controlling the clicking (maybe clicker.cs if you haven’t written an own) you have to add an if statement to ignore all characters/objects that are clickable:
if (hit.transform.gameObject.tag == "clickable") {return;
}
Or something similar.
Although, there is one thing I don’t understand in your question, if the script returns that the node is unwalkable, it shouldn’t move, the objects would be ignored automatically.
I’m not at home right now, i’ll send you a copy of the project (or just the error) tomorrow, thanks for the addendum
Hey Aron,
here’s the warning it spits out
But it’s just a warning although if you click something it would still move if it wasn’t for the code you gave me earlier.
@Kevin
Hi Kevin
I added that function since I wanted the script to correct the user if he clicked on the wrong node, right next to the one he should have clicked. So it walks to the first neighbour of that node.
I forgot it when I first read your comment.
But with the code I gave you it’s working, so everything is fine.
Hi,
You mentioned a trick there:
“The next version will have support for changing the grid. If objects move at runtime the script wont notice unless you recalculate the grid, it’s a bit tricky but you can have objects moving at runtime.”
Where and how would you recommend to recalculate the grid? I assume a full scan is overkill.
The reason I am asking is that I would like to have a lot of units moving around. When a unit stops, then it becomes an obstacle, therefore the grid changes.
I guess SetNodes is the answer
Hello,
I am not a programmer and I am new in Unity. I would like to use pathfinding for the enemies of my player. I want that enemies walk toward him without any click of the mouse. They have just to reach the player to destroy him. I do not have any idea on how to do that inside the scripts. Could You please help me? thanks so much!
Hi Stefano
If you are new to programming I suggest that you don’t start with this, since it involves some quite complicated stuff, I can explain it if you want but I think it will be quite hard for a person who is new to scripting.
Although if the enemies are going to move towards the player only if they see him, then it won’t need complicated stuff, if that’s the case I suggest that you put up a question on the forum about it.
ok thanks so much
Hey Aron,
Question: I change the status of the node at runtime and the seeker still pass thru unwalkable node. How come? I set the current nod to unwalkable and I call RecalcNeighbours. Any other way to do it?
Hi Dave
The reason is probably because you only recalculate the neighbours of the node you are changing, but the nodes around it will still have that node in their neighbours array, the script does actually not check if the node it is travelling is walkable or not, it assumes that the pre computed neighbours array is correct, therefore you have to call RecalcNeighbours on the surrounding nodes too (easily accessible through the neighbours array in the first node).
You can also call SetNodes to change a part of the grid, although I don’t remember if it was included in the latest version to be able to call SetNodes on only a single node.
Hope it helps,
Aron
Thx a lot!
Nice code by the way, works great!
Hey Aron,
Found a little problem, maybe you can check it out.
When you create a Large Grid (ex: 400 x 400) with a small node size (ex: 5) and you start making succesive PF calls (2-3 per frame) your script start leaking like crazy. By that I mean 10mb / sec! I never saw a game reaching 1.7go of memory usage so fast
Tought you’ll be interested
Dave
@Dave
Thanks Dave.
I think I have an idea of what it can be.
For each path the script creates a binary heap, which basically is an array of Node references, when you use a 400*400 grid it will create a heap which is half the size of the grid meaning the array will have a length of 80 000, fortunately, in the new version of the script which will be released soon it doesn’t create new heaps for every path, instead it only creates one at startup, that will (probably) solve the problem, and it came with a performance boost too : )
Got an ETA for that “Released soon”?
No rush, just curious cause your script is great so far ^_^
Soon, the only thing I have got left now is writing the documentation… And fixing a thing some guy on the forum posted today.
Hi. Thanks for sharing your work with the community. I have some difficulties to get it working.
I created a terrain and basically reproduced the scene “Terrain” from your project, but it does not work: it does not show the grid on the terrain and the it doesn´t show the light when I click on the terrain.
I checked all the variables in the inspector and it looks like everything is the same. There´s also an error (I don´t know if it´s the cause of not working) but Unity says vertexCount > 60000.
Is there anything in the project that needs another configuration and it´s not easy to perceive by just checking the project?
Thanks in advance
Hi Ben
That’s a common problem (I get the question often anyway) and it is related to the 60K vertex error.
The thing is that the GetHeight function for the terrain won’t work when the vertex count of the terrain is higher than 60K.
To solve this you can switch to Raycast mode, place the terrain is a specific “Ground” layer, and then change the layer mask to only include that layer.
That’s really a great work you just throwed us !
But I was wondering if it would be possible to include this sytem in an AssetBundle, and load it after the main scene (which would include the terrain, the mesh or… whatever !) ?
Hi K2
I don’t own Unity Pro so I don’t have so much experience with Asset Bundles, however I don’t think it would work since a grid requires very much memory, for example a 100*100 grid would require about 1.9 mb of memory, and that is just the most important stuff.
I am working on a serializer which could (maybe) cut down the file size a bit though.
If you want to trigger the scanning in a later point in the game you can set Calculate On Startup to false (at the bottom in the Static Settings) and then call AstarPath.active.Scan () whenever you want, this will cause some lag though.
It works like a charm !!
Just to mention that I use two parameters from your method : AstarPath.active.Scan(true, 0); // default parameters ?
And if I call it from an “Awake”, AstarPath won’t be initialised, so you need to call it from at less “Start”.
Again, thanks a lot Aron
I have been frustrating myself trying to figure out why my target gameobject will not move to the clicked position in my scene. I have everything working I can move the target object in my scene view to any location within the grid on my map and my character will move to that position. I just don’t understand what would cause the clicker script to not work when moving the target gameobject. Any ideas as to why this is not working?
I am not really understanding your question.
First you say that you have everything working, the character will move to the target, and then that it is not working.
Could you clarify it a bit?
I’m having troubles using this. Here’s what I’m trying to do: Have a 3d scene, but it controls entirely in 2d (think new super mario bros). I have a game object that can fly around in this scene and needs to find a path to the player. Using a grid with ray casting doesn’t work because nothing under the platforms will have a node. I’ve tried Bounds and List, but I just get a bunch of paths that go through my obstacles :/
Any tips on how I should be doing this for a 2.5D platformer?
Hi
You could try the Flat mode (instead of Raycasting for the Y position), this would require the scene to be laid out in X and Z directions instead of what you probably use, X and Y or possibly z and Y.
If you are using the bounds mode and the paths go through your obstacles, then the reason is that your layer mask for the Bounds mode is set up wrong, make sure the obstacles are in a layer which is included in the mask.
This would also require you to rotate your scene 90 degrees though (using XZ not XY or ZY).
Now I have one more thing I need to implement in the comming versions, the ability to rotate the grid.
Well, I made a simple script in Javascript that will move the enemy or object to a target position. This would be a good basis for anyone wanting to make some AI in javascript using this A* pathfinding. Attach this to the object that you want to move, it should also have seeker and AIFollow script on it. The rate, is how often it updates the path, I found .5 to be a pretty good number, but if you want more enemies to move, raising this will help keep the FPS down.
Anyhow, here it is:
var target : Transform;
var timer : float = 0.0;
var Rate : float = 1.0;
function FixedUpdate () {
timer += Time.deltaTime;
if(timer > Rate){
this.GetComponent("Seeker").StartPath(transform.position,target.position);
timer = 0;
}
}