3D寻路系统NavMesh-客户端篇

简介: 3D寻路系统NavMesh-客户端篇

2D寻路我们一般以A*寻路为主,那么,3D游戏世界呢,NavMesh(导航网格) 是3D游戏世界中主动寻路的一种技术,如果你想让游戏人物能自动绕开障碍物到达目的地.那你就来学习下。

Navigation system由四部分组成:

导航网格(即 Navigation Mesh,缩写为 NavMesh)

是一种数据结构,用于描述游戏世界的可行走表面,并允许在游戏世界中寻找从一个可行走位置到另一个可行走位置的路径。该数据结构是从关卡几何体自动构建或烘焙的。

相关文档

导航网格代理 (NavMesh Agent)

组件可帮助您创建在朝目标移动时能够彼此避开的角色。代理使用导航网格来推断游戏世界,并知道如何避开彼此以及移动的障碍物。

相关文档

网格外链接 (Off-Mesh Link)

组件允许您合并无法使用可行走表面来表示的导航捷径。例如,跳过沟渠或围栏,或在通过门之前打开门,全都可以描述为网格外链接。

相关文档

导航网格障碍物 (NavMesh Obstacle)

组件可用于描述代理在世界中导航时应避开的移动障碍物。由物理系统控制的木桶或板条箱便是障碍物的典型例子。障碍物正在移动时,代理将尽力避开它,但是障碍物一旦变为静止状态,便会在导航网格中雕刻一个孔,从而使代理能够改变自己的路径来绕过它,或者如果静止的障碍物阻挡了路径,则代理可寻找其他不同的路线。

相关文档

接下去我们对一张现有的地图进行导航网格的制作:

构建导航网格:

原始图:

shaded:

wireframe:

选中目标地形,将Inspector面板中Static状态设置为Nav Static

2.打开Navigation窗口(Window->AI->Navigation),选择烘焙即可

以上有些参数需要了解:

参数
Agent Radius
定义代理中心与墙壁或窗台的接近程度。(间隔)
Agent Height
定义代理可以达到的空间有多低。(进坑)
Max Slope
定义代理走上坡道的陡峭程度。(爬坡)
Step Height
定义代理可以踏上的障碍物的高度。(楼梯)
Drop Height
可跳下的高度。
Jump Distance
可以跃过的距离。
Min Region Area
可剔除未连接的小型导航网格区域。表面积小于指定值的导航网格区域将被移除。

烘焙出来后,图就如下了,覆盖上了一层蓝绿色的膜:

创建导航主体:

角色,皮卡丘,:

添加组件:

创建静态障碍物:

现在给场景中添加一些静态的障碍物,看他能否绕开他们到达目的地。添加几个Cube,然后调节他们的大小,在把他们在Object栏中的Navigation Static勾选上,最后烘培一下,效果如下,凡是没有被蓝色覆盖到的都是障碍点。

加上导航轨迹

创建shader

//导航箭头
Shader "Custom/NavPathArrow"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _ScrollYSpeed("Y Scroll Speed", Range(-20, 20)) = 20
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" "RenderType"="Transparent" }
        LOD 100
        //双面渲染
        Cull Off
        //Alpha混合
        Blend SrcAlpha OneMinusSrcAlpha
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _ScrollYSpeed;
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
            fixed4 frag(v2f i) : SV_Target
            {
                fixed2 uv = i.uv;
                uv.y += _ScrollYSpeed * _Time;
                fixed4 col = tex2D(_MainTex, uv);
                return col;
            }
            ENDCG
        }
    }
}

创建材质:

创建material,Shader添加以上创建的shader,贴图添加合适的贴图即可。

创建个3D quad,然后把材质添加上去,制作成一个prefab,

创建脚本,

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class Player: MonoBehaviour
{
    private NavMeshAgent agent;
    public MeshRenderer meshRenderer;//箭头3D对象Quad
    private List<Transform> points = new List<Transform>();//路径点
    private List<MeshRenderer> lines = new List<MeshRenderer>();//显示的路径
    private Vector3[] path;
    public float xscale = 1f;//缩放比例
    public float yscale = 1f;
    private void Start()
    {
        agent = GetComponent<NavMeshAgent>();
        //箭头宽度缩放值
        xscale = meshRenderer.transform.localScale.x;
        //箭头长度缩放值
        yscale = meshRenderer.transform.localScale.y;
        meshRenderer.gameObject.SetActive(false);
    }
    [System.Obsolete]
    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit,100))
            {
                HidePath();
                Vector3 point = hit.point;
                agent.destination = hit.point;
                path = agent.path.corners;
                //线段整体y轴加高,使轨迹悬浮
                for (int i = 0; i < path.Length; i++)
                {
                    path[i] = path[i] + new Vector3(0, 0.01f, 0);//这里的y要根据地形调整,原则是不要埋到地下去为准
                }
                //绘制路径
                DrawPath();
            }
        }
    }
    //画路径
    public void DrawPath()
    {
        if (path == null || path.Length <= 1)
            return;
        for (int i = 0; i < path.Length - 1; i++)
        {
            DrawLine(path[i], path[i + 1], i);
        }
    }
    //隐藏路径
    public void HidePath()
    {
        for (int i = 0; i < lines.Count; i++)
            lines[i].gameObject.SetActive(false);
    }
    //画路径
    private void DrawLine(Vector3 start, Vector3 end, int index)
    {
        MeshRenderer mr;
        if (index >= lines.Count)
        {
            mr = Instantiate(meshRenderer);
            lines.Add(mr);
        }
        else
        {
            mr = lines[index];
        }
        var tran = mr.transform;
        var length = Vector3.Distance(start, end);
        tran.localScale = new Vector3(xscale, length, 1);
        tran.position = (start + end) / 2;
        //指向
        tran.LookAt(start);
        //旋转偏移
        tran.Rotate(90, 0, 0);
        mr.material.mainTextureScale = new Vector2(1, length * yscale);
        mr.gameObject.SetActive(true);
    }
}

然后把脚本挂载到游戏主体皮卡丘上:

最终效果如下:

总结:

虽然这个AI系统省去了我们的很多麻烦,但还是有些不方便的地方,如不能渲染出竖直方向的NavMesh,不能做到动态的Bake,不能去实现一些有趣的玩法和随机地图的生成功能,不过高级NavMesh将会完成Unity自带NavMesh未完成的使命。

目录
相关文章
|
5月前
|
缓存 网络协议 CDN
在网页请求到显示的过程中,如何优化网络通信速度?
在网页请求到显示的过程中,如何优化网络通信速度?
210 59
|
人工智能 Java 定位技术
3D寻路系统NavMesh-服务端篇
3D寻路系统NavMesh-服务端篇
173 0
|
机器学习/深度学习 传感器 算法
【具有路由 WSN 模拟器的随机方式移动】具有路由 WSN 模拟器的随机方式移动(Matlab代码实现)
【具有路由 WSN 模拟器的随机方式移动】具有路由 WSN 模拟器的随机方式移动(Matlab代码实现)
|
存储 Python
炸金花底层模拟
炸金花底层模拟
348 0
|
定位技术
巧用千寻位置GNSS软件| 数据链工作状态轻松看
在日常施工中,千寻位置GNSS软件中的「工作状态」功能,可以帮助测绘工作人员查看当前接收机所选择的数据链的作业信息和状态。本期将为各位测友们详细介绍不同工作模式的「工作状态」所呈现的信息。
巧用千寻位置GNSS软件| 数据链工作状态轻松看
|
存储 Web App开发 JavaScript
面试官:你是如何实现浏览器多标签页之间通信的?
前言 我们都知道浏览器是可以打开很多标签页的,如果每个标签页代表的是单独的一个网站,那么这些标签页之间肯定是不能通信的,如果能通信那估计我们都得凉凉。但是在很多情况下,浏览器中的很多标签页都属于某一个网站,而且这些标签页之间会使用一些相同的数据,这个时候我们就需要让这些标签页的数据都保持同步。 比如很多博客网站,点击文章列表通常是打开一个新的标签页进入文章详情页,那么如果我们在文章详情页点赞、评论等操作,而文章列表页也使用了这些数据,这个时候我们需要保持两边的数据一致,衍生出来就是详情页改了数据,需要让列表页知道。 总结来看:在某些情况下,实现多标签页之间通信是必要的!
667 0
面试官:你是如何实现浏览器多标签页之间通信的?
❤三种方式俯瞰后端数据接收❤(建议收藏)(下)
第二种方法: 第三种方法: 三种方法概览图:
128 0
❤三种方式俯瞰后端数据接收❤(建议收藏)(下)
|
Java
❤三种方式俯瞰后端数据接收❤(建议收藏)(上)
一、前言 二、正文部分 第一种方法:
122 0
❤三种方式俯瞰后端数据接收❤(建议收藏)(上)
|
移动开发 小程序 安全