unity 物体朝向某个点nav导航怎么指定人物朝向

后使用快捷导航没有帐号?
只需一步,快速开始
&加载中...
查看: 17115|回复: 10
角色控制-点击移动以及自动寻路
TA的其他好贴
马上注册,加入CGJOY,让你轻松玩转CGJOY。
才可以下载或查看,没有帐号?
本帖最后由 A你 于
17:15 编辑
角色控制-点击移动以及自动寻路(一)
之前在D3D中做的人物控制里,在空间中两点之间行走是一个比较复杂的制作过程。跟大家分享一下我的制作过程,可以一起研究改进改进。
这里面需要解决几个问题:
1.如何取得目标地点
需要通过鼠标当前位置的屏幕二维坐标,逆矩阵变换到世界空间下,做从摄像机到这个点的射线。射线与地面mesh进行碰撞检测,获得一个交点,就是目标点。
2.如何移动角色
实时得出角色的移动方向向量,在两点之间做线性插值计算,移动的同时也要播放动画。
3.两点之间有障碍怎么办
这个过程是一个比较复杂的过程,可以通过两种办法,一种是做一种转向机制,先移动监测点(我自己起的,意思是一个前进向量和一个向下的碰撞向量的假象交点),监测到碰撞了,就将角色转向,再次监测,直到碰撞不到,向当前挪一步,再重复刚才的操作,直到绕过这个障碍物;另一种是俗称的A*算法,需要预先准备好一个二维图来标记哪里可以走,哪里不可以走,并设置权值,以便于计算最短路径。
4.里的碰撞需要注意什么
射线的碰撞检测是比较耗费CPU资源的,推荐的是尽量少用,比如当做场景内物体的鼠标拾取时,对备选对象队列进行筛选后,再去逐一做检测。unity3d中也是一样,只不过没有提供像DX中提供的那种API,无法用自定义的结构去决定对哪些对象进行射线碰撞。所以要引入Layer的概念,具体的操作下节说。
角色控制-点击移动以及自动寻路(二)
针对上一节中第四点--筛选待测物体来说一下U3D中Layer的用法。
U3D中的Layer可以简单地理解为一个集合,所有的GameObject都可以设置Layer,且只能设置一个Layer。
设置方法:
1.Edit-&Project Setting-&Tags复制代码(tag和layer在同一个设置面板上)
2.前几个Builtin Layer是系统默认的,不能修改,可以在后面几个Layer中,设置Layer的名称,比如&Players&复制代码3.在Hierarchy面板中选择GameObject,在Inspector面板上面的Layer下拉框中选择刚才设置好的Layer
从此,该GameObject就属于这个Layer集合中的一员了。
Layer的应用:LayerMask
LayerMask可以在射线碰撞过程中屏蔽掉一些GameObject。
LayerMask的方法:LayerMask.NameToLayer(&Players&) 复制代码返回该Layer的编号
LayerMask.LayerToName(8) 返回该Layer的名称
var layerMaskPlayers:LayerMask = 1 && LayerMask.NameToLayer(&Players&);
var layerMaskTerrains:LayerMask = 1 && LayerMask.NameToLayer(&Terrains&);
var FinalMask:LayerMask&&= (layerMaskPlayers.value | layerMaskTerrains.value);
var hitt : RaycastH
var ray : Ray = mainCamera.ScreenPointToRay(Input.mousePosition);
if( Physics.Raycast(ray,&&hitt, 600, FinalMask.value))
{ ... }
复制代码上面代码描述一个鼠标拾取规则,射线仅与Layer属于Players和Terrains的GameObject做碰撞。
LayerMask的使用是按位操作的,在设置新的Layer时可以看到一共只有32个,应该是因为记录Layer的变量是四个字节。LayerMask.NameToLayer方法返回的LayerId是所要移位的位数,比如在设置Layer时,Players这个Layer的ID是8,则var layerMaskPlayers:LayerMask = 1 && LayerMask.NameToLayer(&Players&);复制代码最后将所有的可能按位或操作,就得出所有需要做射线碰撞的GameObject。
如果想取“除了XXXLayer”的都显示,则可以用取反操作。
layerMaskPlayers = ~layerMaskPlayers.复制代码比如layerMaskPlayers = 1&&8;
则layerMaskPlayers = ;[/code]
在双字长的情况下,~layerMaskPlayers.value = -257
二进制表示:
角色控制-点击移动以及自动寻路(三)
为鼠标点击的位置增加一个光圈。需求如下:
鼠标右键点击地面,射线与地面的交点处出现一个光圈(纹理动画)。
该纹理动画会旋转,跟游戏中一样。
玩家移动到该光圈处,或者进入了其他状态,光圈消失。
玩家点击新的目标点,原目标点光圈消失。
光圈要随着地形的起伏改变自己的朝向,这样自然。
制作过程:
首先create一个Plane,去掉这个Plane的Collider Component,否则玩家会和光圈点发生碰撞。并为这个Plane指定一个纹理贴图,shader方式选择Particles/Alpha Blended Premultiply 颜色相乘,显得光圈亮一些。注意光圈图片必须要有Alpha通道,才能进行透明处理。
(注:另一种可选的shader方式是FX/Flare,与前面的那种不同,这个绘制出来的纹理将会一直显示在前端,相当于关闭了屏幕的深度缓冲区后再绘制。效果就是:即使在障碍物后面,也能看到它,比较不太符合现实逻辑,但是考虑到地形的起伏,光圈平面有可能会嵌入到地形里面,所以有时这个又比较合适,具体是不是有更好的办法处理这个问题,稍后再细细追究,或者大家可以提出更好的意见供参考。主要是U3D中不好确定渲染过程,不能动态操纵何时关闭屏幕深度缓冲区,其实怎么关闭屏幕的深度缓冲区也不清楚。。。)
然后在全局空GameObject(空的对象,不做任何逻辑处理,只为了挂接全局的脚本--不依附于任何Object的脚本)中增加一个鼠标相关控制脚本。加上前面所说过的改变鼠标指针UI操作,整个脚本如下:
这个脚本要完成的工作是:
通过传进来的prefab(transform类型),将光圈点放置在指定位置,并且根据射线与地形交点的面片的法线,调整这个光圈的朝向。还有一个在创建时要注意的是,将光圈的坐标沿着Y轴正向提高了一个数值,是为了避免纹理动画与地面纹理重合造成撕裂现象。
其中定义的两个方法CreateDestinationTex复制代码和DestroyDestinationTex复制代码将会在人物控制脚本中调用。
注:虽然Instantiate方法在手册中显示返回的类型是Object,但实际上是返回跟prefab同类型的一个对象,比如prefab是个transform类型,那么Instantiate返回的就是一个transform类型的对象,在Destroy一个prefeb对象时,Destroy(...) 括号里只能写Object或GameObject,如果是Transform,则会因为有其他Component依附于这个transform,而无法删除掉。
最后修改角色控制脚本,在其中获得鼠标控制脚本的对象:
private varScriptObj_cursorControl :&&Script_C
_ScriptObj_cursorControl = GameObject.Find(&GameObject_GlobalController&).GetComponent(Script_Cursor); 复制代码//获得鼠标图标脚本对象
然后在鼠标右键点击地面后,进入STATE_LERPWALK状态,同时调用鼠标控制脚本中的函数CreateDestinationTex在指定给位置创建一个光圈对象,而当不在STATE_LERPWALK状态下时,调用鼠标控制脚本中的函数DestroyDestinationTex删除该对象。
if (Input.GetMouseButtonUp (1) && _grounded)
{
复制代码......
//调用鼠标处理脚本
if(_ScriptObj_cursorControl != null)
//创建目标点光圈
_ScriptObj_cursorControl.CreateDestinationTex(_endPos,TextureOnFloor,_hit.normal);
if(_playerstate == PlayerState.STATE_LERPWALK && _grounded)
{...}
if(_ScriptObj_cursorControl != null)
_ScriptObj_cursorControl.DestroyDestinationTex(); // 删除目标点光圈
else
Debug.Log(&cursor scirpt error !&);
}
复制代码角色控制-点击移动以及自动寻路(四)
...
var _ray : R& && & //从摄像机发射到鼠标位置的射线
var _hit : RaycastH& &&&//射线与目标网格的交点数据
var _startPos : Vector3;& & //当前位置,出发点
var _endPos : Vector3;& &&&//鼠标点击位置,目标点
var _lerp :& && & //插值系数
var _deltaLerp :& &&&//插值系数递增步长
var _rotation : Q& &//从当前朝向(本地空间Z轴)转向目标方向的旋转四元数值
var _slerpRotation :& &//旋转插值系数
var _deltaSlerpRotation :&&//旋转插值系数递增步长
var _moveDirectionLerpWalk : Vector3; //鼠标点击地面后,应该移动的方向
var _movePrecision :& &&&//当前点与目标点的距离误差
private var _lastDistance:& & //暂时未制作自动寻路系统,地图上未划分网格,如果目标点玩家上不去,有可能会一直跑,玩家与目标点的距离会呈现“先缩小距离,后拉长距离”的结果,所以用此变量记录上一步的距离,当距离开始拉长时,就停下。
enum PlayerState
{
STATE_READY& &= 0,
STATE_RUN& & = 1,
STATE_IDLE& & = 2,
STATE_ATTACK& &= 3,
STATE_JUMP& & = 4,
STATE_LERPWALK = 5,//增加一个状态,标示处于右键点击地面后的移动状态
}
function Update()
//=========================================================================
//鼠标操作 - 右键按下后弹起
var layerPlayers :LayerMask = 1 && LayerMask.NameToLayer(&Players&);
//var layerTerrains :LayerMask = 1 && LayerMask.NameToLayer(&Terrains&);
//var finalLayer : LayerMask = (layerPlayers.value | layerTerrains.value);
layerPlayers = ~layerPlayers. //对Players这个Layer的数值2进制位取反,表示除了Players以外,其他Layer都可以与射线进行碰撞
if (Input.GetMouseButtonUp (1) && _grounded)
{
if(_cameraCurrent == null)
{
print(null);
_ray = _cameraCurrent.ScreenPointToRay (Input.mousePosition);
if (Physics.Raycast (_ray, _hit, Mathf.Infinity , layerPlayers.value))
{
if(_hit.collider.gameObject.tag == &Terrains&)
{
//Debug.DrawLine (_ray.origin, _hit.point);
_playerstate = PlayerState.STATE_LERPWALK;
_lerp = 0;
_startPos = transform.
var tempVec = _hit.
tempVec.y = _startPos.y; //高度拉齐,为了算出方向向量
_endPos = _hit.
print(&*****the endpos is : & + _endPos);
_lastDistance = Vector3.Distance(_startPos , _endPos);
_deltaLerp = 1/Vector3.Distance(_startPos , _endPos);
_slerpRotation = 0;
var relativePos = tempVec - _startP
_rotation = Quaternion.LookRotation(relativePos); //这个函数很方便,计算出从当前朝向转向目标方向的旋转四元数
_moveDirectionLerpWalk = relativePos.
_moveDirectionLerpWalk *= _
//调用鼠标处理脚本
if(_ScriptObj_cursorControl != null)
//创建目标点光圈
_ScriptObj_cursorControl.CreateDestinationTex(_endPos,TextureOnFloor,_hit.normal);
}
}
}
if(_playerstate == PlayerState.STATE_LERPWALK && _grounded)
{
_lerp += _deltaL
//if(_lerp &= 1)
//{
// _playerstate = PlayerState.STATE_IDLE;
// transform.rotation = _
//
//}
_slerpRotation += _deltaSlerpR
if(_slerpRotation &= 1)
{
transform.rotation = _
}
//平滑旋转人物的朝向
transform.rotation = Quaternion.Slerp(transform.rotation, _rotation, _slerpRotation);
//插值虽然计算得准,但直接用插值就无法做碰撞了,这里只用来保存坐标
//先取出移动方向向量,用Move方法移动,可以顺便做碰撞
var position = Vector3.Lerp(_startPos , _endPos , _lerp);
var _flagsLerp : CollisionFlags = _controller.Move(_moveDirectionLerpWalk * Time.deltaTime);
_animation.Play(&run&);
var distance = Vector3.Distance(transform.position,_endPos);
//以当前坐标和目标坐标的距离作为结束条件,小于误差范围值则停下
if(distance &= _movePrecision)
_playerstate = PlayerState.STATE_IDLE;
//当距离由大变小,又开始由小变大时,也要停下
else if(distance &= _lastDistance)
_playerstate = PlayerState.STATE_IDLE;
//记录上一帧中,玩家与目标点的距离
else
_lastDistance =
}
else
{
if(_ScriptObj_cursorControl != null)
_ScriptObj_cursorControl.DestroyDestinationTex(); // 删除目标点光圈
else
Debug.Log(&cursor scirpt error !&);
}
//=========================================================================
}
复制代码总结:右键点击地面,实现上要结合上一篇的内容,点击地面后在地面上创建一个与点击到的三角形朝向相同的平板,上面贴上一个纹理,通过旋转这个平板实现纹理的旋转动画
如何让别人关注你?
虽然看不懂,但一看就是好东西
本楼回复(<span id="dp_count_)
如何让别人关注你?
没工程么?真难得看
本楼回复(<span id="dp_count_)
如何让别人关注你?
文字比较多,先收藏了,谢谢分享
本楼回复(<span id="dp_count_)
如何让别人关注你?
{:soso_e160:}
本楼回复(<span id="dp_count_)
如何让别人关注你?
本楼回复(<span id="dp_count_)
如何让别人关注你?
先标记一下,回头细看
本楼回复(<span id="dp_count_)
如何让别人关注你?
标记标记&&谢谢楼主分享&&{:7_236:}
本楼回复(<span id="dp_count_)
如何让别人关注你?
本楼回复(<span id="dp_count_)
如何让别人关注你?
谢谢分享。
本楼回复(<span id="dp_count_)
如何让别人关注你?
Powered byUnity Runtime NavMesh(运行时导航)总结,实现AI可以导航到墙壁(天花板)上 - l的博客 - CSDN博客
Unity Runtime NavMesh(运行时导航)总结,实现AI可以导航到墙壁(天花板)上
*要求版本5.6以上。
官方教程:
使用插件:
实时导航另一个实例:
  在5.6版本之前,Unity自带的导航烘培场景需要在编辑时烘培(非运行时)。在5.6及其以后版本就可以实现运行时烘培场景,实现实时导航。而且同时可以使用多种导航代理模型(Agent。即不同宽高会有不同的烘培场景。)
机器人从墙面导航到天花板上。
编辑时场景。未烘培,游戏开始时,调用NavMeshSurface.BuildNavMesh() 来烘培场景。
主要的四个组件
NavMeshSurface
导航面。用来烘培场景。
NavMeshLink
导航面连接。Off Mesh Link加强版,连接两个可导航面。
NavMeshModifier
导航更改。就是可以修改部分特殊的导航物体。(是否可以在其上面导航,指定物体可以导航等。)
NavMeshModifierVolume
同上。不过是指定一个区域。
Agent Type
导航代理类型(不同宽高、移动旋转速度、爬坡角度),在Navigation窗口设置。
Collect Objects
从哪些物体上烘培导航场景。下面详解。
Include Layers
包含哪些的层级。
Use Geometry
使用哪种网格来烘培场景。1.Render Meshs:有MeshRenderer组件的。2.Physics Colliders:有物理Collider组件的。
  点击Bake烘培场景。如果要运行时烘培场景,调用其方法:BuildNavMesh()。更新烘培场景:UpdateNavMesh(NavMeshData data)。
Collect Objects
  场景中有所物体都会拿来烘培,不管物体在哪里,在哪个层级。如下图。一个外部立方体上面是可以导航的。
2. Children
  只有自己及其子层级的物体才会加入烘培。如下图,立方体不是自己的孩子时。
  指定区域内才会烘培(不管是不是自己的孩子)。如下图,区域内切割了立方体。
  通过两点连接两个面(控制Width可以形成类似桥面的连接面)。这两个面都必须可以导航得到(淡蓝色表面),连接成功会预览得到白色的桥面。
(地面和墙面连接,导航就可以从地面导航到墙面了)
(对应下图右桥)
- Ignore From Build : 烘培时是否无视他(不加入烘培)。
- Override Area : 重写该区域(是否可以走,是否要跳过)。
- Affected Agents :这些设置影响到的导航代理。
(对应下图左桥)
同上,只是改变区域内物体。
(烘培后,右边的桥被无视,左边有一块区域不能走。)
*在导航到无法到达的地方时候,像教程中的循环墙面,可能会有BUG:点每个墙面上房间的墙面时,导航是错误的(结果不是导航到房间墙面旁边,而是垂直房间墙面最近的地面的点)。
建议下载插件源码(标题处链接)看样例,有更清楚的实时导航,其中用NavMeshSourceTag组件标记一个物体就可以实现实时产生可导航的物体表面(当然还要配合其他组件如:Local Nav Mesh Builder 等。)
我的热门文章独立开发者分享:如何使用Unity做游戏中的寻路导航
招聘信息:
随着手游市场的兴起和对独立开发者的支持,Unity如今已经成为业内开发者用户最多的引擎,最近,一名海外开发者在博客中分享了自己用Unity引擎重做此前研发的Flash游戏寻路导航的心得,希望可以给大家带来帮助:大家好,最近我一直都在忙于把2006年的一款Flash游戏用Unity引擎重做出来,尽管我们在《Arrival in Hell》这个项目已经工作了一年多,但这里我希望从头开始来写开发者博客,因为这样才能让读者们有比较完整的印象。如果你们不太熟悉这款游戏的话,我这里做几句话的介绍,我们在对2006年我和朋友Eduardo Mojica以及Richard Rout三人研发的一款Flash游戏进行重做,这是一款点击式操作的冒险游戏,我们将用Unity引擎进行重做。我做编程和研发游戏已经有十年左右的经验,但这是我使用Unity引擎做的首款游戏。在其他事情之前,我首先想要说的就是玩家角色的移动,由于这款游戏现在是真正的3D,因此玩家角色需要在3D空间里寻路。幸运的是,Unity引擎已经有了一些不错的内置寻路功能,你只要打开窗口-导航(Navigation),选择你想要使用的物体并且放到路径中,然后把他们标记为‘导航静态(Navigation static)’这就会告诉Unity这些物体是静态的(非移动),在寻路的时候应该被考虑进去。把物体设置为‘导航静态’这里我想要说一说这个功能有多么强大。过去,我和大多数的游戏开发者一样,都必须打造自己的寻路系统,我之前就做过一个A*tile和基于节点的寻路系统,在两种情况下,特别是基于节点系统的寻路所产生的walls让人非常头痛。在基于节点的寻路系统中,你必须手动地把AI使用的点在两者之间进行导航。Unity不仅做导航功能,还使用了导航网格(Navigation meshes),这比手动放置节点更有效率而且更流畅。更重要的是,你还可以一键重新计算整个导航网格,彻底摆脱了手动修改导航节点的做法。我用基于节点系统做的失败的寻路系统之一在把静态物体加入了导航网格之后,你可以选择一系列的设定然后点击bake按钮,比如在考虑加入一堵墙之前确定坡有多陡以及台阶应该多高。这样你就可以获得可以预览的视图。值得注意的一件事是,不要仅仅因为物体存在在场景中就意味着它是导航网格的一部分。比如说在这款游戏中,我不在乎玩家们是否会踩到瓦砾,所以我并没有把任何瓦砾标识为导航静态,这加快了当行网格的生成速度。《Arrival in Hell》中其实是有数值的在导航网格生成之后,我简单地给玩家模型增加了一个NavMeshAgent组件,这款游戏现在就可以进行寻路了,唯一剩下的就是增加鼠标输入控制NavMeshAgent的目的地。用NavMesh做的bakeNavMeshAgent设定为了告诉NavMeshAgent导航我做了以下指令:1.注意听取鼠标输入2.把鼠标放进屏幕空间3.把屏幕空间转变成来自摄像头的一束光4.在光达到地面的时候把它移除5.把NavMeshAgent的目的地设定到地板的对应位置。C#代码是这样的:点击设置NavMeshAgent的目的地可视化视图下的目的地与路径这就解决了我这款游戏的大多数导航需求,唯一的例外就是导航网格由于游戏内的一些活动而发生改变的时候。比如第一个房间的们最开始是关闭的,后来当它打开的时候,当行网格需要更新反映此次变化,允许玩家从新开的们中走过去。我并没有在游戏运行的时候rebake完整的静态导航网格,而是使用了NavMeshObstacle组件,该组件可以让你把寻路过程中的动态物体加进去,如果物体移动,Unity的寻路算法就会根据实际情况而更新。导航路径会根据NavMeshObstacle的变化而自动发生改变可视化视图所以,游戏寻路导航就这么做好了,这就是《Arrival in Hell》游戏中的导航工作原理,这一些只需要Unity内自带的导航功能就可以完成了。
微信扫一扫
订阅每日移动开发及APP推广热点资讯公众号:CocoaChina
您还没有登录!请或
点击量5032点击量4026点击量3713点击量3652点击量3251点击量3237点击量3231点击量3195点击量3116
&2016 Chukong Technologies,Inc.
京公网安备89求助怎么控制一个对象的某个轴的朝向?
我做一个类似切水果的游戏,比如滑动的时候碰到一个对象,就实现一个刀光,然后这个刀光的一头要朝向鼠标松开时的那个点,直接使用LookAt的话,只有对象的Y轴才会朝向,其它轴都不会改变,所以无法达到这个效果...希望有大神赐教!
要评论请先&或者&
我试了一下,用lookat不论对象怎么转都会面向相机的。满足你的需求
:我试了一下,用lookat不论对象怎么转都会面向相机的。满足你的需求 转向面向相机怎么满足我的要求?正因为用lookat会转向面向相机所以无法达成这样要求啊0 0
:我试了一下,用lookat不论对象怎么转都会面向相机的。满足你的需求 我想要的是假设鼠标在屏幕上A点down,然后在B点up,这个时候这两个点的坐标我都已经存储起来了,up的时候实例化一个刀光,而刀光是一张长条形的图片,我需要让这张图片从A点朝向B点,是我任意划动都会按A~B点这个朝向,所以使用lookat是做不到的,因为lookat并不会改变Z轴的旋转
你把刀光的图也lookat相机“一次”,不就和对象保持一直了,另外单独旋转刀光的z轴
:你把刀光的图也lookat相机“一次”,不就和对象保持一直了,另外单独旋转刀光的z轴 其它只要单独旋转刀光的Z轴就可以了,但是却没办法旋转啊!因为无法得知鼠标点的那个位置需要旋转多少度...又是动态的,只能根据鼠标点的那个位置和实例他的那个位置来计算Z轴旋转值
鼠标底下挂一个物体跟随的物体LOOKAT那个物体、}

我要回帖

更多关于 unity 朝向目标移动 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信