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,用来处理自动寻路,已经角色动画变换。代码比较长,大家可以结合注释来理解
- 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);
- }
- }
- }
效果图如下,点击任何一个地点,角色都可以自动寻路过去。中间可能经过不同的障碍物,我们可以看到角色如我们所预料的一样,可以跳跃下来,可以爬楼梯,最终到达目标点。
- 总结
今天的这个例子比较复杂,要根据寻路网格的类型,来处理角色的动作是普通寻路,还是攀爬,抑或跳跃。这个例子应该是比较接近真实项目了。大家在实际项目中如果还有更加复杂的寻路,欢迎探讨。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
Unity手游之路<十>自动寻路Navmesh之跳跃,攀爬,斜坡的更多相关文章
- Unity手游之路<八>自动寻路Navmesh之入门
http://blog.csdn.net/janeky/article/details/17457533 在的大部分mmo游戏都有了自动寻路功能.点击场景上的一个位置,角色就会自动寻路过去.中间可能会 ...
- Unity 自动寻路Navmesh之跳跃,攀爬,斜坡
在之前的几篇Blog总,我们已经系统学习了自动寻路插件Navmesh的相关概念和细节.然而,如果要做一个场景精美的手游,需要用到各种复杂的场景地形,而不仅仅是平地上的自动寻路.今天我们将通过一个完整的 ...
- Unity手游之路<九>自动寻路Navmesh之高级主题
http://blog.csdn.net/janeky/article/details/17492531 之前我们一起学习了如何使用Navmesh组件来实现最基本的角色自动寻路.今天我们再继续深入探索 ...
- Unity手游之路<十二>手游资源热更新策略探讨
http://blog.csdn.net/janeky/article/details/17666409 上一次我们学习了如何将资源进行打包.这次就可以用上场了,我们来探讨一下手游资源的增量更新策略. ...
- [转]Unity手游之路<十二>手游资源热更新策略探讨
最近梳理了下游戏流程.恩,本来想写下,但是,还是看前辈的吧 版权声明: https://blog.csdn.net/janeky/article/details/17666409 上一次我们学习了如何 ...
- 【学习】Unity手游之路<十二>手游资源热更新策略探讨
http://blog.csdn.net/janeky/article/details/17666409 =============================================== ...
- Unity手游之路<七>角色控制器
Unity手游之路<七>角色控制器 我们要控制角色的移动,可以全部细节都由自己来实现.控制角色模型的移动,同时移动摄影机,改变视角.当然Unity也提供了一些组件,可以让我们做更少的工作, ...
- Unity手游之路自动寻路Navmesh之入门
http://blog.csdn.net/janeky/article/details/17457533 现在的大部分mmo游戏都有了自动寻路功能.点击场景上的一个位置,角色就会自动寻路过去.中间可能 ...
- Unity手游:自动寻路Navmesh 跳跃 攀爬 斜坡
原地址:http://dong2008hong.blog.163.com/blog/static/46968827201403114644210/ 步骤 1.在场景中摆放各种模型,包括地板,斜坡,山体 ...
随机推荐
- UML图例
概述 常用建模方法:BOOCH.OMT.OOSE等 统一建模语言:UML(Unified Modeling Language) 软件开发过程:RUP(Rational Unified Process) ...
- ci重定向
ci重定向 对于/index.php/abc这种url,Apache和lighttpd会按"index.php?abc"来解释,而nginx会认为是请求名字是"index ...
- c++编译错误提示及解决
IntelliSense: #error 指令: Please use the /MD switch for _AFXDLL builds 修改设置:工程(Project)-> 属性(Prope ...
- Javascript+Dom(加减乘除计算器)
计算器介绍:只能进行加减乘除,提示用户输入数字,正则表达式限制用户只能输入数字(在用户输入时限制),如果出现除零操作答案为0: 有两种针对不同运算符的解决方法: 1. 使用eval() 函数 //函数 ...
- 使用kvm虚拟出Centos6.5系统相关步骤
使用kvm虚拟出Centos6.5系统相关步骤 kvm是啥东西,亲们自行百度哇,一两句话也说不清楚,直接进主题使用宿主机虚拟出一台centos6.5的系统,当然其他系统也可以的,考虑到企业常用服务器系 ...
- Java Native Interface Specification
http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html http://www.ibm.com/develo ...
- netstat--查看服务器[有效]连接数--统计端口并发数--access.log分析
简介 Netstat 命令用于显示各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),masquerade 连接,多播成员 (Multicast Member ...
- IE安全分析
IE安全问题,这个话题似乎很古老了,但是问题又是层出不穷~ 对于IE的安全,我个人认为有两点需要关注,一个是上网的安全,二个是IE解析代码的安全 对于IE上网安全,这是最基本的,也是最常用的了 附上一 ...
- Java并发编程核心方法与框架-ExecutorService的使用
在ThreadPoolExecutor中使用ExecutorService中的方法 方法invokeAny()和invokeAll()具有阻塞特性 方法invokeAny()取得第一个完成任务的结果值 ...
- <Web 之困 现代Web应用安全指南>一本好书 69.00?
NET代码安全 界面漏洞防范与程序优化 一. SQL 注入攻击的源头 1. 过滤或转移危险字符 2. 使用SqlParameter类:.NET 框架有一个叫做SqlParameter 的集合类型,可 ...