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

转载

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

分类: unity
unity3dNavmesh手游自动寻路

在之前的几篇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;  
    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

转载于:https://www.cnblogs.com/lihonglin2016/p/4361162.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/574715.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

热点分析图_通过分析功率MOSFET管的工作特性,判断其损坏原因

0 前言目前&#xff0c;功率 MOSFET管广泛地应用于开关电源系统及其它的一些功率电子电路中&#xff0c;然而&#xff0c;在实际的应用中&#xff0c;通常&#xff0c;在一些极端的边界条件下&#xff0c;如系统的输出短路及过载测试&#xff0c;输入过电压测试以及动态的老化测…

隔年增长的题_资料分析——隔年增长

【导读】中公事业单位为帮助各位考生顺利通过事业单位招聘考试&#xff01;今天为大家带来事业单位考试题库及答案&#xff1a;资料分析——隔年增长。希望可以帮助各位考生顺利备考&#xff01;在公考中&#xff0c;资料分析的重要性显得尤为重要&#xff0c;面对激烈的竞争和…

Nim博弈游戏

给定n堆石子&#xff0c;每次每人能从一堆石子中取若干个石子&#xff08;不能不取&#xff09;&#xff0c;最后不能取石子者败 对于这个游戏&#xff0c;我们要判断的是&#xff0c;给定局势下&#xff0c;先手者胜还是败 设先手胜的局势为N-postion&#xff0c;先手败的局势…

显示多个页面退出登陆_软件测试小白如何第一次登陆时给LINUX的配置网络

接着上一期分享&#xff0c;今天分享的是虚拟机配置完成以后我们接下来怎么做首先我们进入终端登录以后先显示的是user用户点击user输入密码&#xff0c;回车或者点击sigh ln第一次进入会让你选择语音&#xff0c;你可以根据自己的喜好决定如果安装的可视化界面&#xff0c;你的…

SQL Server的聚集索引和非聚集索引

微软的SQL SERVER提供了两种索引&#xff1a;聚集索引(clustered index&#xff0c;也称聚类索引、簇集索引)和非聚集索引(nonclustered index&#xff0c;也称非聚类索引、非簇集索引)……   (一)深入浅出理解索引结构   实际上&#xff0c;您可以把索引理解为一种特殊的目…

python math库函数源码_11. math库函数

一、math库介绍内置数学类函数库&#xff0c;math库不支持复数类型&#xff0c;仅支持整数和浮点数运算。math库一共提供了&#xff1a;4个数字常数44个函数&#xff0c;分为4类&#xff1a;16个数值表示函数8个幂对数函数16个三角对数函数4个高等特殊函数库中函数不能直接使用…

原理 msc_计算机网络原理梳理丨无线与移动网络

目录无线网络移动网络IEEE802.11蜂窝网络移动IP网络其它典型无线网络介绍无线网络无线网络的基本结构无线主机无线链路基站网络基础设施自组织网络(Ad Hoc网络)无线链路与无线网络特性无线链路与有线链路主要区别&#xff1a;1.信号强度的衰弱2.干扰3.多径传播4.隐藏终端移动网…

如何卸载rpm

首先通过 rpm -q <关键字> 可以查询到rpm包的名字 或者rpm -qa|grep 关键字 然后 调用 rpm -e <包的名字> 删除特定rpm包 如果遇到依赖&#xff0c;无法删除&#xff0c;使用 rpm -e --nodeps <包的名字> 不检查依赖&#xff0c;直接删除rpm包 如果恰好有多…

python实现逐步回归分析_Python实现逐步回归(stepwise regression)

逐步回归的基本思想是将变量逐个引入模型&#xff0c;每引入一个解释变量后都要进行F检验&#xff0c;并对已经选入的解释变量逐个进行t检验&#xff0c;当原来引入的解释变量由于后面解释变量的引入变得不再显著时&#xff0c;则将其删除。以确保每次引入新的变量之前回归方程…

二维数组最大子数组和

一&#xff0e;实验题目 求一个二维数组中和最大的子数组。 二&#xff0e;实验思路 基于我们第一次合作时求的一位数组最大子数组&#xff0c;加上一层循环来遍历二维数组中的所有子矩阵的情况。 第一步&#xff1a;先利用上次的方法求每一行的情况&#xff0c;将每行结果存入…

木炭怎么获得_木炭机一体化流程中制做木炭是怎么完成的

关注我们获得更多精彩内容木炭机一体化流程中制做木炭是怎么完成的&#xff0c;木炭机生产线中炭化炉是核心设备。炭化炉炭化过程决定了木炭质量和效果。木炭机生产线中炭化过程需要经历三个不同温度阶段。三个不同温度阶段对薪棒的炭化作用不同。炭化好坏标志着木炭机制炭效果…

HDU 4540

简单得不能再简单的DP了。 #include<iostream> const int inf1<<30; using namespace std; int map[22][22]; int dp[22][22];//表示前i个时刻在第j个位置时的最小消耗能量 int n,m;int main(){while(~scanf("%d%d",&n,&m)){for(int i1;i<n;i…

esp8266舵机驱动_arduino开发ESP8266学习笔记四—–舵机

arduino开发ESP8266学习笔记四—–舵机使用时发现会有ESP8266掉电的情况&#xff0c;应该是板上的稳压芯片的限流导致的&#xff0c;观测波形&#xff0c;发现当舵机运转时&#xff0c;电源线3.3V不再是稳定的3.3V&#xff0c;大概是在3.0V到3.3V范围内高频振动&#xff0c;这应…

【转载】网易将军令工作原理

最近开始玩梦幻手游&#xff0c;为了领以前端游的返利必须输入将军令&#xff0c;那个已经一年没用了&#xff0c;输入了几次都提示错误&#xff08;后来证实是系统繁忙而已&#xff09;&#xff0c;我以为是将军令时间不对了&#xff0c;所以用了下官网的修复功能。也对将军令…

返回数据_多层数据返回匹配值

↑↑↑点击上方图片&#xff0c;了解详情Access Switch函数示例&#xff0c;分享源码。一、问题描述&#xff1a;想实现如下功能&#xff0c;发现用iff嵌套方式太复杂&#xff0c;有没有更简单的方法&#xff1f; A1≤750 返回5.0 750&#xff1c;A1≤865 返回5.…

通知中心 NSNotificationCenter 的简单使用方法

NSNotificationCenter(通知中心)【注意】需再dealloc中移除观察者获取通知中心单例对象NSNotificationCenter *center[NSNotificationCenter defaultCenter]; 常用方法:1.注册观察者- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(…

solaris mysql_配置Solaris下自带的MYSQL

Solaris自带了mysql服务器&#xff0c;省事不少。用的时候只有小小的配置下就可以搞定。1.用ROOT用户登陆系统&#xff0c;在终端输入/usr/sfw/bin/mysql_install_db2.建立mysql user和 group&#xff1a;# groupadd mysql# useradd -g mysql mysql# chgrp -R mysql /var/mysql…

mysql 1786_mysql错误处理之ERROR 1786 (HY000)

ERROR 1786 (HY000)【环境描述】msyql5.6.14【报错信息】执行create table ... select的时候遇到报错&#xff1a;复制代码 代码如下:db1 [test] [23:01:58]> create tablelgmnr_bak select * from lgmnr;ERROR 1786 (HY000): CREATE TABLE ... SELECTis forbidden when GLO…

Java入门第二季

面向对象 对象&#xff1a;世间万物皆对象&#xff0c;所谓对象&#xff0c;并不是一个抽象的概念&#xff0c;泛指我们生活中的各式各样的事物都称之为对象。 类和对象的关系 类是抽象的概念&#xff0c;是对象的模板 对象是具体的事物&#xff0c;是类的具体实例 面向对象…

HDU1114 Piggy-Bank 完全背包

题意&#xff1a; 给出一个存钱罐的空罐时的质量和装了钱之后的质量&#xff0c;再给出一些硬币的质量和相应的价值&#xff0c;问存钱罐里的钱最少可能为多少。 这道题就是完全背包的问题&#xff0c;注意初始化。 完全背包与01背包不同的是第二次遍历的时候要顺序而已。 初始…