原地址:http://dong2008hong.blog.163.com/blog/static/46968827201403114644210/

步骤

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);
        }
    }
}
效果图如下,点击任何一个地点,角色都可以自动寻路过去。中间可能经过不同的障碍物,我们可以看到角色如我们所预料的一样,可以跳跃下来,可以爬楼梯,最终到达目标点。

总结 

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

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

Unity手游:自动寻路Navmesh 跳跃 攀爬 斜坡的更多相关文章

  1. Unity手游之路<十>自动寻路Navmesh之跳跃,攀爬,斜坡

    http://blog.csdn.net/janeky/article/details/17598113 在之前的几篇Blog总,我们已经系统学习了自动寻路插件Navmesh的相关概念和细节.然而,如 ...

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

    在之前的几篇Blog总,我们已经系统学习了自动寻路插件Navmesh的相关概念和细节.然而,如果要做一个场景精美的手游,需要用到各种复杂的场景地形,而不仅仅是平地上的自动寻路.今天我们将通过一个完整的 ...

  3. Unity手游之路<七>角色控制器

    Unity手游之路<七>角色控制器 我们要控制角色的移动,可以全部细节都由自己来实现.控制角色模型的移动,同时移动摄影机,改变视角.当然Unity也提供了一些组件,可以让我们做更少的工作, ...

  4. 知物由学|游戏开发者如何从容应对Unity手游风险?

    本文由  网易云发布. "知物由学"是网易云易盾打造的一个品牌栏目,词语出自汉·王充<论衡·实知>.人,能力有高下之分,学习才知道事物的道理,而后才有智慧,不去求问就不 ...

  5. Unity手游引擎安全解析及实践

    近日,由Unity主办的"Unity技术开放日"在广州成功举办,网易移动安全技术专家卓辉作为特邀嘉宾同现场400名游戏开发者分享了网易在手游安全所积累的经验.当下,很多手游背后都存 ...

  6. Unity手游之路<八>自动寻路Navmesh之入门

    http://blog.csdn.net/janeky/article/details/17457533 在的大部分mmo游戏都有了自动寻路功能.点击场景上的一个位置,角色就会自动寻路过去.中间可能会 ...

  7. Unity手游之路自动寻路Navmesh之入门

    http://blog.csdn.net/janeky/article/details/17457533 现在的大部分mmo游戏都有了自动寻路功能.点击场景上的一个位置,角色就会自动寻路过去.中间可能 ...

  8. Unity手游之路<二>Java版服务端使用protostuff简化protobuf开发

    http://blog.csdn.net/janeky/article/details/17151465 开发一款网络游戏,首先要考虑的是客户端服务端之间用何种编码格式进行通信.之前我们介绍了Unit ...

  9. Unity手游之路<十三>手游代码更新策略探讨

    http://blog.csdn.net/janeky/article/details/25923151 这几个月公司项目非常忙,加上家里事情也多,所以blog更新一直搁置了.最近在项目开发上线过程中 ...

随机推荐

  1. (转)[转]大数据时代的 9 大Key-Value存储数据库

    在过去的十年中,计算世界已经改变.现在不仅在大公司,甚至一些小公司也积累了TB量级的数据.各种规模的组织开始有了处理大数据的需求,而目前关系型数据库在可缩放方面几乎已经达到极限. 一个解决方案是使用键 ...

  2. insertorupdate

    MERGE INTO  运用的心得 最近完成一个功能,就是往表里插入数据,以party_id 和prod_id为联合主键,存在的更新,不存在的插入, ORACLE 10g 后可以试用MERGE INT ...

  3. 通过Driver获取数据库连接

    先看一下文件,在当前包下有一个properties配置文件,在根目录下有一个lib文件夹,里面放的是mySql的驱动jar包 Driver :是一个接口,数据库厂商必须提供实现的接口,能从其中获取数据 ...

  4. 4.Servlet_Form表单处理

    1.建项目"3Servlet_Form",src下建包“com.amaker.servlet”,web-root下建Register.html <!DOCTYPE html& ...

  5. exynos 4412 电源管理芯片PMIC 的配置及使用方法

    /** ****************************************************************************** * @author    Maox ...

  6. Js操作Select大全(取值、设置选中)

    Js操作Select是很常见的,也是比较实用的. jquery操作select(取值,设置选中) 每一次操作select的时候,总是要出来翻一下资料,不如自己总结一下,以后就翻这里了. 比如<s ...

  7. .NET开发作业调度(job scheduling) - Quartz.NET

    Quartz.NET是JAVA Job Scheduling框架Quartz在.NET平台上的实现,可以满足小型乃至大型企业应用中的Job Scheduling. 通过Nuget安装Quartz.NE ...

  8. 1106. Lowest Price in Supply Chain (25)

    A supply chain is a network of retailers(零售商), distributors(经销商), and suppliers(供应商)-- everyone invo ...

  9. Mac OS X 10.10.2 Yosemite jdk 环境变量配置

    我的Mac系统版本是OS X 10.10.2 Yosemite,为了用Eclipse做android开发,安装了jdk 1.7, 但是如果想使用IntelliJ IDE做android开发的话,就需要 ...

  10. COALESCE在SQL拼接中的大用途

    SQL拼接可以使得代码比较灵活,不会那么死板,对于维护也比较方便. 下面是简单的SQL拼接,同时也包含了隐式游标的概念吧,可以遍历表中的每一个字段 -------------------------- ...