开放大世界的全局寻路

news2024/9/22 15:28:27

        开放大世界的寻路一直是很困扰我的一个点,地图大、还是动态可变的,所以寻路会有很多要求。就我们项目来讲,有这几个要求:

  •  能满足极大范围的地图;
  •  地图寻路数据能实时构建,且重建代价很小;
  •  寻路的效率要极高,客户端服务器都要大量计算;
  •  寻路的路径客户端可预测;

        根据上面的需求,像 NavMesh、JPS+ 虽然效率高,但是重建代价大;用 A* 重建代价小但是效率也太差了;而 FlowField 又不支持多目标寻路。最后,我根据项目本身的需求,设计了一套适用于航海主题的海洋+岛屿的寻路解决方案。

        这个方案能够满足项目要求,且只有一个 O(1) 的复杂度,还可以分布寻路,地图的重建开销也很低。当然,这里对岛屿、地图设计做了一些限制,同时是针对航海特化的方案。

1、名词说明

        地图数据上,采用格子来构建地图数据,可以参考 开放大世界的数据管理-CSDN博客 ,对寻路来讲,较为核心的有以下几个:

  •  Cell : 寻路的最小单位(也就是一艘船的占地)。
  •  Chunk:最小的岛屿,也是一个Block的最小单位。
  •  Block :寻路块,等价于一个岛屿,会包含岛上的阻挡、寻路等信息。
  •  AOI Block : AOI 块,用于服务器、客户端的数据同步(对寻路本身作用不大,但是会影响地图设计)。

        在岛屿设计时,不允许任何一个岛屿跨越两个AOI Block,这样能客户端只需要加载有限的几个AOI块就能保证寻路正确。

2、寻路步骤

        以下面的地图举例:

        上图中最小的格子是一个Chunk (最小岛屿),其中橙黄色的部分是岛屿(一个寻路的Block),有大有小。玩家需要从A点(红色块)移动到B点(绿色块),其寻路部分如下:

2.1、两点直线并栅格化

        起点和终点直接做直线,作为初始的两个点。
        之后将直线进行栅格化,找出途经的所有Chunk点,然后将这些Chunk点所在的岛屿进行查询,看是否属于某个岛屿。

        如上图灰色块即为栅格化的Chunk路径,黄色块即为各个岛屿的入口、出口。
        之后计算直线与岛屿的交点,计算入口和出口。判断如果存在出口,即离开了这个岛屿,就可以采用流场寻路。

2.2、穿岛/出岛,流场寻路

        航海大世界的岛屿有一个强制规则:

        所有的岛屿最外面的一圈Cell都是可行走区域(海面)。

        这种设计,保证不会出现绕过岛屿的情况,且所有岛屿都可以穿越。在穿越岛屿时,采用流场(FlowField)进行处理,如下图所示:

        上图是从下方往上走时的流场 G 值表,表示到达目标点的代价。在寻路时,先往 G 值小的地方走,当走到 G 值为0的地方则表示已经到达边缘,然后将边缘出口点作为起点,继续向终点拉直线,寻找下一个穿过的岛屿。

        在实际的数据处理中,不会保存G值,而是会预处理出每个Cell附近G值最小的格子。因此在 rumtime 寻路的时候直接根据预生成的数据,依次行走即可(不需要再进行计算)。

        流场寻路输入出口方向以及当前起点,然后根据当前起点的父节点不断遍历,直到走到无父节点的边缘。

2.3、当终点在岛内时,JPS+

        当终点在岛屿内部时,采用JPS+的方法进行计算。
        由于A*寻路过程中会产生多余的节点被放入OpenList,导致OpenList过大,性能消耗过高。 JPS算法就是对A*的一个改进。JPS事先寻找到地图中可能的跳点,之后仅使用跳点进行A*运算。而JPS+算法则是先离线生成每个格子8方向到跳点的距离,运行时可以加速计算。

        关于算法,可以参考文章:KillerAery_JPS/JPS+ 寻路算法

3、方案特点

        此方案除了效率高之外,还有以下特点:

  • 可以分段寻路,即不用建立所有的路径点仍然可以行走

  • 迁城不影响大的路径:因为一个城只占用一个Chunk,玩家迁城不会影响栅格化的路径,服务器只需要部分重新计算而不需要全部重算。

  • 即便不知道完整数据,客户端仍然可以预测出路线。(因为第一段路线一定是起点到终点的直线,直到遇到第一个岛)局部寻路与整体寻路结果相同。

  • 容错率较高,只要服务器能正确同步动态岛屿(只需要目标船只附近的)的信息,即便是完全没有服务器的寻路数据,客户端依然可以模拟正确的完整路径。

4、通用开放世界寻路的思考

        其实在开发过程中,就是把整个大世界进行了分块,且确保了各个分块之间能互相联通。这样,在最上层的寻路就可以直接走直线而不必关心每一个格子的阻挡。

        抽象来看,即便是非航海主题的开放大世界,也可以用这个思路来设计寻路。

《霍格沃兹之遗》的世界地图(局部)

        现有主机游戏的开放世界 3A 大作,其地图可以明显看到设计过的痕迹。各个区块之间有明显的区分,这就给全局寻路创造了可能。只要在地图设计上,每个区域可以互相联通,且还有若干条大路贯穿整个地图,就能很方便地实现全局寻路。

        对于区域而言,其邻接的区域是有限的几个,可以提前算好连接路线。而在地图内部,可以直接引导寻路至道路上,然后沿着道路前进就行了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2090745.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

地质灾害监测预警系统的作用

在地球的广阔舞台上,自然灾害如同不可预测的演员,时常上演着惊心动魄的剧目。地震的震撼、滑坡的肆虐、泥石流的咆哮,这些地质灾害不仅给人类生命财产带来巨大威胁,也考验着社会的防灾减灾能力。为了应对这一挑战,地质…

【Linux】在 bash shell 环境下,当一命令正在执行时,按下 control-Z 会?

目录 题目分析答案 题目 分析 ctrl-c: 发送 SIGINT 信号给前台进程组中的所有进程。常用于终止正在运行的程序;ctrl-z: 发送 SIGTSTP信号给前台进程组中的所有进程,常用于挂起一个进程;ctrl-d: 不是发送信…

乐城堡 JoyCastle Unity岗位笔试题

1)实现 move(GameObjct gameObject, Vector3 begin, Vector3 end, float time, bool pingpong){ } 使 gameObject 在 time 秒内,从 begin 移动到 end,若 pingpong 为 true,则在结束时 使 gameObject 在 time 秒内从 end 移动到 begin&#xf…

机器学习中的增量学习(Incremental Learning,IL)策略是什么?

机器学习中的增量学习(Incremental Learning,IL)策略是什么? 在当今快速发展的数据驱动世界中,传统的静态机器学习模型逐渐显露出局限性。随着数据量的增长和分布的变化,模型需要不断更新,以保…

opc da 服务器数据 转IEC61850项目案例

目录 1 案例说明 1 2 VFBOX网关工作原理 1 3 应用条件 2 4 查看OPC DA服务器的相关参数 2 5 配置网关采集opc da数据 4 6 用IEC61850协议转发数据 6 7 网关使用多个逻辑设备和逻辑节点的方法 9 8 在服务器上运行仰科OPC DA采集软件 10 9 案例总结 12 1 案例说明 在OPC DA服务…

使用 Vue3 Element Plus 实现el-table中的特定单元格编辑,下拉选择等

效果预览 完整代码(后面有解析) <template><div style="display: flex;align-items: center;justify-co

磁性齿轮箱市场报告:前三大厂商占有大约79.0%的市场份额

磁性齿轮箱是一种用于扭矩和速度转换的非接触式机构。它们无磨损、无摩擦、无疲劳。它们不需要润滑剂&#xff0c;并且可以针对其他机械特性&#xff08;如刚度或阻尼&#xff09;进行定制。 一、全球磁性齿轮箱行业现状与洞察 据 QYResearch 调研团队最新发布的“全球磁性齿轮…

成都高温限电:当电动汽车「无电可充」

8月末的成都&#xff0c;因为高温限电了。 近几日&#xff0c;成都市气象台连续发布了高温红色预警信号。据新华社报道&#xff0c;8月21日&#xff0c;四川电网用电负荷两次创下历史新高&#xff0c;最高达6797万千瓦&#xff0c;较去年最大用电负荷增长近13%&#xff0c;电力…

Golang | Leetcode Golang题解之第385题迷你语法分析器

题目&#xff1a; 题解&#xff1a; func deserialize(s string) *NestedInteger {index : 0var dfs func() *NestedIntegerdfs func() *NestedInteger {ni : &NestedInteger{}if s[index] [ {indexfor s[index] ! ] {ni.Add(*dfs())if s[index] , {index}}indexreturn…

HarmonyOS鸿蒙开发:在线短视频流畅切换最佳实践

简介 为了帮助开发者解决在应用中在线短视频快速切换时容易出现快速切换播放时延过长的问题&#xff0c;将提供对应场景的解决方案。 该解决方案使用&#xff1a; 视频播放框架AVPlayer和滑块视图容器Swiper进行短视频滑动轮播切换。绘制组件XComponent的Surface类型动态渲染…

挂载5T大容量外接硬盘到ubuntu

挂载5T大容量外接硬盘到ubuntu S1&#xff1a;查看硬盘 使用 $ sudo fdisk -l找到对应盘&#xff0c;例如下图所示 /dev/sdc S2: 创建分区 使用 $ sudo fdisk /dev/sdc对上硬盘进行创建分区&#xff1b;可以依次使用以下指令 m &#xff1a;查看命令&#xff1b; g &…

前端篇-html

day1: 超文本标记语言&#xff08;英语&#xff1a;HyperText Markup Language&#xff0c;简称&#xff1a;HTML&#xff09;是一种用于创建网页的标准标记语言。 作用&#xff1a;可以使用 HTML 来建立自己的 WEB 站点&#xff0c;HTML 运行在浏览器上&#xff0c;由浏览器…

基于贝叶斯优化CNN-LSTM网络的数据分类识别算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 卷积神经网络&#xff08;CNN&#xff09; 4.2 长短期记忆网络&#xff08;LSTM&#xff09; 4.3 BO-CNN-LSTM 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) B…

基于物联网的低成本便携式传感器节点用于火灾和空气污染的检测与报警

目录 摘要 引言 材料和方法 传感器节点 IoT 微控制器 颗粒物传感器 环境和气体传感器 MQTT代理 Node-Red监控平台 系统结构 数据存储 工作描述 实验结果 讨论 结论 致谢 参考文献 这篇论文的标题是《Low-cost IoT-based Portable Sensor Node for Fire and Air…

区块链媒体套餐发稿:世媒讯引领项目推广新风潮

在区块链技术迅猛发展的今天&#xff0c;越来越多的企业和项目涌现出来&#xff0c;希望通过区块链技术改变传统行业&#xff0c;并在全球范围内获得更多关注和支持。然而&#xff0c;在这个竞争激烈的市场中&#xff0c;如何快速有效地推广和传播项目变得尤为重要。选择合适的…

disk manager操作教程 如何使用Disk Manager组件 Mac如何打开ntfs格式文件

macOS系统有一个特别明显的弱点&#xff0c;即不能对NTFS格式磁盘写入数据。想要适合Mac系统使用来回转换磁盘格式又十分麻烦&#xff0c;这该怎么办呢&#xff1f;Tuxera ntfs for mac作为一款Mac完全读写软件&#xff0c;大家在安装该软件后&#xff0c;能充分使用它的磁盘管…

macos Homebrew brew 安装 下载 国内加速镜像配置 - 可彻底解决使用brew命令时github.com无法访问相关问题

macos中的brew的默认仓库是github.com &#xff0c; 由于种种原因gh的访问速度很慢或者干脆被和谐&#xff0c;所以设置一个国内的brew加速非常有必要。 masos brew国内加速镜像配置 设置方法&#xff1a; 将下面的代码放到 ~/.bash_profile 文件中&#xff08;没有就手动创建…

003.Python爬虫系列_HTTPHTTPS协议

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

uniapp小程序实现横屏手写签名

<template><view class"signBox column-me"><!-- 这个是自定义的title-可根据自己封装的title的作为调整 --><status-bar title"电子签名" :bgColor"null"></status-bar><view class"topHint">请…

x264 编码器 AArch64汇编系列:zigzag 扫描相关汇编函数

zigzag 在x264_zigzag_init函数中初始化具体的 zigzag 实现函数: 以scan_4x4为例 c 语言实现 4x4 变换块扫描:zigzag_scan_4x4_frame。#define ZIGZAG4_FRAME\ZIGDC( 0,