[Unity插件]A* Pathfinding Project:简易教程(一)

原文链接:http://arongranberg.com/astar/docs/getstarted.php

插件下载地址:http://pan.baidu.com/s/1eROqaB4

题外话:最近想学习一下A*插件,由于在网上没有发现什么比较详细的教程,所以就只能上官网了。这是第一次看这么长的英语文章,翻译得不好,请见谅!

 

概述:

A*插件的核心脚本就是”astarpath.cs”,所以如果你想使用A*插件进行寻路,那么场景中一定要有一个”astarpath.cs”(并且一个场景仅有一个)。可以通过Component->Pathfinding->Pathfinder添加”astarpath.cs”。

第二重要的脚本就是”seeker.cs”,每一个需要寻路的物体最好要有这个脚本(不是一定要添加,但是有了可以让寻路更加简单)。

然后就是一个名叫"SimpleSmoothModifier.cs"的脚本,它可以让路径更加圆滑、简单,它要跟"seeker"脚本一起添加到同一个物体上。

 

1.创建一个新场景,创建一个plane,位置(0,0,0),缩放(10,10,10)。创建一个新层(edit->project settings->tags and layers),命名为”Ground”,把plane设为”Ground”,创建一些cube作为障碍物,把这些cube设为新的层”Obstacles”。

 

2.创建一个新GameObject,命名为A*,添加"AstarPath"脚本。脚本中最重要的是两个东西是“Graphs”和下方的“Scan”按钮。“Graphs”包含了所有的寻路图,最多可以有16个,但是一般1到2个已经足够了。其中主要的两个是”Grid Graph”(以格子的方式生成节点)和”Navmesh”(在可行走的区域生成mesh)。“Scan”按钮用来更新寻路图。

 

3.点击”Grid Graph”,Grid graph会产生一系列的格子,大小为width * height,这个网格可以放在场景中的任何地方,也可以进行旋转。“Node Size”可以设置格子的大小,这里先设为1。注意有一个由5个点组成的正方形图标,点击它的左下方,此时图标那一行前面的文字会变为”Bottom-Left”,设为(-50,-0.1,-50)。其中y方向设置为-0.1是为了避免产生浮点错误,因为地面plane的y向坐标是0,如果寻路图也是y向坐标也为0的话,在进行高度检测的raycast的时候,会产生问题。所以确保寻路图的y要略小于地面的y。为了网格可以适应我们的场景,调整Width和Depth,均为100。(在实际设置中好像有点问题,总之就是让网格跟plane大小适应就是了,可以通过"Scan"进行刷新)

 

高度检测

4.为了把寻路的node放置到场景中的正确高度,一般向下发射一束射线来进行检测,寻路node会被放置到碰撞点的位置。我们将mask设置为Ground,因为只希望寻路节点与Ground进行检测。如果射线没有发生碰撞,说明检测的物体没有设置为“Ground”,又或者上面的第三点中y设为了0。

 

 

碰撞检测

5.当node被放置好后,它就会被用来检测是否可行走,一般可以使用sphere,capsule或ray来进行碰撞检测(这里指的是用Physics.CapsuleCast或者Physics.SphereCast模拟AI对象)。Capsule会使用和AI对象一样的半径和高度来进行碰撞。为了让AI对象和障碍物有一些边缘,这里将Capsule的半径设置为2.另外将碰撞检测的layer设置为Obstacles,因为不想让地面成为障碍。点击底部的Scan,我们就可以看到grid Graph的生成了。

 

 

添加AI

6.创建一个capsule,添加”Character Controller”部件,放到plane上。添加”Seeker”部件,Seeker脚本是一个帮助类的脚本,用来将其他脚本的寻路请求进行处理,它也可以处理Path modifier(一般是对寻路结果进行圆滑处理的脚本)。接下来将会写一个脚本用于寻路,当然你也可以用插件内置的脚本。A* 插件自带了两个AI脚本用于挂接到对象上进行寻路:AIPah可适用于任何类型的寻路图;而RichAI主要适用于NavMesh类型。

 

让物体寻路

7.seek脚本中有一个重要的方法:function StartPath (Vector3 start, Vector3 end, OnPathDelegate callback = null) : Path。参数是开始位置,结束位置,回调函数。其中OnPathDelegate是一个委托,被调用的函数类似于"void SomeFunction (Path p)"

脚本如下:

 

 
  1. using UnityEngine;

  2. using System.Collections;

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

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

  5. using Pathfinding;

  6. public class AstarAI : MonoBehaviour

  7. {

  8. public Transform target;

  9. public void Start()

  10. {

  11. //Get a reference to the Seeker component we added earlier

  12. Seeker seeker = GetComponent<Seeker>();

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

  15. seeker.StartPath(transform.position, target.position, OnPathComplete);

  16. }

  17.  
  18. public void OnPathComplete(Path p)

  19. {

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

  21. }

  22. }

把这个脚本添加到要寻路的物体上,给target赋值,点击"Play"后,你应该会看到一条绿色的线,这就是寻路的路径了,此时物体还不会动。

如果没有看到绿线,检查一下"Seek"的脚本的"Show Gizmos"有无勾上。又或者可能是unity版本的原因,使Gizmos画出来的线被隐藏在plane下面了。

 

可以看到画出来的线很不圆滑,后面会进行美化的。

设置回调的方式也可以是这样:

 

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

  2. seeker.pathCallback += OnPathComplete;

  3. //So now we can omit the callback parameter

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

 

 

注意上面的方法添加回调后,当你要移除或者摧毁该脚本,要:

 

 
  1. public void OnDisable () {

  2. seeker.pathCallback -= OnPathComplete;

  3. }


回调后我们可以通过Path p得到计算后的路径,我们可以用它做什么呢?

 

计算后的路径有两个重要的list。

1.Path.vectorPath是一个Vector3的list,保存着每一个路径点的位置。

2.Path.path是一个node的list,保存着每一个路径点的node。

 

然后拓展一下上面的脚本:

 

 
  1. using UnityEngine;

  2. using System.Collections;

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

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

  5. using Pathfinding;

  6. public class AstarAI : MonoBehaviour

  7. {

  8. //The point to move to

  9. public Transform target;

  10.  
  11. private Seeker seeker;

  12. private CharacterController controller;

  13.  
  14. //The calculated path

  15. public Path path;

  16.  
  17. //The AI's speed per second

  18. public float speed = 100;

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

  21. public float nextWaypointDistance = 3;

  22.  
  23. //The waypoint we are currently moving towards

  24. private int currentWaypoint = 0;

  25.  
  26. public void Start()

  27. {

  28. seeker = GetComponent<Seeker>();

  29. controller = GetComponent<CharacterController>();

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

  32. seeker.StartPath(transform.position, target.position, OnPathComplete);

  33. }

  34.  
  35. public void OnPathComplete(Path p)

  36. {

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

  38. if (!p.error)

  39. {

  40. path = p;

  41. //Reset the waypoint counter

  42. currentWaypoint = 0;

  43. }

  44. }

  45.  
  46. public void FixedUpdate()

  47. {

  48. if (path == null)

  49. {

  50. //We have no path to move after yet

  51. return;

  52. }

  53.  
  54. if (currentWaypoint >= path.vectorPath.Count)

  55. {

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

  57. return;

  58. }

  59.  
  60. //Direction to the next waypoint

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

  62. dir *= speed * Time.fixedDeltaTime;

  63. controller.SimpleMove(dir);

  64.  
  65. //Check if we are close enough to the next waypoint

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

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

  68. {

  69. currentWaypoint++;

  70. return;

  71. }

  72. }

  73. }

 

然后运行游戏,物体就会移动了。注意路径点不是连续的,当物体离下一个路径点小于nextWaypointDistance时才会继续前进。最后物体会停在离目标前的一段距离,这是因为对于终点,我们需要进行特殊处理,但脚本还没有进行处理。

 

路径平滑

8.这时要用到内置的脚本"Path Modifiers",要加在有"seeker"脚本的物体上。(不过好像找不到这个名字的脚本,也可以通过Components–>Pathfinding–>Modifiers–>Simple Smooth添加脚本)。平滑的原理就是将一段段的小路径继续细分,让路径点之间的距离缩小。这里先将Max Segment Length设为1,Iterations设为5,Strength设为0.25。运行之后会发现绿线更加平滑了。

 

 

最后:因为使用上面的脚本进行寻路并不完美,而且每一次都要写脚本的话会很麻烦,所以直接为要寻路的物体添加"AIPath"脚本,赋值target,效果会更好!上面的脚本就当理解一下内部就好了!

  • 点赞4
  • 评论1
  • 分享
  • 收藏4
  • 手机看
  • 文章举报

收起全文

⎝⎝⎝MaximilianLiu⎠⎠⎠的博客

 1503

寻路插件APathfinding Project_Pro的系统学习(二)_AI Path组件_Seeker组件_寻路功能

寻路插件APathfinding Project_Pro的系统学习(二)_AI Path组件_Seeker组件_寻路功能首先,创建一个可供移动对象,并且添加AI Path组件:移除Cylinder自带的碰撞体或者忽略该碰撞器的层,避免炸车:碰撞器移除后状态如下,你可以发现AI Path组件自带一个竖着的光线投射:然后我们开始移动,观察Scene和Game...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值