蓝图(blueprint)是一种以C++为编译目标的可视化编程语言,首次出现在虚幻引擎4中,实质上也是为了紧跟low code的时代潮流,用图论取代文本,让开发者不必再拘泥于语法导致的基本问题。图形语言最大的好处在于可以将代码中的各种树形逻辑展示出来,大大提升了开发效率。

本文的重点是用蓝图实现UE4自带的level streaming volume,这玩意翻译过来叫“场景流触发体”,在此之前需要介绍下什么是level streaming(场景流)。

Level Streaming:场景动态加载

理论基础:和LOD(level of details)始终占用内存的特性不同,场景流可以选择性的流入/流出内存,大大提升效率,尤其是那些躲在室内,从外面看不见的物体,就没必要加载它们了。场景流主要有2个好处:

  • 选择性加载场景:节省cpu/内存开销

  • 模块化分工开发:多人独立开发,最后组合起来

level(场景)本是content browser中的map类型的uasset文件,但可以在Levels窗口中将它们以层级关系联系起来,本质上是对整个项目进行组件化划分,但最常见的用途就是动态加载场景,比如:

  • 无缝地图切换:大型开放世界游戏中,人物走到哪,场景加载到哪

  • 被遮挡的物体:如在玩家到达房间门口,准备进门的时候临时加载房间内的场景

  • 可见的载入场景:一些cyberpunk主题地图或者恐怖游戏中,走着走着,环境就变了,或者一回头出现一个**

总之,场景动态载入是每一个大型3D游戏的必备。

虚幻引擎——场景动态加载_游戏开发

Tip:在无缝载入情况下,level streaming肉眼是看不到效果的(比如进入房间前),但可以根据World Outliner底部统计数据的变化来判断效果。

Level Streaming Volume:场景流触发体

所谓的Volume是一个三维的brush(BSP)几何体,看不见摸不着,却能触发碰撞,将之放在Persistent Level中然后绑定到某个sublevel,人物进入volume体内后触发sublevel中所有actor的加载。

虚幻引擎——场景动态加载_javascript_02

蓝图实现Level Streaming Volume

做web开发的时候,每次出现一个新api,在所有浏览器兼容它之前,我们总是习惯自己去实现这样一个函数,作为过渡,顺便证明自己理解了它。同理,在LevelStreamingVolume出现之前,通过蓝图或C++来实现这个Volume。

首先创建一个蓝图类,因为要实现可放置,所以蓝图类基于actor创建,同时创建一个等待载入的level:subLevel。

虚幻引擎——场景动态加载_epoll_03

然后给这个actor准备3个组件和1个变量:

  • 一个用于碰撞检测的Box Collision【看不见,摸得着】

  • 一个用于暗示触发区的半透明cube【看得见,摸不着】

  • 一行提示信息Text Render

  • 一个全局变量levelId

虚幻引擎——场景动态加载_epoll_04

其中,cube和box空间上完全重叠,cube用半透明的材质代表“可进入”,text位于它们的上方,效果如下:

虚幻引擎——场景动态加载_游戏开发_05

希望实现的效果是:当肉体进入这个橙色区域时载入levelId,离开时载出,levelId可以让用户指定,所以要开启“Expose on Spawn”选项。

虚幻引擎——场景动态加载_游戏_06

整个逻辑很简单:先对box监听onComponentBeginOverlap事件,发生重叠时判断参数otherActor是否等于玩家(getPlayerCharacter),如果相等则调用loadStreamLevel(levelId);

载出场景的逻辑与之相反,只要将对应的方法替换成onComponentEndOverlap和unloadStreamLevel即可。

场景流节流

人物进入volume的时候,2个不规则的几何体发生重叠,可能会触发好几次重叠事件,所以有必要做一个节流(throttle)减少开销。

调用loadStreamLevel之前判断一下,如果场景已经加载,则停止向下执行:我们通过getStreamingLevel(levelId)获得场景的引用,再传入isLevelLoaded判断加载状态。所以整个蓝图如下:

虚幻引擎——场景动态加载_js_07

这是载入部分,载出部分基本对称,就不放图了。自此,引擎自带的Level Streaming Volume被蓝图成功地模拟了。


虚幻引擎——场景动态加载_epoll_08