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更新一直搁置了.最近在项目开发上线过程中 ...
随机推荐
- OC8_代理基本概念
// // ProtectedDelegate.h // OC8_代理基本概念 // // Created by zhangxueming on 15/6/24. // Copyright (c) 2 ...
- POJ 2287 Tian Ji -- The Horse Racing(贪心)
题意:田忌和齐王有n匹马,进行n局比赛,每局比赛输者给胜者200,问田忌最多能得多少钱. 分析:如果田忌最下等的马比齐王最下等的马好,是没必要拿最下等的马和齐王最好的马比的.(最上等马同理) 因此,如 ...
- POD数据了解
Plain old data (普通旧的数据); POD 是Plain Old Data的簡寫,是指一些系統的int, char, float.指標.array之類的資料型別,這應該蠻好想像的,就是C ...
- Linux C 程序 GTK+图形界面编程(22)
GTK+图形界面编程 Linux大多是在字符界面,但也可以开发图形界面 目前已经存在多种Linux下开发图形界面的程序开发包:最常用的是Qt和GTK+ Qt是一个跨平台的图形界面开发库,不仅仅支持Li ...
- TCP协议承载的DNS报文,DNS报文首部前多出两个字节的DNS报文长度字段,是何意义?
一.TCP报文头部简介 ●源.目标端口号字段:占16比特.TCP协议通过使用"端口"来标识源端和目标端的应用进程.端口号可以使用0到65535之间的任何数字.在收到服务请求时,操作 ...
- SASS语法备忘
sass语法 关于sass 3.3.0更新说明——3.3.0 sublime相关插件为:scss语法高亮,sass语法高亮,编译,保存即编译,格式化 文件后缀名 sass有两种后缀名文件:一种后缀名为 ...
- fastclick插件 导致 input[type="date"] 无法触发问题解决方案
鄙人才疏学浅,新人一枚,不足之处还请谅解,写下这个也只是为了给大家分享一下我解决这个BUG的方法,也是自己的一个笔记. 首先,我们使用fastclick插件的初衷是解决“tap”事件“点透”的BUG: ...
- apache 多站点搭建
一.apache配置多站点方法一 1.首先修改apache httpd.conf 文件 启用虚拟主机组件功能 取消 LoadModule vhost_alias_module modules/mod_ ...
- ckeditor增加上传图片的功能
1.配置config.js开启图片上传选项卡. CKEDITOR.editorConfig = function( config ) { config.filebrowserImageUploadUr ...
- 《PHP和MySQL Web开发》精彩的地方收录
1.用SESSION来做的购物车,做成数组,用isbn对应书的数量作为二维数组保存 $new GET传值加入购物车,submit是修改数量,提交后的表单,通过历遍原来的数组,对应isbn修改最新的数量 ...