Unity手游:自动寻路Navmesh 跳跃 攀爬 斜坡
原地址: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 跳跃 攀爬 斜坡的更多相关文章
- Unity手游之路<十>自动寻路Navmesh之跳跃,攀爬,斜坡
http://blog.csdn.net/janeky/article/details/17598113 在之前的几篇Blog总,我们已经系统学习了自动寻路插件Navmesh的相关概念和细节.然而,如 ...
- Unity 自动寻路Navmesh之跳跃,攀爬,斜坡
在之前的几篇Blog总,我们已经系统学习了自动寻路插件Navmesh的相关概念和细节.然而,如果要做一个场景精美的手游,需要用到各种复杂的场景地形,而不仅仅是平地上的自动寻路.今天我们将通过一个完整的 ...
- Unity手游之路<七>角色控制器
Unity手游之路<七>角色控制器 我们要控制角色的移动,可以全部细节都由自己来实现.控制角色模型的移动,同时移动摄影机,改变视角.当然Unity也提供了一些组件,可以让我们做更少的工作, ...
- 知物由学|游戏开发者如何从容应对Unity手游风险?
本文由 网易云发布. "知物由学"是网易云易盾打造的一个品牌栏目,词语出自汉·王充<论衡·实知>.人,能力有高下之分,学习才知道事物的道理,而后才有智慧,不去求问就不 ...
- Unity手游引擎安全解析及实践
近日,由Unity主办的"Unity技术开放日"在广州成功举办,网易移动安全技术专家卓辉作为特邀嘉宾同现场400名游戏开发者分享了网易在手游安全所积累的经验.当下,很多手游背后都存 ...
- Unity手游之路<八>自动寻路Navmesh之入门
http://blog.csdn.net/janeky/article/details/17457533 在的大部分mmo游戏都有了自动寻路功能.点击场景上的一个位置,角色就会自动寻路过去.中间可能会 ...
- Unity手游之路自动寻路Navmesh之入门
http://blog.csdn.net/janeky/article/details/17457533 现在的大部分mmo游戏都有了自动寻路功能.点击场景上的一个位置,角色就会自动寻路过去.中间可能 ...
- Unity手游之路<二>Java版服务端使用protostuff简化protobuf开发
http://blog.csdn.net/janeky/article/details/17151465 开发一款网络游戏,首先要考虑的是客户端服务端之间用何种编码格式进行通信.之前我们介绍了Unit ...
- Unity手游之路<十三>手游代码更新策略探讨
http://blog.csdn.net/janeky/article/details/25923151 这几个月公司项目非常忙,加上家里事情也多,所以blog更新一直搁置了.最近在项目开发上线过程中 ...
随机推荐
- ThinkPHP中的模型二
ThinkPHP中的模型 1.为什么要创建数据对象 案例:使用ThinkPHP完成部门管理 ① 设计数据库 ② 创建Dept控制器 路径:./Application/Admin/Controller创 ...
- hdu 1874 畅通工程续 Dijkstra
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1874 题目分析:输入起点和终点,顶点的个数,已连通的边. 输出起点到终点的最短路径,若不存在,输出-1 ...
- 使用notepad++编辑器
使用notepad++编辑器 在公司时经常要用到文本编辑器去写jsp文件,之前使用的是sublime text 3,但是觉得不太顺手,于是转用notepad++编辑器. 这个编辑器最吸引我的地方是层次 ...
- Tomcat找不到service.bat文件
说明:我们给客户做安装包,Tomcat我们设置了编码和端口,所以用绿色版的,同时又要注册成windows服务.但是bin下面没有service.bat文件(tomcat6.exe,tomcat6x.e ...
- mysql颠覆实战笔记(五)--商品系统设计(二):定时更新商品总点击量
继续回到沈老师的MYSQL颠覆实战,首先回顾下上一节课的内容,请大家会看下上节课写的存储过程. 打开prod_clicklog表, 我们只要把日期(不含时分秒)的部分存在数据库中, 如果同一日期有相同 ...
- Ubantu 安装 LAMP环境
1.通过 apt-get update 命令从ubantu软件源中下载可安装软件的列表. 2.通过一条命令可以一次性的安装LAMP apt-get install apache2 php5 mysql ...
- java中对集合对象list的几种循环访问
java中对集合对象list的几种循环访问的总结如下 1 经典的for循环 public static void main(String[] args) { List<String> li ...
- 配置普通用户可以运行saltstack的模块
client_acl 配置使用 1.概述:开启对系统上非root的系统用户在master上可以执行特殊的模块.这些模块名可以使用正则表达式来表示,不能指定对哪些minion执行命令.执行命令只需要切换 ...
- ROS
1 SSH 为什么我用 ssh 用户名 就不行,用 ssh xxx.xxx.xxx.xxx -l 用户名 就可以了呢 2 SCP 传送文件到另一个IP 用法: scp xxx root@xx.x ...
- 13个SQL优化技巧
避免无计划的全表扫描<!--?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" ...