如何使用Unity做游戏中的寻路导航
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用Unity做游戏中的寻路导航相关的知识,希望对你有一定的参考价值。
参考技术A 现在的大部分mmo游戏都有了自动寻路功能。点击场景上的一个位置,角色就会自动寻路过去。中间可能会有很多的障碍物,角色会自动绕过障碍物,最终达到终点。使用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做的bake
NavMeshAgent设定
为了告诉NavMeshAgent导航我做了以下指令:
1.注意听取鼠标输入
2.把鼠标放进屏幕空间
3.把屏幕空间转变成来自摄像头的一束光
4.在光达到地面的时候把它移除
5.把NavMeshAgent的目的地设定到地板的对应位置。
C#代码是这样的:
可视化视图下的目的地与路径
这就解决了我这款游戏的大多数导航需求,唯一的例外就是导航网格由于游戏内的一些活动而发生改变的时候。比如第一个房间的们最开始是关闭的,后来当它打开的时候,当行网格需要更新反映此次变化,允许玩家从新开的们中走过去。我并没有在游戏运行的时候rebake完整的静态导航网格,而是使用了NavMeshObstacle组件,该组件可以让你把寻路过程中的动态物体加进去,如果物体移动,Unity的寻路算法就会根据实际情况而更新。
导航路径会根据NavMeshObstacle的变化而自动发生改变
可视化视图
所以,游戏寻路导航就这么做好了,这就是《Arrival in Hell》游戏中的导航工作原理,这一些只需要Unity内自带的导航功能就可以完成了。
Unity导航
在如今当下的互联网时代背景下,Unity3D作为游戏开发的主要工具,为我们游戏开发提供了诸多便利.
说到游戏我们不得不提到一个不可或缺的组成部分:导航系统;
NavMesh(导航网格)是3D游戏世界中用于实现动态物体自动寻路的一种技术,从而实现游戏自动寻路的功效.
那我们今天就来搭建一个简单的自动寻路的导航系统:
首先:我们要有一个可以供我们游戏物体移动的游戏场景,如:一个Plane或者一个Scene;
这里,作者使用了一个简单的塔防场景;
游戏场景设置好了,我们下一步做什么呢?
找到检视窗口点击Navigation烘焙路径; 当然我们这里要将我们要烘焙的游戏物体呢,设置为Static;当然不需要有路径的地方就不需要设置Static了;
设置好了以后一定要记得点击下方的Bake烘焙;
第二步: 给我们的player设置一个StartPoint(起始点)和EndPoint(目标点);
这里使用空物体来创建起始点和目标点,并为了方便查看给它们添加标记;
好了,起始点和目标点设置好了,那我们就要对player进行操作了;
因为我们这里要让player在行进的途中有行走操作,所以我们要用到动画; 动画还记得吗? idle,walk
第三步:拖一个player模型到参差视图中,添加Nav Mesh Agent组件,并为它设置Animator中的Controller(动画状态机);
双击打开进行编辑状态机; 将idle状态和walk状态拖到状态机里面,并设置idle为最初始状态 ,并设置状态参数,这里我们使用的是int;
然后将我们设置好的状态机设置给我们的Player;
第四步:添加让player移动和播放动画的脚本;然后将player拖成预设体,将原来层次视图中的player删除;
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEngine.AI; 5 6 public class PlayerMoveScript : MonoBehaviour { 7 8 private Animator playerAnimator; 9 private NavMeshAgent agent; // 用来接收获得的组件 10 private float time; // 计时 11 private int index; 12 private int move; // 用来转换哈希值 13 14 // public Transform[] targetPoint; 15 public Transform endPoint; // 设置目的地 16 17 void Start () { 18 // 获得自动寻路的组件 19 agent = GetComponent<NavMeshAgent> (); 20 // 获得动画的组件 21 playerAnimator = GetComponent<Animator> (); 22 //转换哈希值 23 move = Animator.StringToHash ("Move"); 24 25 } 26 27 void Update () { 28 29 time+=Time.deltaTime; 30 // 一开始有1秒的idle时间 31 if (time >= 1.0f) { 32 // 如果开始生成的时候处于idle状态 33 if (playerAnimator.GetCurrentAnimatorStateInfo (0).shortNameHash == Animator.StringToHash ("idle")) { 34 //设置状态参数,并开始移动的动画; 35 playerAnimator.SetInteger (move,1); 36 // 设置目标点 37 agent.destination = endPoint.position; 38 } 39 } 40 //如果起始点和终止点的位置距离为0 41 if (agent.remainingDistance == 0) { 42 //并且现在的游戏物体处于walk状态 43 if (playerAnimator.GetCurrentAnimatorStateInfo (0).shortNameHash == Animator.StringToHash ("walk")) { 44 //那么执行下一步动画操作,设置状态参数 45 playerAnimator.SetInteger (move, 0); 46 //删除游戏物体 47 Destroy (gameObject); 48 } 49 } 50 } 51 52 }
这样player的一切组件和脚本准备就绪,那么下一步我们是不是就要生成player!(因为我们player是预设体);
第五步:生成player(在StartPiont位置生成);建立一个空物体来放置脚本
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 public class CreatPlayerScript : MonoBehaviour { 6 7 public GameObject PlayerPerfabs; // 预设体 8 public Transform startPoint; // 开始生成预设体的位置 9 public Transform endPoint; //目标位置 10 11 void Start () { 12 13 } 14 15 void Update () { 16 //按下鼠标左键做什么事情 17 if (Input.GetMouseButtonDown (0)) { 18 //在startPoint位置生成预设体,并让预设体不能旋转; 19 GameObject Player = Instantiate (PlayerPerfabs, startPoint.position, Quaternion.identity)as GameObject; 20 // 设置目标点 21 Player.GetComponent<PlayerMoveScript> ().endPoint = endPoint; 22 } 23 24 } 25 }
然后将我们外联进行设置;
我们可以创建一个Camera给我们的player,设置好位置,就可以去Game场景去查看我们的效果了.
好了,一个简单的导航就实现了,当然了小伙伴们可以去试试给游戏场景添加一个MiniMap(小地图);
注意:我们在做小地图的时候呢,为了不占用过多资源(就是优化嘛);可以将我们的小地图的Camera的渲染层数减少;进行一个优化
以上是关于如何使用Unity做游戏中的寻路导航的主要内容,如果未能解决你的问题,请参考以下文章