首页 > 其他分享 >3D寻路系统NavMesh-客户端篇

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

时间:2022-12-27 20:05:02浏览次数:66  
标签:障碍物 NavMesh uv 网格 path 3D 导航 客户端


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

Navigation system由四部分组成:

导航网格(即 Navigation Mesh,缩写为 NavMesh)
是一种数据结构,用于描述游戏世界的可行走表面,并允许在游戏世界中寻找从一个可行走位置到另一个可行走位置的路径。该数据结构是从关卡几何体自动构建或烘焙的。
​​​相关文档​

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

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

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

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

构建导航网格:

原始图:

shaded:

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

wireframe:

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

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

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


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

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


以上有些参数需要了解:

参数

Agent Radius
定义代理中心与墙壁或窗台的接近程度。(间隔)

Agent Height
定义代理可以达到的空间有多低。(进坑)

Max Slope
定义代理走上坡道的陡峭程度。(爬坡)

Step Height
定义代理可以踏上的障碍物的高度。(楼梯)

Drop Height
可跳下的高度。

Jump Distance
可以跃过的距离。

Min Region Area
可剔除未连接的小型导航网格区域。表面积小于指定值的导航网格区域将被移除。

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

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


创建导航主体:

角色,皮卡丘,:

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

添加组件:

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


创建静态障碍物:

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

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


加上导航轨迹

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


创建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
}
}
}

创建材质:

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


创建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);
}

}

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

最终效果如下:

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


总结:

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


标签:障碍物,NavMesh,uv,网格,path,3D,导航,客户端
From: https://blog.51cto.com/u_4176761/5973456

相关文章

  • Unity3D学习之路
    1.准备C#的开发环境VS2015, Unity3D5.5.12.准备通信协议protobuf3.3.0 具体请参考:​​Protobuf3.3使用总结​​3.引入日志系统:​​C#日志系统Log4net使用总结​......
  • Nacos1.4源码(1):客户端注册源码
    搭建环境搭建Nacos服务端环境从github上下载nacos1.4源码下来:https://github.com/alibaba/nacos下载下来,源码编译好,直接idea运行com.alibaba.nacos.Nacos类就行:那我......
  • Flutter 实现 “真” 3D 动画效果,用纯代码实现立体 Dash 和 3D Logo
    我正在参加「创意开发投稿大赛」详情请看:​​掘金创意开发大赛来了!​​本篇将给你带来更加炫酷动画效果,最后教你如何通过纯代码实现一只立体的Flutter的吉祥物Dash和3......
  • rk3588 rtk8723DU 蓝牙驱动移植
    平台:rk388 debian 系统,内核版本是:  蓝牙驱动: 支持的内核的版本是:  开始移植: 1 首先不知问什么sdk源码中的 rtl8723DU 用不了,这个驱动是我从......
  • U3D开发性能优化笔记(待增加版本.x)
    AmirFasshihi优化方案:一、遇到麻烦时要调用“垃圾回收器”(GarbageCollector,无用单元收集程序,以下简称GC)由于具有C/C++游戏编程背景,我们并不习惯无用单元收集程序的特定......
  • 客户端 连接 k8s-kafka 异常
     异常信息:java.net.UnknownHostException:kafka-0.kafka.flink-stat.svc.cluster.localatjava.net.InetAddress.getAllByName0(InetAddress.java:1287)~[?:1.8......
  • 判断网页打开浏览器类型,PC 手机端,微信浏览器,在新浪微博客户端打开
    于2021-06-1720:33:33订阅专栏//判断网页打开浏览器类型,PC手机端,微信浏览器,,,<scripttype="text/javascript">varbrowser={versions:fu......
  • 前端--3D立体魔方小游戏
    一、案列效果二、案列目录文件获取 ​​JS文件获取​​三、案列代码文件可在本文案例目录获取<!DOCTYPEhtml><html><head><metacharset="utf-8"><metaname="v......
  • Web前端--HTML+Canvas+Js实现3D魔方小游戏
    一、案列效果二、案列思路1、先将平面上的6个DIV拼接在一起。形成一张类似于3d立方体图形展开的平面图。 2、我们需要将每一个面旋转到相应的位置上,每一个面的旋转轴都是不......
  • Unity3D 渲染路径
    Unity渲染路径: Unity支持不同的渲染路径。您应具体取决于你的游戏内容和目标平台/硬件来选择使用哪一个。不同的渲染路径有不同的特点和性能特点,主要影响灯光和阴影如果图......