GameDesign 板


LINE

网页版 https://yekdniwue.blogspot.com/2020/07/AutoBuildNavMesh.html Build Static Navigation Mesh in World Composition 简介 先前有提到因为地图被细切成很多张的关系,想要使用static nav mesh, 有几个困难处要解决: 1. 地图范围很大的时候,是没办法一次载入所有地图, 只按一次build path就完成的。 不仅会执行很久,也会遇到build path失败的情况。 失败会有警告讯息并且有部分nav mesh不完整。 2. 如果想要每张地图各别计算,要在编辑器内重复的 读取子地图 build path 子地图存档 卸载子地图 这样的流程其实更适合用自动化来做。 名词与缩写说明 在开始之前,先介绍一些本篇文章会用到的名词或是缩写。 NBV: Navigation Bounds Volume,用来定义navigation mesh的范围。 P-Level: Persistent Level。在本篇指的是在World Composition模式下的主地图。 Sublevel: 在本篇指的是在World Composition模式下的各个子地图, 可能是透过tiled height map汇入进来的。 前置准备 要能够执行期间读取/卸载存在子地图的静态nav mesh资料,需要以下步骤。 我试过很多方法,下面的步骤缺一不可。 这些步骤都是在开启P-Level的模式下运作。 1. 放置一个NBV在P-Level中,可以不需要跟任何东西交集。 2. 选择P-Level内自动产生的RecastNavMesh Actor。 3. Runtime Generation 设为Static。 4. Fixed Tile Pool size设为true。 5. Tile Pool size有可能需要随着地形大小调大。 6. 在每个子地图放置需要的NBV。 以上的步骤完成之後,就可以手动对一个子地图build path再执行, 确认是不是能够真的动态载入。 如果能够动态载入/卸载,就代表成功了。 要严谨一点的话,最好存档後重开编辑器再测试, 最严谨的话,甚至要package测试。 基本上我在实验的过程中各种情况都遇到了,例如: 只有编辑器预览正常 或是编辑器重启後不正常 或是package出来才不正常... 但是没有关系,只要照着上面的步骤作,应该是不会有问题的。 注意事项 World Composition模式下的Static Nav Mesh, 似乎只能在主地图开启的模式下build path。 如果你直接打开子地图Build path再存档的话, 这个子地图的nav mesh反而不会被即时载入。 因为nav mesh直接被存进子地图的RecastNavMesh这个actor内了。 但是在world composition模式主要的RecastNavMesh是放在P-Level内。 UE4可能不支援多个RecastNavMesh actor,所以会有无法载入的问题。 这个细节不一定每个团队人员都会知道,所以会造成开发上的麻烦。 例如可能某个人只单独开启子地图编辑场景, 修改场景内容的同时也按了build path并存档。 (又或是他的editor设定为自动更新navigation) 这样一作下去这块地图的nav mesh就坏了,如图所示。 [图] 所以引入Commandlet自动化的话,比较不会有这样的疑虑。 NavMesh的部分就一律交给机器更新。 Commandlet Construction Commandlet可以开启无使用者介面的UE4 editor, 并执行Commandlet内撰写的流程。 这次的目标是开启P-Level,依序载入子地图,计算路径後对子地图存档。 所以我们需要继承已经有读档/存档能力的UResavePackagesCommandlet。 不过因为Commandlet是Editor用,直接创在专案内会影响打包流程。 可能会有编译错误的问题,所以制作成Plugin会比较好管理。 创造新的Commandlet流程 1. 在Editor创一个Blank Plugin。(不用是Editor Plugin) 2. 修改.uplugin档Modules内的参数 "Type": "Editor"以及"LoadingPhase": "Default" 3. 修改Build.cs档PrivateDependencyModuleNames内的参数 新增"UnrealEd"来开启相关的功能 4. 在Plugin内新增C++ class 并继承UResavePackagesCommandlet 5. Override PerformAdditionalOperations,需要实作的行为写在这里 [图] Modify .uplugin. [图] Add UnrealEd into build.cs in plugin. ResavePackagesCommandlet介绍 在这次的环境,我们需要ResavePackagesCommandlet内建的几个重要功能, 包含 1. InitializeResaveParameters 2. LoadAndSaveOnePackage 3. PerformAdditionalOperations 4. CheckoutFile InitializeResaveParameters 我们需要InitializeResaveParameters来解析输入参数,主要是要撷取Map参数, 让後续的LoadAndSaveOnePackage使用。 LoadAndSaveOnePackage LoadAndSaveOnePackage算是ResavePackagesCommandlet的主函式。 主要内容就是读档,作事情(存档,算Lighting资讯等等) 与上传(负责与source control沟通)。 PerformAdditionalOperations 因为LoadAndSaveOnePackage实作了太多事情, 我找到PerformAdditionalOperations有提供virtual可以实作, 除了会把读地图档建立好的World传进来。 函式本身的程式码也非常具有参考价值,尤其是Setup the World的部分, 充分说明了在Commandlet里面要如何读一个地图档并且建立出正确的World, 如同Editor中一样。 CheckoutFile 因为我们是使用Perforce作版本控管,所以还多需要CheckoutFile的功能。 Commandlet Implementation 这边还可以分为几个部分: Build Navigation必要的程式码 正确的读取子地图必要的程式码 如果是要使用Commandlet计算静态场景(非World Composition)的路径, 就不用了解後者 。 只需要参考Build Navigation必要的程式码就好。 Build Navigation必要的程式码 需要Build Navigation总共需要呼叫两个函式: FNavigationSystem::AddNavigationSystemToWorld NavigationSystem::Build 呼叫AddNavigationSystemToWorld的原因,是因为我们需要建立 MainNavData。 call stack 大概是 AddNavigationSystemToWorld InitializeForworld ProcessRegisterationCandidates 如果没有呼叫这行,後续要执行NavigationSystem::Build的时候, 会因为无资料而直接结束Build流程。 NavigationSystem::Build 呼叫Navigation build nav mesh的最主要函式,所有的资料 都要准备齐全才会有正确的结果。 我有追查到在编辑器点选Build Path其实是会呼叫 FEditorBuildUtils::EditorBuild 不过在Commandlet使用会因为GUnrealEd是null而当机,所以不能直接呼叫。 正确的读取子地图必要的程式码 Load Sublevel Initialize Sublevel Save Sublevel Unload Sublevel CollectGarbage Load Sublevel 首先我们有的是P-Level的UWorld*,从 World->WorldComposition->TilesStreaming 可以拿到子地图的资讯。 利用GetWorldAssetPackageName可以查到子地图的完整路径, 再透过LoadPackage读入记忆体。 Initialize Sublevel 此时P-Level跟读入的子地图还是没有关连的 所以要透过AddStreamingLevel建立P-Level跟子地图的关系。 然後子地图要呼叫一连串的函式,设定状态,再透过FlushLevelStreaming更新。 但是只有这样还不够,我发现子地图其实是以原点为中心储存的, Engine会读取Tile资讯的Absolute position, 然後呼叫ApplyWorldOffset将地图内所有actor的座标更新。 Navigation mesh才会计算到对的位置。 Tile的Absolute position的抓取可看下面的程式码 TArray<FWorldCompositionTile>& tileList = worldComposition->GetTilesList(); TArray<ULevelStreaming*> tilesStreaming = worldComposition->TilesStreaming; for (int32 index = 0; index < tilesStreaming.Num(); ++index) { auto perLevelStreaming = tilesStreaming[index]; auto tileInfo = tileList[index].Info; FIntVector levelOffset = tileInfo.AbsolutePosition; } 在设置的最後一个步骤记得要呼叫EditorLevelUtils::SetLevelVisibility, 宣告Sublevel的显示状态visible。 这样navigation build的时候才抓的到SubLevel的NBV。 详细的程式码可看图。 [图] Initialize sublevel. Save Sublevel 在储存Sublevel之前,要先还原刚刚为了正确计算nav mesh的位移操作。 所以要呼叫一次ApplyWorldOffset,但是这次要乘上-1。 储存相关的程式码请参考图。 [图] Save sublevel. Unload Sublevel Unload 相对简单,子地图设MarkPendingKill,P-Level移除StreamingLevel与 RemoveFromWorld就可以了。如图所示。 [图] Unload sublevel. CollectGarbage 为了避免持续读取地图造成记忆体不足,每处理完一个子地图就呼叫 CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS) 可以释放记忆体。 整合以上重点项目後的最後流程 1. AddNavigationSystemToWorld 2. For each Sublevel Load Sublevel Initialize Sublevel NavigationSystem::Build Save Sublevel Unload Sublevel CollectGarbage 结论与未来工作 结论 藉由这次的项目,我学到很多新的观念,包含了解navigation mesh, commandlet,package操作,world composition等系统。 navigation mesh相关的部份,我得知了navigation的build到底是如何运作的; 需要哪些资讯才能正确build path; 程式码在哪边;nav mesh的储存规则;子地图的offset机制。 commandlet则是学到如何在commandlet模式读取一个地图档; 根据地图档产生UWorld;了解ResavePackage执行的项目; 为了能存档读档,UPackage、ULevel、ULevelStreaming等资料型态也概略的看过,稍微 分辨得出差异。 World Composition系统虽然很大,从子地图资料的抽取; 子地图载入/卸载;如何获得Tile资讯,也都包含在这次的研究范围。 未来工作 以目前的版本来说,已经达到我原先想作的目标了, 不过我依然有注意到一些项目是可以再进一步改进的。 首先就是这个Commandlet储存出来的地图, 在World Composition预览会变成预设图。 而原来在编辑器操作并储存的版本预览图会是正确的。 日後如果真的要使用这套流程的话应该要修正。 commandlet如果遇到checkout失败要怎麽办,也是可以再延伸的课题。 另外一个重要的项目就是,大型场景不会那麽乾净,只有一张地形档, 一次只需要计算一个Sublevel。 实际上可能会再细分企划场景(内含有碰撞会影响nav mesh的Actor), 建筑物,零碎物件等等。 但是在计算nav mesh的时候要将这些地图一起纳入再算,才会是正确的结果。 所以除了开发之前要制定良好地图资料夹规范以外, commandlet也要随着这个规范稍作修改。 要能支援多阶层的地图结构,并且某一个阶层下的地图会把所有subLevel读取进来, 统一build。 举例来说,地图结构可能如图所描述: [图] 这时候navigation mesh应该是存於X0_Y0以及X0_Y1内,计算两次就好。 最後,其实这次对World Composition的了解还不够深入; 包含调整地图读取的优先顺序; 如何确保地形载入後再spawn玩家;Sublevel LOD的产生与影响。 都是还没了解的部份,有实际需求的话是要优先研究的。 --



※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 59.120.146.90 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/GameDesign/M.1595387022.A.63E.html
1F:推 metallican: 每次都很期待大大的文章 感谢 受教了 07/22 11:38
2F:推 damody: 赞!感觉下一步要做自动生地形了 07/22 21:55
3F:推 coolrobin: 只能推了 07/23 21:53







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:Tech_Job站内搜寻

TOP