A* Pathfinding Project
4.2.5
The A* Pathfinding Project for Unity 3D
|
AI for following paths. More...
AI for following paths.
This AI is the default movement script which comes with the A* Pathfinding Project. It is in no way required by the rest of the system, so feel free to write your own. But I hope this script will make it easier to set up movement for the characters in your game. This script works well for many types of units, but if you need the highest performance (for example if you are moving hundreds of characters) you may want to customize this script or write a custom movement script to be able to optimize it specifically for your game.
This script will try to move to a given destination. At regular intervals, the path to the destination will be recalculated. If you want to make the AI to follow a particular object you can attach the AIDestinationSetter component. Take a look at the Get Started With The A* Pathfinding Project tutorial for more instructions on how to configure this script.
Here is a video of this script being used move an agent around (technically it uses the Pathfinding.Examples.MineBotAI script that inherits from this one but adds a bit of animation support for the example scenes):
In the inspector in Unity, you will see a bunch of variables. You can view detailed information further down, but here's a quick overview.
The repathRate determines how often it will search for new paths, if you have fast moving targets, you might want to set it to a lower value. The destination field is where the AI will try to move, it can be a point on the ground where the player has clicked in an RTS for example. Or it can be the player object in a zombie game. The maxSpeed is self-explanatory, as is rotationSpeed. however slowdownDistance might require some explanation: It is the approximate distance from the target where the AI will start to slow down. Setting it to a large value will make the AI slow down very gradually. pickNextWaypointDist determines the distance to the point the AI will move to (see image below).
Below is an image illustrating several variables that are exposed by this class (pickNextWaypointDist, steeringTarget, desiredVelocity)
This script has many movement fallbacks. If it finds an RVOController attached to the same GameObject as this component, it will use that. If it finds a character controller it will also use that. If it finds a rigidbody it will use that. Lastly it will fall back to simply modifying Transform.position which is guaranteed to always work and is also the most performant option.
In this section I'm going to go over how this script is structured and how information flows. This is useful if you want to make changes to this script or if you just want to understand how it works a bit more deeply. However you do not need to read this section if you are just going to use the script as-is.
This script inherits from the AIBase class. The movement happens either in Unity's standard Update or FixedUpdate method. They are both defined in the AIBase class. Which one is actually used depends on if a rigidbody is used for movement or not. Rigidbody movement has to be done inside the FixedUpdate method while otherwise it is better to do it in Update.
From there a call is made to the MovementUpdate method (which in turn calls MovementUpdateInternal). This method contains the main bulk of the code and calculates how the AI *wants* to move. However it doesn't do any movement itself. Instead it returns the position and rotation it wants the AI to move to have at the end of the frame. The Update (or FixedUpdate) method then passes these values to the FinalizeMovement method which is responsible for actually moving the character. That method also handles things like making sure the AI doesn't fall through the ground using raycasting.
The AI recalculates its path regularly. This happens in the Update method which checks shouldRecalculatePath and if that returns true it will call SearchPath. The SearchPath method will prepare a path request and send it to the Seeker component which should be attached to the same GameObject as this script. Since this script will when waking up register to the Seeker.pathCallback delegate this script will be notified every time a new path is calculated by the OnPathComplete method being called. It may take one or sometimes multiple frames for the path to be calculated, but finally the OnPathComplete method will be called and the current path that the AI is following will be replaced.
Public Member Functions | |
Vector3 | CalculateVelocity (Vector3 position) |
Current desired velocity of the agent (excluding physics and local avoidance but it includes gravity). More... | |
virtual void | OnTargetReached () |
The end of the path has been reached. More... | |
override void | Teleport (Vector3 newPosition, bool clearPath=true) |
Instantly move the agent to a new position. More... | |
Public Member Functions inherited from AIBase | |
virtual void | FinalizeMovement (Vector3 nextPosition, Quaternion nextRotation) |
Moves the agent to a position. More... | |
virtual Vector3 | GetFeetPosition () |
Position of the base of the character. More... | |
virtual void | Move (Vector3 deltaPosition) |
Move the agent. More... | |
void | MovementUpdate (float deltaTime, out Vector3 nextPosition, out Quaternion nextRotation) |
Calculate how the character wants to move during this frame. More... | |
virtual void | SearchPath () |
Recalculate the current path. More... | |
void | SetPath (Path path) |
Make the AI follow the specified path. More... | |
Quaternion | SimulateRotationTowards (Vector3 direction, float maxDegrees) |
Simulates rotating the agent towards the specified direction and returns the new rotation. More... | |
Public Member Functions inherited from IAstarAI | |
void | FinalizeMovement (Vector3 nextPosition, Quaternion nextRotation) |
Move the agent. More... | |
void | Move (Vector3 deltaPosition) |
Move the agent. More... | |
void | MovementUpdate (float deltaTime, out Vector3 nextPosition, out Quaternion nextRotation) |
Calculate how the character wants to move during this frame. More... | |
void | SearchPath () |
Recalculate the current path. More... | |
void | SetPath (Path path) |
Make the AI follow the specified path. More... | |
Public Attributes | |
bool | alwaysDrawGizmos |
Draws detailed gizmos constantly in the scene view instead of only when the agent is selected and settings are being modified. More... | |
bool | constrainInsideGraph = false |
Ensure that the character is always on the traversable surface of the navmesh. More... | |
float | endReachedDistance = 0.2F |
Distance to the end point to consider the end of path to be reached. More... | |
float | maxAcceleration = -2.5f |
How quickly the agent accelerates. More... | |
float | pickNextWaypointDist = 2 |
How far the AI looks ahead along the path to determine the point it moves to. More... | |
float | rotationSpeed = 360 |
Rotation speed in degrees per second. More... | |
float | slowdownDistance = 0.6F |
Distance from the end of the path where the AI will start to slow down. More... | |
bool | slowWhenNotFacingTarget = true |
Slow down when not facing the target direction. More... | |
CloseToDestinationMode | whenCloseToDestination = CloseToDestinationMode.Stop |
What to do when within endReachedDistance units from the destination. More... | |
Public Attributes inherited from AIBase | |
bool | canMove = true |
Enables or disables movement completely. More... | |
bool | canSearch = true |
Enables or disables recalculating the path at regular intervals. More... | |
bool | enableRotation = true |
If true, the AI will rotate to face the movement direction. More... | |
Vector3 | gravity = new Vector3(float.NaN, float.NaN, float.NaN) |
Gravity to use. More... | |
LayerMask | groundMask = -1 |
Layer mask to use for ground placement. More... | |
float | height = 2 |
Radius of the agent in world units. More... | |
float | maxSpeed = 1 |
Max speed in world units per second. More... | |
IMovementPlane | movementPlane = GraphTransform.identityTransform |
Plane which this agent is moving in. More... | |
OrientationMode | orientation = OrientationMode.ZAxisForward |
Determines which direction the agent moves in. More... | |
float | radius = 0.5f |
Height of the agent in world units. More... | |
float | repathRate = 0.5f |
Determines how often the agent will search for new paths (in seconds). More... | |
bool | updatePosition = true |
Determines if the character's position should be coupled to the Transform's position. More... | |
bool | updateRotation = true |
Determines if the character's rotation should be coupled to the Transform's rotation. More... | |
Protected Member Functions | |
virtual void | CalculateNextRotation (float slowdown, out Quaternion nextRotation) |
override Vector3 | ClampToNavmesh (Vector3 position, out bool positionChanged) |
Constrains the character's position to lie on the navmesh. More... | |
override void | MovementUpdateInternal (float deltaTime, out Vector3 nextPosition, out Quaternion nextRotation) |
Called during either Update or FixedUpdate depending on if rigidbodies are used for movement or not. More... | |
override void | OnDisable () |
override void | OnPathComplete (Path newPath) |
Called when a requested path has been calculated. More... | |
override int | OnUpgradeSerializedData (int version, bool unityThread) |
Handle serialization backwards compatibility. More... | |
Protected Member Functions inherited from AIBase | |
AIBase () | |
void | ApplyGravity (float deltaTime) |
Accelerates the agent downwards. More... | |
Vector2 | CalculateDeltaToMoveThisFrame (Vector2 position, float distanceToEndOfPath, float deltaTime) |
Calculates how far to move during a single frame. More... | |
virtual void | CalculatePathRequestEndpoints (out Vector3 start, out Vector3 end) |
Outputs the start point and end point of the next automatic path request. More... | |
void | CancelCurrentPathRequest () |
virtual void | FindComponents () |
virtual void | FixedUpdate () |
Called every physics update. More... | |
virtual void | OnDrawGizmos () |
virtual void | OnDrawGizmosSelected () |
virtual void | OnEnable () |
Called when the component is enabled. More... | |
Vector3 | RaycastPosition (Vector3 position, float lastElevation) |
Checks if the character is grounded and prevents ground penetration. More... | |
override void | Reset () |
Handle serialization backwards compatibility. More... | |
Quaternion | SimulateRotationTowards (Vector2 direction, float maxDegrees) |
Simulates rotating the agent towards the specified direction and returns the new rotation. More... | |
virtual void | Start () |
Starts searching for paths. More... | |
virtual void | Update () |
Called every frame. More... | |
void | UpdateVelocity () |
Protected Member Functions inherited from VersionedMonoBehaviour | |
virtual void | Awake () |
Protected Attributes | |
PathInterpolator | interpolator = new PathInterpolator() |
Helper which calculates points along the current path. More... | |
Path | path |
Current path which is followed. More... | |
Protected Attributes inherited from AIBase | |
CharacterController | controller |
Cached CharacterController component. More... | |
Vector2 | lastDeltaPosition |
Amount which the character wants or tried to move with during the last frame. More... | |
float | lastDeltaTime |
Delta time used for movement during the last frame. More... | |
float | lastRepath = float.NegativeInfinity |
Time when the last path request was started. More... | |
int | prevFrame |
Last frame index when prevPosition1 was updated. More... | |
Vector3 | prevPosition1 |
Position of the character at the end of the last frame. More... | |
Vector3 | prevPosition2 |
Position of the character at the end of the frame before the last frame. More... | |
Rigidbody | rigid |
Cached Rigidbody component. More... | |
Rigidbody2D | rigid2D |
Cached Rigidbody component. More... | |
RVOController | rvoController |
Cached RVOController component. More... | |
Seeker | seeker |
Cached Seeker component. More... | |
Vector3 | simulatedPosition |
Position of the agent. More... | |
Quaternion | simulatedRotation |
Rotation of the agent. More... | |
Transform | tr |
Cached Transform component. More... | |
Vector2 | velocity2D |
Current desired velocity of the agent (does not include local avoidance and physics). More... | |
float | verticalVelocity |
Velocity due to gravity. More... | |
bool | waitingForPathCalculation = false |
Only when the previous path has been calculated should the script consider searching for a new path. More... | |
Properties | |
bool IAstarAI. | canMove [get, set] |
Enables or disables movement completely. More... | |
bool IAstarAI. | canSearch [get, set] |
Enables or disables recalculating the path at regular intervals. More... | |
bool | hasPath [get] |
True if this agent currently has a path that it follows. More... | |
float IAstarAI. | height [get, set] |
Radius of the agent in world units. More... | |
float IAstarAI. | maxSpeed [get, set] |
Max speed in world units per second. More... | |
bool | pathPending [get] |
True if a path is currently being calculated. More... | |
float IAstarAI. | radius [get, set] |
Height of the agent in world units. More... | |
bool | reachedDestination [get] |
True if the ai has reached the destination. More... | |
bool | reachedEndOfPath [get, protected set] |
True if the agent has reached the end of the current path. More... | |
float | remainingDistance [get] |
Remaining distance along the current path to the end of the path. More... | |
float | speed [get, set] |
Maximum speed in world units per second. More... | |
Vector3 | steeringTarget [get] |
Point on the path which the agent is currently moving towards. More... | |
Vector3 | targetDirection [get] |
Direction that the agent wants to move in (excluding physics and local avoidance). More... | |
bool | TargetReached [get] |
True if the end of the path has been reached. More... | |
float | turningSpeed [get, set] |
Rotation speed. More... | |
Properties inherited from AIBase | |
float | centerOffset [get, set] |
Offset along the Y coordinate for the ground raycast start position. More... | |
Vector3 | desiredVelocity [get] |
Velocity that this agent wants to move with. More... | |
Vector3 | destination [get, set] |
Position in the world that this agent should move to. More... | |
bool | isStopped [get, set] |
Gets or sets if the agent should stop moving. More... | |
System.Action | onSearchPath [get, set] |
Called when the agent recalculates its path. More... | |
Vector3 | position [get] |
Position of the agent. More... | |
Quaternion | rotation [get] |
Rotation of the agent. More... | |
bool | rotationIn2D [get, set] |
If true, the forward axis of the character will be along the Y axis instead of the Z axis. More... | |
virtual bool | shouldRecalculatePath [get] |
True if the path should be automatically recalculated as soon as possible. More... | |
Transform | target [get, set] |
Target to move towards. More... | |
bool | usingGravity [get, private set] |
Indicates if gravity is used during this frame. More... | |
Vector3 | velocity [get] |
Actual velocity that the agent is moving with. More... | |
Properties inherited from IAstarAI | |
bool | canMove [get, set] |
Enables or disables movement completely. More... | |
bool | canSearch [get, set] |
Enables or disables recalculating the path at regular intervals. More... | |
Vector3 | desiredVelocity [get] |
Velocity that this agent wants to move with. More... | |
Vector3 | destination [get, set] |
Position in the world that this agent should move to. More... | |
bool | hasPath [get] |
True if this agent currently has a path that it follows. More... | |
float | height [get, set] |
Radius of the agent in world units. More... | |
bool | isStopped [get, set] |
Gets or sets if the agent should stop moving. More... | |
float | maxSpeed [get, set] |
Max speed in world units per second. More... | |
System.Action | onSearchPath [get, set] |
Called when the agent recalculates its path. More... | |
bool | pathPending [get] |
True if a path is currently being calculated. More... | |
Vector3 | position [get] |
Position of the agent. More... | |
float | radius [get, set] |
Height of the agent in world units. More... | |
bool | reachedDestination [get] |
True if the ai has reached the destination. More... | |
bool | reachedEndOfPath [get] |
True if the agent has reached the end of the current path. More... | |
float | remainingDistance [get] |
Remaining distance along the current path to the end of the path. More... | |
Quaternion | rotation [get] |
Rotation of the agent. More... | |
Vector3 | steeringTarget [get] |
Point on the path which the agent is currently moving towards. More... | |
Vector3 | velocity [get] |
Actual velocity that the agent is moving with. More... | |
Static Private Attributes | |
static NNConstraint | cachedNNConstraint = NNConstraint.Default |
Additional Inherited Members | |
Static Public Attributes inherited from AIBase | |
static readonly Color | ShapeGizmoColor = new Color(240/255f, 213/255f, 30/255f) |
|
protectedvirtual |
Vector3 CalculateVelocity | ( | Vector3 | position | ) |
Current desired velocity of the agent (excluding physics and local avoidance but it includes gravity).
|
protectedvirtual |
Constrains the character's position to lie on the navmesh.
Not all movement scripts have support for this.
position | Current position of the character. |
positionChanged | True if the character's position was modified by this method. |
Reimplemented from AIBase.
|
protectedvirtual |
Called during either Update or FixedUpdate depending on if rigidbodies are used for movement or not.
Implements AIBase.
|
protectedvirtual |
Reimplemented from AIBase.
|
protectedvirtual |
Called when a requested path has been calculated.
A path is first requested by #UpdatePath, it is then calculated, probably in the same or the next frame. Finally it is returned to the seeker which forwards it to this function.
Implements AIBase.
Reimplemented in LegacyAIPath.
|
virtual |
The end of the path has been reached.
If you want custom logic for when the AI has reached it's destination add it here. You can also create a new script which inherits from this one and override the function in that script.
This method will be called again if a new path is calculated as the destination may have changed. So when the agent is close to the destination this method will typically be called every repathRate seconds.
|
protectedvirtual |
Handle serialization backwards compatibility.
Reimplemented from AIBase.
|
virtual |
Instantly move the agent to a new position.
This will trigger a path recalculation (if clearPath is true, which is the default) so if you want to teleport the agent and change its destination it is recommended that you set the destination before calling this method.
The current path will be cleared by default.
Reimplemented from AIBase.
bool alwaysDrawGizmos |
Draws detailed gizmos constantly in the scene view instead of only when the agent is selected and settings are being modified.
|
staticprivate |
bool constrainInsideGraph = false |
Ensure that the character is always on the traversable surface of the navmesh.
When this option is enabled a GetNearest query will be done every frame to find the closest node that the agent can walk on and if the agent is not inside that node, then the agent will be moved to it.
This is especially useful together with local avoidance in order to avoid agents pushing each other into walls.
This option also integrates with local avoidance so that if the agent is say forced into a wall by other agents the local avoidance system will be informed about that wall and can take that into account.
Enabling this has some performance impact depending on the graph type (pretty fast for grid graphs, slightly slower for navmesh/recast graphs). If you are using a navmesh/recast graph you may want to switch to the RichAI movement script which is specifically written for navmesh/recast graphs and does this kind of clamping out of the box. In many cases it can also follow the path more smoothly around sharp bends in the path.
It is not recommended that you use this option together with the funnel modifier on grid graphs because the funnel modifier will make the path go very close to the border of the graph and this script has a tendency to try to cut corners a bit. This may cause it to try to go slightly outside the traversable surface near corners and that will look bad if this option is enabled.
Below you can see an image where several agents using local avoidance were ordered to go to the same point in a corner. When not constraining the agents to the graph they are easily pushed inside obstacles.
float endReachedDistance = 0.2F |
Distance to the end point to consider the end of path to be reached.
When the end is within this distance then OnTargetReached will be called and reachedEndOfPath will return true.
|
protected |
Helper which calculates points along the current path.
float maxAcceleration = -2.5f |
How quickly the agent accelerates.
Positive values represent an acceleration in world units per second squared. Negative values are interpreted as an inverse time of how long it should take for the agent to reach its max speed. For example if it should take roughly 0.4 seconds for the agent to reach its max speed then this field should be set to -1/0.4 = -2.5. For a negative value the final acceleration will be: -acceleration*maxSpeed. This behaviour exists mostly for compatibility reasons.
In the Unity inspector there are two modes: Default and Custom. In the Default mode this field is set to -2.5 which means that it takes about 0.4 seconds for the agent to reach its top speed. In the Custom mode you can set the acceleration to any positive value.
|
protected |
Current path which is followed.
float pickNextWaypointDist = 2 |
How far the AI looks ahead along the path to determine the point it moves to.
In world units. If you enable the alwaysDrawGizmos toggle this value will be visualized in the scene view as a blue circle around the agent.
Here are a few example videos showing some typical outcomes with good values as well as how it looks when this value is too low and too high.
{aipath/picknext/video_02_default.mp4} | A too low value and a too low acceleration will result in the agent overshooting a lot and not managing to follow the path well. |
{aipath/picknext/video_02_100.mp4} | A low value but a high acceleration works decently to make the AI follow the path more closely. Note that the AILerp component is better suited if you want the agent to follow the path without any deviations. |
{aipath/picknext/video_07_default.mp4} | A reasonable value in this example. |
{aipath/picknext/video_10_default.mp4} | A reasonable value in this example, but the path is followed slightly more loosely than in the previous video. |
{aipath/picknext/video_20_default.mp4} | A too high value will make the agent follow the path too loosely and may cause it to try to move through obstacles. |
float rotationSpeed = 360 |
Rotation speed in degrees per second.
Rotation is calculated using Quaternion.RotateTowards. This variable represents the rotation speed in degrees per second. The higher it is, the faster the character will be able to rotate.
float slowdownDistance = 0.6F |
Distance from the end of the path where the AI will start to slow down.
bool slowWhenNotFacingTarget = true |
Slow down when not facing the target direction.
Incurs at a small performance overhead.
CloseToDestinationMode whenCloseToDestination = CloseToDestinationMode.Stop |
What to do when within endReachedDistance units from the destination.
The character can either stop immediately when it comes within that distance, which is useful for e.g archers or other ranged units that want to fire on a target. Or the character can continue to try to reach the exact destination point and come to a full stop there. This is useful if you want the character to reach the exact point that you specified.
|
getsetprivate |
Enables or disables movement completely.
If you want the agent to stand still, but still react to local avoidance and use gravity: use isStopped instead.
This is also useful if you want to have full control over when the movement calculations run. Take a look at MovementUpdate
|
getsetprivate |
Enables or disables recalculating the path at regular intervals.
Setting this to false does not stop any active path requests from being calculated or stop it from continuing to follow the current path.
Note that this only disables automatic path recalculations. If you call the SearchPath() method a path will still be calculated.
|
get |
True if this agent currently has a path that it follows.
|
getsetprivate |
Radius of the agent in world units.
This is visualized in the scene view as a yellow cylinder around the character.
|
getsetprivate |
Max speed in world units per second.
|
get |
True if a path is currently being calculated.
|
getsetprivate |
Height of the agent in world units.
This is visualized in the scene view as a yellow cylinder around the character.
This value is currently only used if an RVOController is attached to the same GameObject, otherwise it is only used for drawing nice gizmos in the scene view. However since the height value is used for some things, the radius field is always visible for consistency and easier visualization of the character. That said, it may be used for something in a future release.
|
get |
True if the ai has reached the destination.
This is a best effort calculation to see if the destination has been reached. For the AIPath/RichAI scripts, this is when the character is within endReachedDistance world units from the destination. For the AILerp script it is when the character is at the destination (±a very small margin).
This value will be updated immediately when the destination is changed (in contrast to reachedEndOfPath), however since path requests are asynchronous it will use an approximation until it sees the real path result. What this property does is to check the distance to the end of the current path, and add to that the distance from the end of the path to the destination (i.e. is assumes it is possible to move in a straight line between the end of the current path to the destination) and then checks if that total distance is less than endReachedDistance. This property is therefore only a best effort, but it will work well for almost all use cases.
Furthermore it will not report that the destination is reached if the destination is above the head of the character or more than half the height of the character below its feet (so if you have a multilevel building, it is important that you configure the height of the character correctly).
The cases which could be problematic are if an agent is standing next to a very thin wall and the destination suddenly changes to the other side of that thin wall. During the time that it takes for the path to be calculated the agent may see itself as alredy having reached the destination because the destination only moved a very small distance (the wall was thin), even though it may actually be quite a long way around the wall to the other side.
In contrast to reachedEndOfPath, this property is immediately updated when the destination is changed.
|
getprotected set |
True if the agent has reached the end of the current path.
Note that setting the destination does not immediately update the path, nor is there any guarantee that the AI will actually be able to reach the destination that you set. The AI will try to get as close as possible. Often you want to use reachedDestination instead which is easier to work with.
It is very hard to provide a method for detecting if the AI has reached the destination that works across all different games because the destination may not even lie on the navmesh and how that is handled differs from game to game (see also the code snippet in the docs for destination).
|
get |
Remaining distance along the current path to the end of the path.
For the RichAI movement script this may not always be precisely known, especially when far away from the destination. In those cases an approximate distance will be returned.
If the agent does not currently have a path, then positive infinity will be returned.
|
getset |
Maximum speed in world units per second.
|
get |
Point on the path which the agent is currently moving towards.
This is usually a point a small distance ahead of the agent or the end of the path.
If the agent does not have a path at the moment, then the agent's current position will be returned.
|
get |
Direction that the agent wants to move in (excluding physics and local avoidance).
|
get |
True if the end of the path has been reached.
|
getset |
Rotation speed.