纳金网

标题: 利用Unity3d中的自动寻路来为角色设置目的地 [打印本页]

作者: wyb314    时间: 2012-11-16 10:02
标题: 利用Unity3d中的自动寻路来为角色设置目的地
         不少游戏中,你只需在屏幕上单击一下,角色就可以向那个地方奔跑,然后到达目的地停了下来 ,Dota里面就是这样的 。今天,就让我们来实现这一功能吧。我们这次是借助Unity3d里面的自动寻路组件来实现这一功能,所以对此组件不熟悉的读者我希望您事先看一下这方面的资料,不会花你多长时间的。好了,让我们开始吧!

        首先,我们得有一个工作目录,所以我们得新建一个工程,我把它命名为:TestNavMeshAgent,然后保存当前的Scene,给它一个名字:RobotNavMesh。接下来我们开始搭建我们的场景了。因为我不会做模型,所以像往常一样,我从别的工程里面抠出了一个角色:Robot,还是官方的CharacterAnimation这个工程里的一个机器人模型。我们得新建一个平面(用Cube做的)作为Robot落脚的地方,还是给这个平面一个颜色吧,并且加一个灯光吧。调节一下摄像机的位置与旋转角到一个比较合适的程度,就像下面这样:



我们接下来该烘焙平面,生成导航网格了。如果读者对此部分不是很熟悉,建议认真研究一下,也没什么难度,如果不熟悉英文,就到Unity圣典上看中文算了。
        好了,我们开始烘焙场景了。选中我们的地面,然后打开Navigation窗口:Window->Navigation。勾选Navigation Static 这个选项,并且打开Navigation Layer下拉菜单,然后选择Default,最后单击Navigation窗口的右下角的Bake按钮。我们可以发现,Scene中的地面颜色发生了改变,如:

这就是导航网格生成后的场景。然后我们往Robot身上添加一个组件:NavMeshAgent,即:Unity菜单栏:Component->Navigation->Nav Mesh Agent。这个组件就是寻路系统的核心组件,我们接下来将利用这个组件来完成Robot的移动了。接下来我们必须写几个脚本,我写了3个脚本:RayCastPerFrame.cs,    NavMeshController.cs,       RobotNavAnimaiton.cs。RayCastPerFrame.cs的代码如下:


using UnityEngine;
using System.Collections;

public class RayCastPerFrame : MonoBehaviour {//这个脚本将要被附着到Robot身上

    private RaycastHit hitInfo ;
   
    void Update () {
        if(Input.GetMouseButtonDown(1)){//当我们点击鼠标右键时,
             Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition),out hitInfo);
            //以摄像机为原点,向鼠标光标世界坐标点发射射线投射碰撞
         }
     }

    public RaycastHit GetHitInfo()
    {
        return hitInfo;//得到碰撞信息
    }
}

这个脚本比较简单,是专门获取射线碰撞信息hitInfo的。

NavMeshController.cs的代码如下:

using UnityEngine;
using System.Collections;

public class NavMeshController : MonoBehaviour {//此脚本将附着到Robot上

    public RayCastPerFrame rcpf;//待会会将Robot拖拽到这个变量上,为的是直接访问Robot身上的RayCastFrame脚本。

    private NavMeshAgent nma;//用于存储Robot身上附带的NavMeshAgent组件

void Start () {
    nma = gameObject.GetComponent<NavMeshAgent>();
            //实例化nma
}

void Update () {


        if(!rcpf){
            return;
        }



        RaycastHit hit = rcpf.GetHitInfo();//获取碰撞点

        if (hit.transform)
        {
            nma.SetDestination(hit.point);//设置目的地位射线与平面的碰撞点
        }


}
}


RobotNavAnimaiton.cs的代码如下:

using UnityEngine;
using System.Collections;

public class RobotNavAnimation : MonoBehaviour {//此脚本呢附着在Robot身上

    private NavMeshAgent nma;//用于存储Robot身上附带的NavMeshAgent组件
    void Start()
    {
        nma = gameObject.GetComponent<NavMeshAgent>();//实例化nma
        animation.AddClip(animation["shoot"].clip, "shootUpperBody");
        animation["shootUpperBody"].AddMixingTransform(transform.Find("mover/gun"));
        animation["shootUpperBody"].AddMixingTransform(transform.Find("mover/roothandle/spine1"));

        animation.wrapMode = WrapMode.Loop;
        
        animation["jump"].wrapMode = WrapMode.Clamp;
        animation["shoot"].wrapMode = WrapMode.Clamp;
        animation["shootUpperBody"].wrapMode = WrapMode.Clamp;

        animation["idle"].layer = -1;
        animation["***n"].layer = -1;
        animation.Stop();
    }

void Update () {

        if (nma.remainingDistance != 0)//如果导航代理还没有到达目的地,则播放跑的动画。
        {
            animation.CrossFade("***n");
            //animation["***n"].speed = Mathf.Sign(Input.GetAxis("Vertical"));
        }
        else //否则播放站立的动画
        {
            animation.CrossFade("idle");
        }
        if (Input.GetButtonDown("Jump"))
        {
            animation.CrossFade("jump", 0.3f);
        }

        if (Input.GetButtonDown("Fire1"))
        {
            if (animation["***n"].weight > 0.5f)
                animation.CrossFadeQueued("shootUpperBody", 0.3f, QueueMode.PlayNow);
            else
                animation.CrossFadeQueued("shoot", 0.3f, QueueMode.PlayNow);
        }

}
}


这个脚本涉及到了动画的混合,现在就先将就着看吧,我或许会在接下来的日子里写出我的第四篇有关与动画方面的文章,有兴趣的可以留意一下。
        最后,我们将这三个脚本绑定在Robot上面,并且在Hierarchy面板中将Robot拖拽到NavMeshController这个脚本中的Rcpf这个选项上,如下图:




寻路时的状态,已经达到了我们想要的效果。



我们还可以家一个障碍物,这个就留给读者自己添加吧。可是你有没有注意到一点,RayCastPerFrame .cs这个射线检测的脚本是每帧都在检测,NavMeshController.cs也是每帧都在获取碰撞信息,每帧设定目的地。这个就相当消耗性能。那么这个问题就留给读者自行解决吧!我提个思路,比如说当距离上次检测碰撞信息的时间超过0.1s时设置导航网格代理的目的地,这个应该是很好实现的。
我把工程给贡献出来,有兴趣的读者不妨下载下来研究一下。

TestNevMeshAgent.rar

0 Bytes, 下载次数: 253


作者: 王者再临    时间: 2012-11-16 15:56
寻路系统很少有这方面的介绍,谢谢了!!
作者: 所罗门封印    时间: 2012-11-16 23:23
记得貌似有个啥专业A星寻路的插件。
作者: 其实我是神    时间: 2012-11-17 09:52
好东西好东西!好东西好东西!
作者: 比巴卜    时间: 2012-11-21 09:25
来围观!!!
作者: 冰点    时间: 2012-11-23 16:05
wed 3d 纳金网
作者: 王者再临    时间: 2012-11-24 02:15
学习中!
作者: may    时间: 2012-11-24 03:01
  lZ的回复很不错,支持!

作者: lhg22254    时间: 2012-12-21 11:52
认真学习中                        

作者: may    时间: 2012-12-24 05:25
来支持一下楼主的帖子哦
作者: 王者再临    时间: 2012-12-27 19:33
学习了,虽然还是有难度,谢谢楼主的用心




欢迎光临 纳金网 (http://rs.narkii.com/club/) Powered by Discuz! X2.5