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


    http://blog.csdn.net/janeky/article/details/17598113

    在之前的几篇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,用来处理自动寻路,已经角色动画变换。代码比较长,大家可以结合注释来理解

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. using UnityEngine;  
    2. using System.Collections;  
    3.   
    4. public class AgentLocomotion : MonoBehaviour  
    5. {  
    6.     private Vector3 target;//目标位置  
    7.     private NavMeshAgent agent;  
    8.     private Animation anim;//动画  
    9.     private string locoState = "Locomotion_Stand";  
    10.     private Vector3 linkStart;//OffMeshLink的开始点  
    11.     private Vector3 linkEnd;//OffMeshLink的结束点  
    12.     private Quaternion linkRotate;//OffMeshLink的旋转  
    13.     private bool begin;//是否开始寻路  
    14.   
    15.     // Use this for initialization  
    16.     void Start()  
    17.     {  
    18.         agent = GetComponent<NavMeshAgent>();  
    19.         //自动移动并关闭OffMeshLinks,即在两个隔离障碍物直接生成的OffMeshLink,agent不会自动越过  
    20.         agent.autoTraverseOffMeshLink = false;  
    21.         //创建动画  
    22.         AnimationSetup();  
    23.         //起一个协程,处理动画状态机  
    24.         StartCoroutine(AnimationStateMachine());  
    25.     }  
    26.   
    27.     void Update()  
    28.     {  
    29.         //鼠标左键点击  
    30.         if (Input.GetMouseButtonDown(0))  
    31.         {  
    32.             //摄像机到点击位置的的射线  
    33.             Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);  
    34.             RaycastHit hit;  
    35.             if (Physics.Raycast(ray, out hit))  
    36.             {  
    37.                 //判断点击的是否地形  
    38.                 if (hit.collider.tag.Equals("Obstacle"))  
    39.                 {  
    40.                     begin = true;  
    41.                     //点击位置坐标  
    42.                     target = hit.point;  
    43.                 }  
    44.             }  
    45.         }  
    46.         //每一帧,设置目标点  
    47.         if (begin)  
    48.         {  
    49.             agent.SetDestination(target);  
    50.         }  
    51.     }  
    52.   
    53.     IEnumerator AnimationStateMachine()  
    54.     {  
    55.         //根据locoState不同的状态来处理,调用相关的函数  
    56.         while (Application.isPlaying)  
    57.         {  
    58.             yield return StartCoroutine(locoState);  
    59.         }  
    60.     }  
    61.   
    62.     //站立  
    63.     IEnumerator Locomotion_Stand()  
    64.     {  
    65.         do  
    66.         {  
    67.             UpdateAnimationBlend();  
    68.             yield return new WaitForSeconds(0);  
    69.         } while (agent.remainingDistance == 0);  
    70.         //未到达目标点,转到下一个状态Locomotion_Move  
    71.         locoState = "Locomotion_Move";  
    72.         yield return null;  
    73.     }  
    74.   
    75.     IEnumerator Locomotion_Move()  
    76.     {  
    77.         do  
    78.         {  
    79.             UpdateAnimationBlend();  
    80.             yield return new WaitForSeconds(0);  
    81.             //角色处于OffMeshLink,根据不同的地点,选择不同动画  
    82.             if (agent.isOnOffMeshLink)  
    83.             {  
    84.                 locoState = SelectLinkAnimation();  
    85.                 return (true);  
    86.             }  
    87.         } while (agent.remainingDistance != 0);  
    88.         //已经到达目标点,状态转为Stand  
    89.         locoState = "Locomotion_Stand";  
    90.         yield return null;  
    91.     }  
    92.   
    93.     IEnumerator Locomotion_Jump()  
    94.     {  
    95.         //播放跳跃动画  
    96.         string linkAnim = "RunJump";  
    97.         Vector3 posStart = transform.position;  
    98.   
    99.         agent.Stop(true);  
    100.         anim.CrossFade(linkAnim, 0.1f, PlayMode.StopAll);  
    101.         transform.rotation = linkRotate;  
    102.   
    103.         do  
    104.         {  
    105.             //计算新的位置  
    106.             float tlerp = anim[linkAnim].normalizedTime;  
    107.             Vector3 newPos = Vector3.Lerp(posStart, linkEnd, tlerp);  
    108.             newPos.y += 0.4f * Mathf.Sin(3.14159f * tlerp);  
    109.             transform.position = newPos;  
    110.   
    111.             yield return new WaitForSeconds(0);  
    112.         } while (anim[linkAnim].normalizedTime < 1);  
    113.         //动画恢复到Idle  
    114.         anim.Play("Idle");  
    115.         agent.CompleteOffMeshLink();  
    116.         agent.Resume();  
    117.         //下一个状态为Stand  
    118.         transform.position = linkEnd;  
    119.         locoState = "Locomotion_Stand";  
    120.         yield return null;  
    121.     }  
    122.     //梯子  
    123.     IEnumerator Locomotion_Ladder()  
    124.     {  
    125.         //梯子的中心位置  
    126.         Vector3 linkCenter = (linkStart + linkEnd) * 0.5f;  
    127.         string linkAnim;  
    128.         //判断是在梯子上还是梯子下  
    129.         if (transform.position.y > linkCenter.y)  
    130.             linkAnim = "Ladder Down";  
    131.         else  
    132.             linkAnim = "Ladder Up";  
    133.   
    134.         agent.Stop(true);  
    135.   
    136.         Quaternion startRot = transform.rotation;  
    137.         Vector3 startPos = transform.position;  
    138.         float blendTime = 0.2f;  
    139.         float tblend = 0f;  
    140.   
    141.         //角色的位置插值变化(0.2内变化)  
    142.         do  
    143.         {  
    144.             transform.position = Vector3.Lerp(startPos, linkStart, tblend / blendTime);  
    145.             transform.rotation = Quaternion.Lerp(startRot, linkRotate, tblend / blendTime);  
    146.   
    147.             yield return new WaitForSeconds(0);  
    148.             tblend += Time.deltaTime;  
    149.         } while (tblend < blendTime);  
    150.         //设置位置  
    151.         transform.position = linkStart;  
    152.         //播放动画  
    153.         anim.CrossFade(linkAnim, 0.1f, PlayMode.StopAll);  
    154.         agent.ActivateCurrentOffMeshLink(false);  
    155.         //等待动画结束  
    156.         do  
    157.         {  
    158.             yield return new WaitForSeconds(0);  
    159.         } while (anim[linkAnim].normalizedTime < 1);  
    160.         agent.ActivateCurrentOffMeshLink(true);  
    161.         //恢复Idle状态  
    162.         anim.Play("Idle");  
    163.         transform.position = linkEnd;  
    164.         agent.CompleteOffMeshLink();  
    165.         agent.Resume();  
    166.         //下一个状态Stand  
    167.         locoState = "Locomotion_Stand";  
    168.         yield return null;  
    169.     }  
    170.   
    171.     private string SelectLinkAnimation()  
    172.     {  
    173.         //获得当前的OffMeshLink数据  
    174.         OffMeshLinkData link = agent.currentOffMeshLinkData;  
    175.         //计算角色当前是在link的开始点还是结束点(因为OffMeshLink是双向的)  
    176.         float distS = (transform.position - link.startPos).magnitude;  
    177.         float distE = (transform.position - link.endPos).magnitude;  
    178.   
    179.         if (distS < distE)  
    180.         {  
    181.             linkStart = link.startPos;  
    182.             linkEnd = link.endPos;  
    183.         }  
    184.         else  
    185.         {  
    186.             linkStart = link.endPos;  
    187.             linkEnd = link.startPos;  
    188.         }  
    189.         //OffMeshLink的方向  
    190.         Vector3 alignDir = linkEnd - linkStart;  
    191.         //忽略y轴  
    192.         alignDir.y = 0;  
    193.         //计算旋转角度  
    194.         linkRotate = Quaternion.LookRotation(alignDir);  
    195.   
    196.         //判断OffMeshLink是手动的(楼梯)还是自动生成的(跳跃)  
    197.         if (link.linkType == OffMeshLinkType.LinkTypeManual)  
    198.         {  
    199.             return ("Locomotion_Ladder");  
    200.         }  
    201.         else  
    202.         {  
    203.             return ("Locomotion_Jump");  
    204.         }  
    205.     }  
    206.   
    207.     private void AnimationSetup()  
    208.     {  
    209.         anim = GetComponent<Animation>();  
    210.   
    211.         // 把walk和run动画放到同一层,然后同步他们的速度。  
    212.         anim["Walk"].layer = 1;  
    213.         anim["Run"].layer = 1;  
    214.         anim.SyncLayer(1);  
    215.   
    216.         //设置“跳跃”,“爬楼梯”,“下楼梯”的动画模式和速度  
    217.         anim["RunJump"].wrapMode = WrapMode.ClampForever;  
    218.         anim["RunJump"].speed = 2;  
    219.         anim["Ladder Up"].wrapMode = WrapMode.ClampForever;  
    220.         anim["Ladder Up"].speed = 2;  
    221.         anim["Ladder Down"].wrapMode = WrapMode.ClampForever;  
    222.         anim["Ladder Down"].speed = 2;  
    223.   
    224.         //初始化动画状态为Idle  
    225.         anim.CrossFade("Idle", 0.1f, PlayMode.StopAll);  
    226.     }  
    227.     //更新动画融合  
    228.     private void UpdateAnimationBlend()  
    229.     {  
    230.         //行走速度  
    231.         float walkAnimationSpeed = 1.5f;  
    232.         //奔跑速度  
    233.         float runAnimationSpeed = 4.0f;  
    234.         //速度阀值(idle和walk的临界点)  
    235.         float speedThreshold = 0.1f;  
    236.   
    237.         //速度,只考虑x和z  
    238.         Vector3 velocityXZ = new Vector3(agent.velocity.x, 0.0f, agent.velocity.z);  
    239.         //速度值  
    240.         float speed = velocityXZ.magnitude;  
    241.         //设置Run动画的速度  
    242.         anim["Run"].speed = speed / runAnimationSpeed;  
    243.         //设置Walk动画的速度  
    244.         anim["Walk"].speed = speed / walkAnimationSpeed;  
    245.   
    246.         //根据agent的速度大小,确定animation的播放状态  
    247.         if (speed > (walkAnimationSpeed + runAnimationSpeed) / 2)  
    248.         {  
    249.             anim.CrossFade("Run");  
    250.         }  
    251.         else if (speed > speedThreshold)  
    252.         {  
    253.             anim.CrossFade("Walk");  
    254.         }  
    255.         else  
    256.         {  
    257.             anim.CrossFade("Idle", 0.1f, PlayMode.StopAll);  
    258.         }  
    259.     }  
    260. }  

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


    • 总结

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

    • 源码

    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

  • 相关阅读:
    Debian Linux 查看用户命令
    WPF 样式(Style)(2)
    SQLServer2012下更改数据库名字
    WPF 触发器Triggers
    随记
    网页打印总结(1)
    安装node.js,CoffeeScript,Express.js,mysql,jade
    javascript——this
    win8下IIS8.0下uploadifyv3.1上传文件超过30M,报HTTP Error(404)
    Linux软件安装常用方法(转载)
  • 原文地址:https://www.cnblogs.com/123ing/p/4114848.html
Copyright © 2020-2023  润新知