Unity里面的自动寻路(一)

来源:互联网 发布:图书管理系统c语言代码 编辑:程序博客网 时间:2024/04/30 06:44
众所周知,自动寻路是所有游戏的一个难点,属于AI(人工智能)的范畴。一个游戏的AI的设计是否足够完美,可能决定了这个游戏的命运。然而自动寻路就是AI中的一个十分重要的分支,其算法异常复杂。然而unity3d中提供了一套非常成熟的组件来为我们解决这一难题。今天,我们就来一起欣赏一下Unity3d自带的自动寻路系统。

我们在学习一个陌生的知识时,免不了要记一些令人烦恼的概念,自动寻路也是一样的。但是记东西也是有技巧的,我认为我们应该先看到一点惊喜再学东西,这样兴趣有了,学东西就不那么抵触了。所以我现在先列举举一个简单的例子,记住,先不要思考为什么我要这样做,如果你是初次学习这个自动寻路的话最好这样,不然你会走很多弯路的。我们需要新建一个工程,将其起名为:NavMeshProject。然后我们可以制作场景了。

像往常一样,先做一个地面,这里我用的是Cube。然后我将我们的朋友Robot也请来了,看这:

78dboneae7-3aone7-44cone-8b26-d27504d20cef.jpg 

是不是很帅啊!我特意加了个灯光,给地面换了个柔和的颜色,看起来就不那么单调了。然后我们保存一下整个场景,该场景取名为:TestNavgation1。
下面我来烘焙场景,但是记住,先不要想为什么。Unity3d编辑器的菜单下:Window->Navigation,这是我们可以发现编辑器的某一部分出现了一个Navigation窗口,如:
07634459-0fa4-4ce0-87onea-b5oneede53f8done.jpg 

请注意一下此图的右下角的一个Bake按钮。
在Hierarchy下选中Plane(就是那个地面),然后我们在Navigation面板中的Object选项卡下找到Navigation Static复选框,勾选它,然后点击Navigation面板右下角的Bake按钮:
3299dac8-85ea-4933-a777-5fbf357b525f.jpg 

此时我们可以发现Project面板下面多出了一个文件夹,且此文件夹出现了一个子文件NavMesh: 

8385280b-96c8-425b-95one9-e2a448d32906.jpg 

并且Scene窗口中的地面上出现了一下变化: 

b4736d3f-ad0f-4f60-bc26-8574one84f4886.jpg 

然后我们跟robot添加一个组件:NavMeshAgent。具体做法是 :  在Hierarchy面板下选中robot,然后在Unity3d菜单下:Component->Navigation->NavMeshAgent,你会发现robot身上出现了一个类似胶囊体碰撞器的绿色线框的包围体,调节一下盖NavMeshAgent组件中的一些参数:Height,BaseOffset等,如图: 

f3one4f23b-one0one7-4bcd-8b2c-bc0f9bd04c85.jpgd885ce2b-2efe-4257-92a8-bad0deb8bc7f.jpg 

最后我们该编写脚本了。我还是新建一个文件夹:Scripts。然后编写两个脚本:
脚本一,专门设置导航网格代理的目的地的:

  1. using UnityEngine;
  2. using System.Collections;

  3. public class NavMeshMove : MonoBehaviour {

  4.     public Transform[] NavMeshTransforms;//导航网格的目的地组。

  5.     private NavMeshAgent nma;//Robot的导航网格代理

  6. void Start () {
  7.         if(NavMeshTransforms == null)
  8.         {
  9.             return;
  10.         }
  11.     nma = gameObject.GetComponent();
  12.             nma.SetDestination(NavMeshTransforms[0].position);//初始时刻设置的导航网格代理的目的地
  13. }

  14. void Update () {

  15.         if(nma.remainingDistance == 0){
  16.             //当导航网格代理到达了目的地时,更换目的地,且是随机的更换
  17.             nma.SetDestination(NavMeshTransforms[Random.Range(0,NavMeshTransforms.Length)].position);
  18.         }
  19.    }
  20. }
复制代码


脚本二,是专门为导航网格代理编写的动画控制脚本:

  1. using UnityEngine;
  2. using System.Collections;

  3. public class NavNeshAnimation : MonoBehaviour {

  4.     public float***nAnimaitonSpeed = 4.0f;//定义最大的跑步速度,一般为导航网格代理的速度
  5.     public float speedThreshold = 0.1f;//定义动画从idle***n过渡的临界速度

  6.     private string loadAnimation = "load_Idle";//定义需要执行的协同函数名
  7.     private NavMeshAgent nma;//定义导航网格代理

  8. IEnumerator Start () {
  9.     nma = gameObject.GetComponent();
  10.         AnimationSetup();//简单的设定一下动画。

  11.         while(Application.isPlaying) {
  12.             yield return StartCoroutine(loadAnimation);//执行协同函数
  13.     }
  14.     }

  15.     IEnumerator load_Idle() 
  16.     {
  17.         do{
  18.             UpdateAnimationBlend();
  19.             yield return null;
  20.             
  21.         }while(nma.remainingDistance == 0);

  22.         loadAnimation = "load_Run";
  23.         yield return null;
  24.     }

  25.     IEnumerator load_Run()
  26.     {
  27.         do
  28.         {
  29.             UpdateAnimationBlend();
  30.             yield return null;

  31.         } while (nma.remainingDistance != 0);

  32.         loadAnimation = "load_Idle";
  33.         yield return null;

  34.     }

  35.     void AnimationSetup() 
  36.     {
  37.         animation["idle"].layer = 1 ;
  38.         animation["***n"].layer = 1;
  39.         animation.SyncLayer(1);

  40.         animation.CrossFade("idle", 0.1f, PlayMode.StopAll);
  41.     }
  42.     
  43.     void UpdateAnimationBlend() 
  44.     {
  45.         Vector3 velocityXZ = new Vector3(nma.velocity.x,0.0f,nma.velocity.z);
  46.         float speed = velocityXZ.magnitude;
  47.         animation["***n"].speed = speed /***nAnimaitonSpeed;

  48.         if (speed > speedThreshold)
  49.         {
  50.             animation.CrossFade("***n");
  51.         }
  52.         else {
  53.             animation.CrossFade("idle");
  54.         }
  55.     
  56.     }
  57. }
复制代码

我们将这两个脚本绑定到robot身上,然后我们在Hierarchy面板中新建一个空的GameObject,重命名为:NavMeshPoint,并且新建5个spere作为它的子对象被其管理,如下:
efd40af9-6a37-4one53-b3bone-e5b55a0a7d07.jpg 



然后将这5个spere分开到Plane上的不同位置,如下: 

3a04one92a-923d-4be7-92cone-6e3eonedfaonede0.jpg 

选中robot,然后在Inspector下的NavMeshMove脚本上设置NavMeshTransforms的个数为5,并且将那5个spere分别拖拽到相应的位置,如下: 

edf0e68e-5one9one-4f09-a5d3-604b3oneab9b3d.jpg 

好了,我们可以运行一下工程,看一下效果,这里只截了部分图片: 

9c20oneebf-a8c0-425e-8e30-7cb89b6oneb8fe.jpg 

0dfb2bc4-b3onef-4e55-bdbone-4f072faf44dc.jpg 

325bab24-e3dd-4848-83bf-a57af2one524fb.jpg 


从图中我们可以看出,我们的robot在不断的跑步,每次跑到目标点之后停下来,然后转身朝向下一个目标点,如果工程没有停止,那么它周而复始的循环下去,这对于编写NPC的AI很有帮助。
是不是很神奇呢?有没有想要将这部分知识融会贯通的冲动?我把工程一起献上吧。这个星期我的连续几篇文章,会深入的介绍这一系列的知识。敬请期待,不过我建议一下读者实现最好了解一些这个组件,看看与我的理解有什么不同,我们一同探讨,共同切磋。
0 0