将Behavior插件和AStarPathFinding插件结合

来源:互联网 发布:素质测评软件 编辑:程序博客网 时间:2024/06/02 01:59

在上一篇博文中,简单写了一个A*插件的简单移动和动态生成网格,可以看到,在那里,有一个StarPath方法一直没有使用,那是因为我故意留给行为树这里用的哈哈,接下来把代码改一改,把没必要的代码注释掉,我们把这个脚本封装起来,以后只提供给其他脚本实现寻路。AStar.cs 脚本代码如下:

using System.Collections;using System.Collections.Generic;using UnityEngine;using Pathfinding;public class AStar : MonoBehaviour {  //  public Transform target;    private Seeker seeker;    private CharacterController characterController;    public Path path;    private float speed = 100;    private float angularSpeed = 10;    private float nextWaypointDistance = 1.5f;    private int currentWaypoint = 0;    void Start () {    }    public void StarPath(Transform target)    {        seeker = GetComponent<Seeker>();        characterController = GetComponent<CharacterController>();        seeker.pathCallback += OnPathComplete; //寻路的一个回调        seeker.StartPath(transform.position, target.position, OnPathComplete);    }    private void FixedUpdate()    {        Move();    }    public void Move()    {        if (path == null)   //如果路径为空,直接返回        {            return;        }        if (currentWaypoint >= path.vectorPath.Count)        {            return;        }        if (characterController != null && characterController.enabled == true)        {            Vector3 dir = (path.vectorPath[currentWaypoint] - transform.position).normalized;            dir *= speed * Time.deltaTime;            characterController.SimpleMove(dir);            Quaternion targetRotation = Quaternion.LookRotation(dir);            transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.fixedDeltaTime * angularSpeed);            if (Vector3.Distance(transform.position, path.vectorPath[currentWaypoint]) < nextWaypointDistance)            {                currentWaypoint++;                return;            }        }    }    public void OnPathComplete(Path p)    {        if (!p.error)        {            path = p;   //如果不发生错误,把p传给path            currentWaypoint = 0;        }    }    private void OnDisable()    {        seeker.pathCallback -= OnPathComplete;    }    //提供get set方法供其他脚本使用    #region GetSet          public float Speed    {        get { return speed; }        set { speed = value; }    }    public float AngularSpeed    {        get { return angularSpeed; }        set { angularSpeed = value; }    }    public float ArriveDistance    {        get { return nextWaypointDistance; }        set { nextWaypointDistance = value; }    }    #endregion}

一定要记得加头文件!一定要记得加头文件!一定要记得加头文件!一定要记得加头文件!一定要记得加头文件!

这部分代码仅供寻路移动使用。接下来,我们开始准备行为树的部分。首先导入BehaviorDesigner.1.5.7.unitypackage和Behavior Designer - Movement Pack v1.5.2.unitypackage。这两个包,前一个是基础包,后一个是行为树移动的包。大家可以去官网下载,如果只是学习使用,也可以去网上找一下资源包。导入后,选择我们的角色,点击Unity上面的Tools->Behavior Designer->Editor。
这里写图片描述

然后会出现一个灰色面板,我们鼠标右键Add Behavior Tree或者点击上面的那个+号添加我们的行为树。(这里只简单实现A*和行为树的结合,不再对行为树进行详细的讲解,网上也有很多大神详细的讲了,有兴趣可以自行百度。)。添加后,我们来自己写一个巡逻过程。我们先看看自带的巡逻是怎么样的。

这里写图片描述

先添加上巡逻任务,然后单击这个任务图标,可以看到巡逻的一系列信息。图片如下:
这里写图片描述

Speed是巡逻的速度,然后是旋转速度,以及距离目标点多远算抵达。后面的Stop On Task End如果选中了,则会在巡逻后停止。Waypoint Pause Duration则表示到达一个目标点之后停留多久。 Waypoints这里可以设置巡逻点个数,以及巡逻点的位置。我们参照自带的自己写一个巡逻MyPatrol.cs

using System.Collections;using System.Collections.Generic;using UnityEngine;using BehaviorDesigner.Runtime.Tasks;using BehaviorDesigner.Runtime;using Pathfinding;public class MyPatrol : Action {    public SharedFloat speed;   //行动的速度。    public SharedFloat angularSpeed;    //旋转速度    public SharedFloat arriveDistance;  //多远算到达目的地    public SharedBool stopOnTaskEnd;    //需不需要在巡逻完所有的点后停止    public SharedFloat waypointPauseDuration;   //巡逻到目标点后停留的间隙    public SharedGameObjectList waypoints;    private bool isOverPatrol = false;    private AStar aStar;    public override void OnAwake()    {        aStar = GetComponent<AStar>();        if (aStar != null && aStar.enabled == true)        {            aStar.Speed = speed.Value;            aStar.AngularSpeed = angularSpeed.Value;            aStar.ArriveDistance = arriveDistance.Value;        }    }    public override void OnStart()    {        StartCoroutine(_myPatrol());    //调用巡逻    }    public override TaskStatus OnUpdate()    {        if (waypoints == null)  //如果目标点为空,则返回失败        {            Debug.LogError("No Set Waypoint!");            return TaskStatus.Failure;        }        if (stopOnTaskEnd.Value && isOverPatrol)    //如果选择了巡逻完后停止,并且巡逻完毕了。返回true        {            return TaskStatus.Success;        }        return TaskStatus.Running;    }    IEnumerator _myPatrol()    {        if (stopOnTaskEnd.Value)    //如果需要巡逻后停住        {            foreach (GameObject item in waypoints.Value)    //巡逻完所有的节点            {                aStar.StarPath(item.transform);     //设置一个目标点                while (true)                {                    if (Vector3.Distance(transform.position, item.transform.position) <= arriveDistance.Value)                    {                        break;                    }                    yield return null;                }                yield return new WaitForSeconds(waypointPauseDuration.Value);   //停留多少间隙            }            isOverPatrol = true;    //已经完成巡逻            yield return null;        }        else        {            while (true)    //完成巡逻后重新设置对象            {                foreach (GameObject item in waypoints.Value)                {                    aStar.StarPath(item.transform);                    while (true)                    {                        if (Vector3.Distance(transform.position,item.transform.position) <= arriveDistance.Value)                        {                            break;                        }                        yield return null;                    }                     yield return new WaitForSeconds(waypointPauseDuration.Value);   //停留多少间隙                }            }        }    }}

这里主要是使用了IEnumerator实现了巡逻的主要逻辑。首先定义一些行为树自带的共享变量,这样就可以在各个脚本间引用一些值了,比如移动速度等。要注意的地方是:共享变量必须定义为publi,且要用.Value的形式才能引用出里面的值。比如public SharedFloat speed;想用speed必须是speed.Value才行。我们先将一些必要的变量定义好,然后写一个IEnumertor,里面分别针对巡逻完后是否需要停止写逻辑。如果需要停止,我们就遍历巡逻目标点waypoints这个List,首先用aStar.StarPath(item.transform)寻路并移动(注意:不能一直寻路移动,只需要执行一次就行了),然后用一个死循环判断目标点和自己的距离,如果小于arriveDistance则认为是到达了,到达了直接break跳出去寻路到下一个目标点。否则就yield return null跳过这一帧(这里一定要注意!这个死循环里判断,如果不满足一定要跳过这一帧,否则会在这一帧不停得判断,导致Unity卡死。这个地方卡了我好久)。还有种情况,巡逻完不需要停止,即反复巡逻,这里我们只需要把刚才那段复制进一个死循环即可,当目标点都走完了,我们再从第一个开始,继续巡逻。
就这样,我们就写了一个简单的小例子,有兴趣的朋友可以自己试试。

最后这里放上我们学习用的插件:
行为树插件 http://download.csdn.net/download/l1606468155/9923832
AStarPathfinding插件 http://download.csdn.net/download/l1606468155/9923826

原创粉丝点击