Get Started with the A* Pathfinding Project

本系列教程为A*寻路插件的官方教程翻译,本来我想找现成的文档貌似没有无奈之下只好自行翻译顺便造福群众。

 

Get Started with the A* Pathfinding Project

A*插件大概的功能就是A到B点之间最佳的路径,读完本篇教程你能学到在项目中配置A*并且写一个简单的能规避障碍物的AI。

Pathfinding is all about finding the best path between point A and B. This is what the A* Pathfinding Project does, in this tutorial you will learn how to set up the project in a new scene and get a simple AI moving while avoiding obstacles.

你将要写的这个AI不会非常的高深莫测,只需要少量的代码获取当前移动与剩余跟随路径之和。如果你要自定义更多的高级功能可以扩展AIPath与RichAI,RichAI的用法详见Part2

This AI you will write will not be very advanced, it is just the minimal amount of code needed to get moving and following a path. If you want a more advanced AI you can either extend the script you will write in this tutorial or use (or extend) the AIPath or RichAI scripts included in the package (see part 2 for basic usage of the RichAI component).

使用A*Path插件制作的塔防游戏的视频链接 : http://www.youtube.com/watch?feature=player_embedded&v=PUJSvd53v4k 需要FQ ,你懂的~

If you like videos more than just text you can follow the excellent tutorial by Gabriel Williams (Unity Cookie) in part 8 of the series on making a Tower Defence game: http://www.youtube.com/watch?feature=player_embedded&v=PUJSvd53v4k The video covers most things which will be discussed in this tutorial.

Downloading

The first thing you need to do, if you haven't done so already, is to download the A* Pathfinding Project
The project can be downloaded from here, you can either download the free version with some limited features (but still very powerful) or buy the pro version with more cool stuff included.
If you want, you can explore the different example scenes in the project before you start with the next section.

Javascript (Unityscript)

If you are using UnityScript, you should first follow the instructions on the page Working with Javascript.

All example code is in C#. But I hope it will not be too hard following them since C# and Unityscript are quite similar. You should know that unityscript does not support optional parameters. So you must always pass all parameters to a function. If you get an error message that the function you are trying to call has no such overload, this might be the problem. Then check the docs for all parameters and their default values.

Deploying for Mobile

如果你要开发移动端的版本请先阅读   Deploying for mobile

Depending on which platform you are building for, you might want to read this page first: Deploying for mobile

报错 ?

在这个阶段 你可以先检查Readme_upgrading.txt文件,如果你从一个旧的版本更新到新的版本最常见的问题就是命名空间冲突,改一下命名空间名就行了,更多的报错信息请看 http://forum.arongranberg.com 论坛地址 。

Errors?

At this stage, if you are getting any compiler errors from the code you can first check the Readme_upgrading.txt file included, if you are upgrading from an older version. A common problem is that classes in the A* Pathfinding Project have the same name as classes in your project. This can be solved either by renaming one of the classes, or placing your class in a namespace so it will not conflict with the other class anymore.

If you are still getting errors, take a look at the forums (see http://forum.arongranberg.com) to see if anyone else has the same problem or post a new question there.

概览

1.对于使用A*寻路插件的项目来说 AstarPath.cs这个插件是核心脚本

  在AStarPath.cs监视面板上你可以创建所有的Graphs(这里意为可视化操作A*path的组件) 并且调整所有的设置

  在使用寻路的场景中AStarPath组件应该是唯一的,这个比较好理解

  AStarPath组件位于 Components->Pathfinding->Pathfinder

2.第二重要的组件是Seeker.cs组件,Seeker组件必须附着在有Pathfinding的GameObject上,例如 所有的AI

  Seeker组件处理路径的调用,Seeker组件不是必须的,可以把它看作一个优化措施

3.在最后 有一类修饰脚本,例如 SimpleSmoothModifier.cs ,详见 Using Modifiers.

Overview

  • The central script of the A* Pathfinding Project is the script 'astarpath.cs', it acts as a central hub for everything else.
    In the AstarPath inspector you create all graphs and adjust all settings.
    There should always be one (always one, no more) astarpath.cs component in a scene which uses Pathfinding.
    The astarpath.cs script can be found at Components–>Pathfinding–>Pathfinder
  • The second most important component is the 'Seeker.cs' component, a Seeker component should be attached to every GameObject which uses Pathfinding (e.g all AIs).
    The Seeker component handles path calls for one unit and post processes the paths. The Seeker isn't needed, but it makes Pathfinding easier.
  • Lastly there are the modifier scripts (e.g SimpleSmoothModifier.cs). Modifiers post-processes paths to smooth or simplify them, if a modifier is attached to the same GameObject as a Seeker it will post-process all paths that Seeker handles. See Using Modifiers.

New Scene

创建一个新的场景,名称是"PathfindingTest"。现在让我们创建一些能走会规避障碍物的AI , 添加一个Plane,将其放在场景原点 (0,0,0) 和将其缩放到10,10,10。

创建一个新的层 Edit->Project Settings->Tags 命名为"Ground",将Plane放在该图层。再创建一个大小不一的Cube置于Plane上,这些Cube将是 AI 应避免的障碍。将它们放在一个新的层命名为"Obstacles"。

你的场景现在应该看起来像这样:

Create a new scene, name it "PathfindingTest". Now let's create something which an AI could walk on and something for it to avoid: add a plane to the scene, place it in the scene origin (0,0,0) and scale it to 10,10,10.
Create a new layer (Edit->Project Settings->Tags) named "Ground" and place the plane in that layer. Now create some cubes of differerent scales and place them on the plane, these will be obstacles which the AI should avoid. Place them in a new layer named "Obstacles".
Your scene should now look something like this:

Adding A*

添加A* ,现在我们有站在平台上的AI与需要规避的障碍。所以现在我们添加A*Pathfinding 系统到场景中开启寻路

创建一个新的游戏对象,命名为"A *",将"AstarPath"组件添加到它身上 (Components–>Pathfinding–>Pathfinder)。

AstarPath 在监视面板中分为几个部分。两个最重要是Graphs和在底部的Scan按钮。

Graphs区域中保存了场景中所有的graphs(在本文中将译做图,其实就是网格),你可能至多有 16 个图表,但通常 1 - 2 个是不够用的。一个单一的图通常就是首选(通常我们只使用一类的图表)。

如果你打开Graphs区域你将看到你的图列表,您可以在列表中添加。我无法解释他们都在这里(为什么这列表在这儿),但两个主要的是在一个网格模式中生成节点网格图和Navmesh 图以网格为可行走区域。

扫描按钮是用于更新图,这也是在启动时 (除非高速缓存启动,更详细的请参照另一部分) ,一些图会自动改变设置但是不会引起卡帧

这有个刷新图的快捷方式 Cmd+Alt+S (mac) or Ctrl+Alt+S (windows).

在本教程中,我们将创建网格图然后添加, 请单击new Grid Graph 标签在监视面板上。

Now we have ground for an AI to stand on and obstacles for it to avoid. So now we are going to add the A* Pathfinding System to the scene to enable Pathfinding.
Create a new GameObject, name it "A*", add the "AstarPath" component to it (Components–>Pathfinding–>Pathfinder).
The AstarPath inspector is divided into several parts. The two most important is the Graphs area and the Scan button at the bottom.
The Graphs area holds all the graphs in your scene, you may have up to 16 but usually 1 or 2 will be sufficient. A single graph is usually to be preferred.
If you open the Graphs area by clicking on it you will see a list of graphs which you can add. I can't explain them all here but the two main ones is the Grid Graph which generates nodes in a grid pattern and the Navmesh Graph which takes a mesh as the walkable area.
The Scan button is for updating the graphs, this is also done on startup (unless the startup is cached, more about that in another part) and some graphs will do it automatically when changing the graph settings and the scanning won't cause any lag.
There is also a shortcut to use Cmd+Alt+S (mac) or Ctrl+Alt+S (windows).

For this tutorial we will create a Grid Graph, after adding it, click on the new Grid Graph label to bring up the graph inspector.

顾名思义,GridGraph 将生成网格节点,宽度 * 深度。网格可以放置在场景中的任何位置,你可以任意旋转。

节点大小变量确定网格中的正方形与节点有多大,在本教程中,您可以保留它 1,所以节点会间隔 1 个单位。

当前图的位置需要改变的话。点击image切换到左下角(相对为止),默认为中心,然后输入 (-50,-0.1-50)。-0.1 是避免浮点小数错误,在我们的场景中,地面的 Y = 0,如果图的 Y 也是0,当使用射线检测的时候,可能会出现令人讨厌的浮点小数错误(就像高度检查【高度检查用的是射线】)。

为使网格填充场景我们我们需要将变量改到合适的宽度和深度,在这个案例中我们将Depth与Width都设置为100。你可以看到在场景视图中的白色边框应为网格的位置正确。

As the name implies, the GridGraph will generate a grid of nodes, width*depth. A grid can be positioned anywhere in the scene and you can rotate it any way you want.
The Node Size variable determines how large a square/node in the grid is, for this tutorial you can leave it at 1, so the nodes will be spaced 1 unit apart.
The position needs to be changed though. Switch to bottom-left in the small selector to the right of the position field (currently named "Center"), then enter (-50,-0.1,-50). The -0.1 is to avoid floating point errors, in our scene the ground is at Y=0, if the graph was to have position Y=0 too, we might get annoying floating point errors when casting rays against it for example (like the height check does).
To make the grid fit our scene we need to change the width and depth variables, set both to 100 in this case. You can see that the grid is correctly positioned by the white bounding rectangle in the scene view which should now be enclosing the plane exactly.

Height Testing

高度测试,为了将节点置于正确的高度,A * 系统射出一条射线到场景中(一般是需地板)。这是高度测试的设置。

关于射线高度这一段我建议大家动手试一下

我们需改Mask,目前它包括Everything,但这将包括我们的障碍,以及和我们不需要检测的层。目前只需要包括Ground。

In order to place the nodes at their correct height, the A* system fires off a bunch of rays against the scene to see where they hit. That's the Height Testing settings.
A ray, optionally thick (as opposed to a line), is fired from [Ray Length] units above the grid downwards, a node is placed where it hits. If it doesn't hit anything, it is either made unwalkable if the Unwalkable When No Ground variable is toggled or the node is placed at Y=0 relative to the grid if it is set to false.
We need to change the mask used, currently it includes everything, but that would include our obstacles as well, and we don't want that. So set the Mask to only include the "Ground" layer which we created earlier.

Collision Testing

碰撞检测,当一个节点被放置,用来检查可通过性,这可以用一个球体,胶囊或射线。通常一个胶囊是用相同的直径和高度,作为人AI的控制器,在世界上行走,最好是有一定的冗余空间。
我们的AI将有标准的直径和高度,分别为1和2世界单位,但我们将设置的碰撞测试的直径和高度为2和2,以获得一定的冗余空间。
下一步,要使系统意识到我们放置的障碍,我们需要改变的碰撞测试的Mask,现在我们只设置Obstracles层作为碰撞检测层,因为我们不希望我们的地面被视为一个障碍。
现在一切都设置好了,扫描图把。
点击扫描~等一秒,你将获得一个生成的网格!(如果你做了所有的事情,那就是,把你的设置与下面的图像进行比较,顺便检验下图是否正确)

When a node has been placed, it is checked for walkability, this can be done with a Sphere, Capsule or a Ray. Usually a capsule is used with the same diameter and height as the AI character which is going to be walking around in the world, preferably with some margin though.
Our AI will have the standard diameter and height of 1 and 2 world units respectively, but we will set the diameter and height for the collision testing to 2 and 2 to get some margin.
Next, to make the system aware of the obstacles we placed, we need to change the mask for the Collision Testing, this time set it to contain only the "Obstacles" layer as we wouldn't want our ground to be treated as an obstacle.

Now everything should be set up correctly to scan the graph.
Press Scan. Wait a fraction of a second and you've got a generated grid! (if you have done everything correctly, that is, compare your settings to the image below, also check that Show Graphs is true)

Adding the AI

添加AI,没有一些东西来测试寻路?一点都不好玩,所以我们加一个AI玩玩。
创建一个胶囊,并添加第一人称控制器,也将它放置在Plane上可见的地方。
将Seeker组件添加到AI,这个脚本是从其他脚本中请求路径,它也可以处理修饰路径可以使路径更平滑或简化使用raycasts。
我们将写我们自己的,非常简单的脚本,用于移动的AI,所以跟着我打开你最喜欢的脚本编辑器。当然在项目中包含运动脚本,如果你不想写自己的脚本,你可以使用它。包含的脚本比我们在本教程中写的要牛B的多。包括脚本叫做AIPath和RichAI,的AIPath脚本可用于任何图,RichAI主要基于navmesh。

What is a pathfinding test without some moving stuff? Not fun at all, so let's add an AI to play around with.
Create a capsule and add the Character Controller component to it, also place it somewhere visible on the plane.
Add the Seeker component to the AI, this script is a helper script for calling requesting paths from other scripts, it can also handle path modifiers which can e.g smooth the path or simplify it using raycasts.
We are going to write our own, really simple script for moving the AI, so open your favourite script-editor and follow. There are also included movement scripts in the project which you can use if you don't want to write your own script. The included scripts are much more advanced than what we are going to write in this tutorial. The included scripts are called AIPath and RichAI, the AIPath script can be used on any graph while RichAI is primarily for navmesh based graphs.

Moving Stuff Around

下面是移动AI的演示代码

The call to the Seeker is really simple, three arguments, a start position, an end position and a callback function (must be in the form "void SomeFunction (Path p)"):

function StartPath (Vector3 start, Vector3 end, OnPathDelegate callback = null) : Path

So let's start our script with a simple snippet calling the pathfinder at startup:

using UnityEngine;
using System.Collections;
//Note this line, if it is left out, the script won't know that the class 'Path' exists and it will throw compiler errors
//This line should always be present at the top of scripts which use pathfinding
using Pathfinding;
public class AstarAI : MonoBehaviour {
public Vector3 targetPosition;
public void Start () {
//Get a reference to the Seeker component we added earlier
Seeker seeker = GetComponent<Seeker>();
//Start a new path to the targetPosition, return the result to the OnPathComplete function
seeker.StartPath (transform.position,targetPosition, OnPathComplete);
}
public void OnPathComplete (Path p) {
Debug.Log ("Yay, we got a path back. Did it have an error? "+p.error);
}
}

保存为AstarAI.cs文件并添加脚本到AI游戏对象。
在监视面板中,目标的位置为(- 20,0,22)。这是现在的AI需要尝试寻到的点。
点击播放。你可以得到Log,场景视图中有一根绿色线(Seeker组件试用Gizmos绘制的)。
如果你没有看到绿色的线,Seeker在监视面板中ShowGizmos被勾选。最近的Unity版本中,这绿线可能在地板下,禁用深度测试,场景视图窗口的上方点击Gizmos按钮和取消3D Gizmos复选框。
如果你报错了,确保Seeker组件是否附着在AIStar的GameObject上。如果仍然有错误,目标位置可能无法到达,换一个位置试试。

Save it to a file in your project named AstarAI.cs and add the script to the AI GameObject.
In the inspector, change Target Position to something like (-20,0,22). This is the position the AI will try to find a path to now.
Press Play. You should get the log message and also the path should show in the scene view as a green line (the Seeker component draws the latest path using Gizmos).
If you do not see a green line, make sure that the checkbox Show Gizmos on the Seeker component is checked. More recent Unity versions also depth-test gizmos, so it might be hidden under the ground, to disable the depth-testing click the Gizmos button above the scene view window and uncheck the 3D Gizmos checkbox.
In case you get an error, make sure that the Seeker component really is attached to the same GameObject as the AstarAI script. If you still get an error, the target position might not be reachable, try to change it a bit.

It looks a bit edgy, but that will do for now as you might be waiting for an explanation of what that code really did.

第一个脚本调用Seeker的startpath功能,Seeker将创建新的路径实例然后把它转发给astarpath脚本(在你添加Pathfinder组件之前)。AstarPath脚本将把路径置于列表中,只要可能,脚本将通过搜索网格,直到找到最终节点的路径。
在本教程中的搜索步骤是非常好的解释。它并不完全和教程中一致,但概念是相同的。
一旦计算出路径,该路径返回给Seeker,然后处理,如果附加过任何修饰组件,然后Seeker将调用在调用中指定的回调函数。回调也送到seeker.pathcallback,如果你不想特殊处理回调函数你可以注册这个回调

What happens is that first the script calls the Seeker's StartPath function. The seeker will then create a new Path instance and then sent it forward to the AstarPath script (the Pathfinder component you added before). The AstarPath script will put the path in a queue. As soon as possible, the script will then process the path by searching the grid, node by node until the end node is found.
The searching step is explained really well in this tutorial here. It does not work exactly like in the tutorial, but the concept is the same.
Once calculated, the path is returned to the Seeker which will post process it if any modifiers are attached and then the Seeker will call the callback function specified in the call. The callback is also sent to Seeker.pathCallback which you can register to if you don't want to specify a callback every time you call StartPath:

//OnPathComplete will be called every time a path is returned to this seeker 

seeker.pathCallback += OnPathComplete; 

//So now we can omit the callback parameter 

seeker.StartPath (transform.position,targetPosition);
Note
When removing or destroying the script, callback references are not removed, so it is good practise to add a removing snipped in case that should happen
public void OnDisable () { 

seeker.pathCallback -= OnPathComplete; 

}

当我们得到计算后的路径,我们如何从中获得信息?

一个路径实例包含与此相关的两个列表。

Path.vectorPath 是一个 Vector3 列表,其中包含的路径,如果使用任何平滑法都将修改此列表,它是获取路径的推荐的方式。

其次还有 Path.path 列表中的 GraphNode 元素的列表,它包含的所有节点路径访问,可以用来获取额外遍历路径上的信息。

首先,你应经常检查 path.error,如果真,出于某种原因失败的路径。Path.errorLog 将有更多的错误信息。

为了扩展我们的 AI 脚本,让我们添加一些运动脚本: 也添加第一人称控制器到AI的GameObject上。

When we get the calculatated path back, how can we get info from it?
A Path instance contains two lists related to that.
Path.vectorPath is a Vector3 list which holds the path, this list will be modified if any smoothing is used, it is the recommended way to get a path.
secondly there is the Path.path list which is a list of GraphNode elements, it holds all the nodes the path visisted which can be useful to get additonal info on the traversed path.
First though, you should always check path.error, if that is true, the path has failed for some reason. Path.errorLog will have more info on what went wrong in case path.error is true.
To expand our AI script, let's add some movement: Also add the CharacterController to the AI gameObject.

 

using UnityEngine;

using System.Collections;

//Note this line, if it is left out, the script won't know that the class 'Path' exists and it will throw compiler errors

//This line should always be present at the top of scripts which use pathfinding

using Pathfinding;

public class AstarAI : MonoBehaviour {

//The point to move to

public Vector3 targetPosition;

private Seeker seeker;

private CharacterController controller;

//The calculated path

public Path path;

//The AI's speed per second

public float speed = 100;

//The max distance from the AI to a waypoint for it to continue to the next waypoint

public float nextWaypointDistance = 3;

//The waypoint we are currently moving towards

private int currentWaypoint = 0;

public void Start () {

seeker = GetComponent<Seeker>();

controller = GetComponent<CharacterController>();

//Start a new path to the targetPosition, return the result to the OnPathComplete function

seeker.StartPath (transform.position,targetPosition, OnPathComplete);

}

public void OnPathComplete (Path p) {

Debug.Log ("Yay, we got a path back. Did it have an error? "+p.error);

if (!p.error) {

path = p;

//Reset the waypoint counter

currentWaypoint = 0;

}

}

public void Update () {

if (path == null) {

//We have no path to move after yet

return;

}

if (currentWaypoint >= path.vectorPath.Count) {

Debug.Log ("End Of Path Reached");

return;

}

//Direction to the next waypoint

Vector3 dir = (path.vectorPath[currentWaypoint]-transform.position).normalized;

dir *= speed * Time.deltaTime;

controller.SimpleMove (dir);

//Check if we are close enough to the next waypoint

//If we are, proceed to follow the next waypoint

if (Vector3.Distance (transform.position,path.vectorPath[currentWaypoint]) < nextWaypointDistance) {

currentWaypoint++;

return;

}

}

}

 

这段代码的功能是,在FixedUpdate得到归一化方向朝下一个关键点,向它靠近一点,然后检查它是否足够接近,继续下一个点,在这个例子中,通过简单的递增currentwaypoint的下标。
AI将在距离终点很短的位置暂停,这是因为我们没有在最后一个点做特殊的逻辑。

If you press play now, the AI will follow the calculated path, neat, eh?
What the code does is to, in FixedUpdate get the normalized direction towards the next waypoint, move towards it a bit, then it checks if it is close enough to continue to the next waypoint, in this example that is done by simply by incrementing the currentWaypoint index.
The AI will stop a short distance from the end point, but that's just because we haven't got any special logic for the last point.

Smoothing the Path

平滑路径
现在你已经学会了如何建立一个简单的网格图和如何调用寻路,但肯定会有办法让这些路径看起来平滑一点?
当然是。路径平滑和简化脚本叫做路径修饰脚本,脚本可以被添加到相同的对象作为Seeker。
最直接的一个简单的光滑脚本可以在Components–>Pathfinding–>Modifiers–>Simple Smooth。添加到我们的AI。
这个修改是要做的,是把路径多次直到每一段变得小于最大段长度可变。然后,它会平滑的路径移动的点更接近彼此。修改器有很多设置,我不会在这里通过所有的设置。更多关于每个变量的信息具体看simplesmoothmodifier文档。对于这个教程,你可以设置最大段长度,say 1. Iterations =5 与Strength =0.25。使用不同的数值试验下。
现在点击播放,这条路径应该看起来更平滑,正如我们所希望的那样。
注意
平滑一般不考虑世界的几何或图,所以要小心运用,太多的平滑可能导致路径走不通。

Now you have learned how to set up a simple grid graph and how to call the Pathfinding, but surely there must be a way to get those paths to look a bit smoother?
Sure it is. Path smoothing and simplification scripts are called Path Modifiers and are scripts which can be added to the same GameObject as a Seeker.
The most straight forward one is the Simple Smooth modifier which can be found at Components–>Pathfinding–>Modifiers–>Simple Smooth. Add that to our AI.

What this modifier is going to do, is to subdivide the path a number of times until each segment becomes smaller than the Max Segment Length variable. Then it will smooth the path by moving the points closer to each other. The modifier has a number of settings, I won't go through all of them here. See the SimpleSmoothModifier documentation for more info about each variable. For this tutorial you can set Max Segment Length to, say 1. Iterations to 5 and Strength to 0.25. Experiment with it to get good values.

Now press play again, the path should look much smoother, just as we wanted.

Note
Smoothers don't usually take world geometry or the graph into account, so be careful with applying too much smoothing since that could cause paths to pass through unwalkable areas.

另一种很棒的修饰符是 FunnelModifier,它将大量简化路径。如果您添加它,请确保它顶部,或略低于 StartEnd 修饰符,因为它优先要求原始路径数据操作。阅读更多关于修饰符在这里Using Modifiers

Another good modifier to use is the FunnelModifier which will simplify the path a great deal. If you add it, make sure it has the top, or just below the StartEnd modifier, priority since it requires original path data to work. Read more about modifiers here Using Modifiers You can see a list of all modifiers if you click on Class Collections above and go to "Modifiers".

The End

没啥好说的,下一章介绍导航图 nvamesh,祝君好运~

That was the end of the Get Started tutorial part 1. I hope you learned something from it.
From here on you can explore the rest of the documentation or dig straight in to the project.
If you want a little better AI, you can use the AIPath script which is included in the project.

You can continue with the next part of the get started tutorial, where we will use navmesh graphs: Get Started Part 2

Look in the Related Pages tab, there you will find a number of tutorials and how-tos for how to use the package.

Good Luck!

    posted @ 2015-09-06 00:45 keyle_xiao 阅读(...) 评论(...) 编辑 收藏