Unity Apex寻路插件 与Mecanim动画结合。

来源:互联网 发布:webclient post json 编辑:程序博客网 时间:2024/05/18 03:12

最近寻找合适的寻路算法。目前主流的有三种选择。

Unity自带的NavMesh。

优点:原生寻路,整合性好,基于多边形网格的寻路,更精确,适合制作人数不是很多,但需求精度高,功能庞大的动作类游戏。

缺点:慢,不适合做RTS,不能在运行时重新烘焙。


A Star Project寻路算法(正版100刀,祖国版五块钱,淘宝有卖)

一开始我也打算用这个,不过,看了一下网上的评价自己也简单体验了一下,功能强大没的说,但是的确易用性上不是很好。

在寻路算法上甚至优于Apex,但是学习曲线明显比Apex长,代码结构也没有Apex好,在单位很多的时候,负载均衡没有Apex强大

支持Runtime更新,但是更新的时候对帧率没有保障,对于需要频繁Runtime Bake的生存游戏,明显就不合适了。他似乎只能以网格为单位进行烘焙,但是没必要把所有的网格都重新烘焙一遍。

适合做RTS,MMORPG之类的游戏,当然动作游戏也OK.不适合做生存类游戏。


最后是Apex Path 寻路算法。

缺点:

(1)基于Grid的寻路算法,不像NavMesh那样有非常高的精度。不支持单Grid多层,多层的时候需要Portal(传送门,可以实现等价于Off mesh Link 的功能)

(2)贵:有三个插件 Apex Path基本包,95刀,还有两个DLC,啊,不对,是Add on。一个是Apex Steering 支持更优秀的导航和阵型,不过据说没有BehaviorDesigner那家出的Formation Pack好用。还有一个是Apex Dynamic Obstacle,支持更强大的动态障碍功能,据说优化的非常好,不过目前没有祖国版。可以等打折的时候入手(妈的,现在Asset Store 已经超过Sbeam成为我的第一剁手eshop了)

优点:

代码结构合理,非常适合学习

支持一个网格内的局部动态更新。比如新建了一个台阶,那么可以直接针对台阶附近10*10*10Unity单位的网格进行再烘焙。不必重新烘焙整张网格。

负载均衡做的非常好,几百个单位也不卡

支持超大世界,不过需要同样需要做网格分页,可以结合WorldStreamer插件一起做分页。

多线程烘焙,并且支持逐帧延迟烘焙,以保证不掉帧。

非常易于扩展,这得益于良好的代码结构


所以最后我选择了Apex。


基本的例子都很好用,不过当我开始做导航和Mecanim动画结合的时候,遇到了一些小问题。 就是人物像得了帕金森一样,一上楼梯就抖个不停。

最后找到原因:

后来发现官网提供的例子,没有使用碰撞体,单纯演示了如何实现IMoveUnit接口。我傻傻的直接用了一个胶囊碰撞体做为Player的碰撞体。胶囊碰撞体在上楼梯的时候,会不停的改变速度。于是乎Unity 的BlendTree自然也就会不停的来回切换造成抖动了。

之后无意中发现了官网还有一个例子,是使用Apex结合CharacterController来实现导航移动的,于是我想是不是把这两个例子结合起来能解决我的问题呢。

结合后的代码如下:

namespace Apex.Mecanim{    using UnityEngine;    using System.Collections;    using Apex.Steering;    public class ApexMecanimMover : MonoBehaviour, IMoveUnits    {                public float animatorSpeed = 1f;        private CharacterController _controller;        private Transform _transform;        private Animator _animator;        private int _speedId;        private int _angleId;        private Rigidbody _rigidbody;        private void Start()        {            _animator = GetComponent<Animator>();            _controller = GetComponent<CharacterController>();            _transform = transform;            _animator.speed = animatorSpeed;            _speedId = Animator.StringToHash("Speed");            _angleId = Animator.StringToHash("Angle");        }        public void Move(Vector3 velocity, float deltaTime)        {            //_rigidbody.velocity = velocity;            float speed = Mathf.Sqrt(velocity.x * velocity.x + velocity.z + velocity.z);            _animator.SetFloat(_speedId, speed);            if (speed < 0.2f)            {                _animator.SetFloat(_speedId, 0f);                _animator.SetFloat(_angleId, 0f);                return;            }            _animator.SetFloat(_speedId, speed);            float angleDirection = TurnDir(_transform.forward, velocity);            float angle = Vector3.Angle(_transform.forward, velocity) * angleDirection;            _animator.SetFloat(_angleId, angle);            _controller.Move(velocity * deltaTime);        }        public void Rotate(Vector3 targetOrientation, float angularSpeed, float deltaTime)        {            /* NOT USED */            _transform.forward = Vector3.RotateTowards(_transform.forward, targetOrientation, angularSpeed * deltaTime, 0f);        }        public void Stop()        {            _animator.SetFloat(_speedId, 0f);        }        private static float TurnDir(Vector3 p1, Vector3 p2)        {            return Mathf.Sign((p1.z * p2.x) - (p1.x * p2.z));        }    }}


运行,帕金森终于治好啦!

阅读全文
1 0