array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 Unity 自动寻路Navmesh之跳跃,攀爬,斜坡 - 爱码网

Unity 自动寻路Navmesh之跳跃,攀爬,斜坡

在之前的几篇Blog总,我们已经系统学习了自动寻路插件Navmesh的相关概念和细节。然而,如果要做一个场景精美的手游,需要用到各种复杂的场景地形,而不仅仅是平地上的自动寻路。今天我们将通过一个完整的复杂的实例,来贯穿各个细节。我们将实现一个复杂的场景,角色可以在里面攀爬,跳跃,爬坡。是不是感觉很像当年的CS游戏呢?本案例将会用得一些基本的动画函数,大家可以先结合文档有个大概的了解。本实例是在官方的范例上加工而成。

(转载请注明原文地址http://blog.csdn.net/janeky/article/details/17598113

  • 步骤
1.在场景中摆放各种模型,包括地板,斜坡,山体,扶梯等
2.为所有的模型加上Navigation Static和OffMeshLink Generatic(这个根据需要,例如地板与斜坡相连,斜坡就不需要添加OffMeshLink)
3.特殊处理扶梯,需要手动添加Off Mesh Link,设置好开始点和结束点
4.保存场景,烘焙场景
5.添加角色模型,为其加Nav Mesh Agent组件

6.为角色添加一个新脚本,AgentLocomotion.cs,用来处理自动寻路,已经角色动画变换。代码比较长,大家可以结合注释来理解

using UnityEngine;
using System.Collections;

public class AgentLocomotion : MonoBehaviour
{
    private Vector3 target;//目标位置
    private NavMeshAgent agent;
    private Animation anim;//动画
    private string locoState = "Locomotion_Stand";
    private Vector3 linkStart;//OffMeshLink的开始点
    private Vector3 linkEnd;//OffMeshLink的结束点
    private Quaternion linkRotate;//OffMeshLink的旋转
    private bool begin;//是否开始寻路

    // Use this for initialization
    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
        //自动移动并关闭OffMeshLinks,即在两个隔离障碍物直接生成的OffMeshLink,agent不会自动越过
        agent.autoTraverseOffMeshLink = false;
        //创建动画
        AnimationSetup();
        //起一个协程,处理动画状态机
        StartCoroutine(AnimationStateMachine());
    }

    void Update()
    {
        //鼠标左键点击
        if (Input.GetMouseButtonDown(0))
        {
            //摄像机到点击位置的的射线
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                //判断点击的是否地形
                if (hit.collider.tag.Equals("Obstacle"))
                {
                    begin = true;
                    //点击位置坐标
                    target = hit.point;
                }
            }
        }
        //每一帧,设置目标点
        if (begin)
        {
            agent.SetDestination(target);
        }
    }

    IEnumerator AnimationStateMachine()
    {
        //根据locoState不同的状态来处理,调用相关的函数
        while (Application.isPlaying)
        {
            yield return StartCoroutine(locoState);
        }
    }

    //站立
    IEnumerator Locomotion_Stand()
    {
        do
        {
            UpdateAnimationBlend();
            yield return new WaitForSeconds(0);
        } while (agent.remainingDistance == 0);
        //未到达目标点,转到下一个状态Locomotion_Move
        locoState = "Locomotion_Move";
        yield return null;
    }

    IEnumerator Locomotion_Move()
    {
        do
        {
            UpdateAnimationBlend();
            yield return new WaitForSeconds(0);
            //角色处于OffMeshLink,根据不同的地点,选择不同动画
            if (agent.isOnOffMeshLink)
            {
                locoState = SelectLinkAnimation();
                return (true);
            }
        } while (agent.remainingDistance != 0);
        //已经到达目标点,状态转为Stand
        locoState = "Locomotion_Stand";
        yield return null;
    }

    IEnumerator Locomotion_Jump()
    {
        //播放跳跃动画
        string linkAnim = "RunJump";
        Vector3 posStart = transform.position;

        agent.Stop(true);
        anim.CrossFade(linkAnim, 0.1f, PlayMode.StopAll);
        transform.rotation = linkRotate;

        do
        {
            //计算新的位置
            float tlerp = anim[linkAnim].normalizedTime;
            Vector3 newPos = Vector3.Lerp(posStart, linkEnd, tlerp);
            newPos.y += 0.4f * Mathf.Sin(3.14159f * tlerp);
            transform.position = newPos;

            yield return new WaitForSeconds(0);
        } while (anim[linkAnim].normalizedTime < 1);
        //动画恢复到Idle
        anim.Play("Idle");
        agent.CompleteOffMeshLink();
        agent.Resume();
        //下一个状态为Stand
        transform.position = linkEnd;
        locoState = "Locomotion_Stand";
        yield return null;
    }
    //梯子
    IEnumerator Locomotion_Ladder()
    {
        //梯子的中心位置
        Vector3 linkCenter = (linkStart + linkEnd) * 0.5f;
        string linkAnim;
        //判断是在梯子上还是梯子下
        if (transform.position.y > linkCenter.y)
            linkAnim = "Ladder Down";
        else
            linkAnim = "Ladder Up";

        agent.Stop(true);

        Quaternion startRot = transform.rotation;
        Vector3 startPos = transform.position;
        float blendTime = 0.2f;
        float tblend = 0f;

        //角色的位置插值变化(0.2内变化)
        do
        {
            transform.position = Vector3.Lerp(startPos, linkStart, tblend / blendTime);
            transform.rotation = Quaternion.Lerp(startRot, linkRotate, tblend / blendTime);

            yield return new WaitForSeconds(0);
            tblend += Time.deltaTime;
        } while (tblend < blendTime);
        //设置位置
        transform.position = linkStart;
        //播放动画
        anim.CrossFade(linkAnim, 0.1f, PlayMode.StopAll);
        agent.ActivateCurrentOffMeshLink(false);
        //等待动画结束
        do
        {
            yield return new WaitForSeconds(0);
        } while (anim[linkAnim].normalizedTime < 1);
        agent.ActivateCurrentOffMeshLink(true);
        //恢复Idle状态
        anim.Play("Idle");
        transform.position = linkEnd;
        agent.CompleteOffMeshLink();
        agent.Resume();
        //下一个状态Stand
        locoState = "Locomotion_Stand";
        yield return null;
    }

    private string SelectLinkAnimation()
    {
        //获得当前的OffMeshLink数据
        OffMeshLinkData link = agent.currentOffMeshLinkData;
        //计算角色当前是在link的开始点还是结束点(因为OffMeshLink是双向的)
        float distS = (transform.position - link.startPos).magnitude;
        float distE = (transform.position - link.endPos).magnitude;

        if (distS < distE)
        {
            linkStart = link.startPos;
            linkEnd = link.endPos;
        }
        else
        {
            linkStart = link.endPos;
            linkEnd = link.startPos;
        }
        //OffMeshLink的方向
        Vector3 alignDir = linkEnd - linkStart;
        //忽略y轴
        alignDir.y = 0;
        //计算旋转角度
        linkRotate = Quaternion.LookRotation(alignDir);

        //判断OffMeshLink是手动的(楼梯)还是自动生成的(跳跃)
        if (link.linkType == OffMeshLinkType.LinkTypeManual)
        {
            return ("Locomotion_Ladder");
        }
        else
        {
            return ("Locomotion_Jump");
        }
    }

    private void AnimationSetup()
    {
        anim = GetComponent<Animation>();

        // 把walk和run动画放到同一层,然后同步他们的速度。
        anim["Walk"].layer = 1;
        anim["Run"].layer = 1;
        anim.SyncLayer(1);

        //设置“跳跃”,“爬楼梯”,“下楼梯”的动画模式和速度
        anim["RunJump"].wrapMode = WrapMode.ClampForever;
        anim["RunJump"].speed = 2;
        anim["Ladder Up"].wrapMode = WrapMode.ClampForever;
        anim["Ladder Up"].speed = 2;
        anim["Ladder Down"].wrapMode = WrapMode.ClampForever;
        anim["Ladder Down"].speed = 2;

        //初始化动画状态为Idle
        anim.CrossFade("Idle", 0.1f, PlayMode.StopAll);
    }
    //更新动画融合
    private void UpdateAnimationBlend()
    {
        //行走速度
        float walkAnimationSpeed = 1.5f;
        //奔跑速度
        float runAnimationSpeed = 4.0f;
        //速度阀值(idle和walk的临界点)
        float speedThreshold = 0.1f;

        //速度,只考虑x和z
        Vector3 velocityXZ = new Vector3(agent.velocity.x, 0.0f, agent.velocity.z);
        //速度值
        float speed = velocityXZ.magnitude;
        //设置Run动画的速度
        anim["Run"].speed = speed / runAnimationSpeed;
        //设置Walk动画的速度
        anim["Walk"].speed = speed / walkAnimationSpeed;

        //根据agent的速度大小,确定animation的播放状态
        if (speed > (walkAnimationSpeed + runAnimationSpeed) / 2)
        {
            anim.CrossFade("Run");
        }
        else if (speed > speedThreshold)
        {
            anim.CrossFade("Walk");
        }
        else
        {
            anim.CrossFade("Idle", 0.1f, PlayMode.StopAll);
        }
    }
}


效果图如下,点击任何一个地点,角色都可以自动寻路过去。中间可能经过不同的障碍物,我们可以看到角色如我们所预料的一样,可以跳跃下来,可以爬楼梯,最终到达目标点。
Unity 自动寻路Navmesh之跳跃,攀爬,斜坡

  • 总结

今天的这个例子比较复杂,要根据寻路网格的类型,来处理角色的动作是普通寻路,还是攀爬,抑或跳跃。这个例子应该是比较接近真实项目了。大家在实际项目中如果还有更加复杂的寻路,欢迎探讨。[email protected]

  • 源码

http://pan.baidu.com/s/1i35cVOD

  • 参考资料
1.http://www.xuanyusong.com/
2.http://liweizhaolili.blog.163.com/
3.http://game.ceeger.com/Components/class-NavMeshAgent.html
posted @ 2016-09-20 17:58 00000000O 阅读(...) 评论(...) 编辑 收藏

相关文章: