元梦之星地图性能优化指引
为何要考虑性能
地图性能较差时,会导致游玩过程发热和卡顿,严重时甚至无法进入地图或者进入地图后闪退,严重影响玩家的游戏体验,对地图数据有很大的影响。在平台的推荐逻辑中,地图性能较差的地图获得的曝光也会更少一些。
以下为一些性能比较差的地图收到的后台反馈。
| 反馈截图1 | 反馈截图2 |
|---|---|
![]() |
![]() |
该文档将会介绍影响性能的因素,如何在设计、制作地图的过程中优化好性能。适合满足以下任一条件的创作者:
- 中级造梦师及以上的创作者
- 做过占用值7万以上的地图
- 单张地图的创作时长超过50小时
- 准备创作一张其他创作者没做过的创新玩法地图
性能的影响因素
和体验相关的性能指标包括帧率、内存、发热、加载时长。
| 体验指标 | 不达标时负面体验 | 地图关联因素 |
|---|---|---|
| 帧率 | 卡顿掉帧,体验不流畅 | 占用值,元件数,运动器数量,物理元件数,生物数量,自定义逻辑复杂度 |
| 内存 | 低端机崩溃 | 占用值,元件数,界面控件数 |
| 发热 | 游玩一段时间后手机发烫 | 占用值,元件数,运动器数量,物理元件数,生物数量,自定义逻辑复杂度 |
| 加载时长 | 等待时间无聊,体验中断 | 占用值,元件数,界面控件数 |
随着地图内容增多、复杂度变高,要维持良好的性能指标,就需要更合理的去设计地图,把性能更多的分配到玩家主要的游戏体验上。
以《孤岛行动-无限枪战》这张地图为例,玩家的游戏范围主要在对战区域,外围区域主要作用是提供远景。那么外围区域的元件数量就可以非常少,体积放大,且可以取消掉物理碰撞;而对战区域则可以用更多的元件进行精细的设计。
| 示例:性能分配优化 |
|---|
![]() |
地图制作过程的性能管控
在制作地图时,从最开始的设计阶段就应该将性能纳入考虑,过程中也需要经常关注性能的实际情况。如果地图制作完成时再调整,往往需要花大量的时间,甚至可能因为工作量太大改不动了。
参考市面上游戏开发流程,地图制作可以分为几个阶段(图片来源于地图《枪王之王-无限升级》,仅用于直观说明各个阶段):
1. 白盒阶段
使用简单几何体 (立方体、圆柱体等) 构建地图基本布局,用于验证地图流程和可玩性。这一步可以大致根据地图面积和复杂度估算最终的元件数量和占用值,如果估算的性能超标,在这个阶段就需要调整设计。
| 白盒阶段示例 |
|---|
![]() |
2. 灰盒阶段
细化白盒设计,加入更详细的地形、障碍物和交互元素。这一步完成后,可以更加精确的估计最终的数据。
| 灰盒阶段示例 |
|---|
![]() |
3. 标准确定阶段
选择地图的一个核心局部区域进行细化并达到最终想要的效果。这个阶段,可以根据细化的区域占整个地图的比例,来估算整个地图完成后的占用值和元件数量。如果占用值会超标的话,需要在这个阶段重新调整,直到预期的最终占用值和元件数量不会超标。
| 标准确定阶段示例 |
|---|
![]() |
4. 最终打磨阶段
根据确定的标准细化整个地图并进行最终的测试和调整。
| 最终打磨阶段示例 |
|---|
![]() |
常见的性能优化点
从前面的说明可以看到,性能和游戏设计、制作过程息息相关。当我们发现地图性能出现问题时,在元件数量和占用值已经控制住的情况下,可以检查是否有以下性能过度消耗的情况。在优化过程中,可以借助工具箱下的性能检查辅助判断。
| 性能检查工具 |
|---|
![]() |
内存类
- 自定义图片过大:越大的图片占用的内存越高,但图片在屏幕上的显示区域是有限的,图片的大小超过显示区域太多的话,就会浪费内存。下面的截图中,3个图标的原图尺寸分别为64x64、128x128、1024x1024。在手机上行查看时,64x64的图标会有轻微的模糊感,128x128、1024x1024则区别很小,但128x128的内存占用只有1024x1024的1/16,综合考虑,这种情况我们应该使用128x128的图片。
| 图片大小对比 |
|---|
![]() |
渲染类
- 元件过于密集:元件过于密集会导致视角对准该区域时,渲染消耗大幅增加,在游戏时容易出现卡顿、不流畅的情况。可以借助性能检查工具定位元件过于密集的地方。
| 元件密集区域 |
|---|
![]() |
- 半透元件或特效重叠:半透效果会大幅增加画面渲染的负担。当半透元件之间重叠时,其渲染消耗相较单层半透又会有大幅增加。下图所示的情况中:左图没有半透物件;中间图片紫色水晶为半透;右图紫色水晶和雪人底座都是半透且两者有重叠。因此渲染消耗上右图远高于中间,中间高于左图。
| 无半透 | 单层半透 | 双层半透重叠 |
|---|---|---|
![]() |
![]() |
![]() |
由于特效大多带有半透效果,因此特效和特效重叠、特效与半透元件重叠也属于要尽量规避的情形。
- 天空环境使用节制:天气类型、滤镜效果开启会给画面渲染带来负担,低端机容易发热。
| 天空环境设置 |
|---|
![]() |
逻辑类
- 碰撞复杂:每个开启碰撞的元件,都会带来物理计算的负担。一些并不会影响玩法的元件,我们可以选择关掉碰撞。例如下图中,红色范围内的元件玩家无法到达,其是否有碰撞对玩法无影响,这种情况就可以关掉这些元件的碰撞。
| 碰撞优化示例1 |
|---|
![]() |
另外,对于比较密集的元件区域,当玩法不需要很精细的碰撞时,以选择关掉这些元件的碰撞,同时用一个开启碰撞的空气墙盖住这些元件,来达到节省性能又不穿模的效果。如下图的楼梯,正常所有元件都开启碰撞的话,会有18个长方体碰撞;如果我们关掉元件碰撞,而是用贴合楼梯大小的空气墙提供碰撞,就只有3个长方体碰撞。实际上,就这个例子而言,用空气墙时玩家无需在上楼时一直跳跃,体验也是更好的。
| 碰撞优化示例2 |
|---|
![]() |
建议用简单几何体代替复杂模型的碰撞,性能消耗排序:立方体 < 球体 < 异形/不规则形状。
- 运动器过多:场景中运动器驱动的元件越多,性能消耗越大。建议运动器驱动的元件总量控制在500以内,可借助性能检查工具统计数量。
| 运动器统计 |
|---|
![]() |
- 动态生成的物件过多:当通过自定义逻辑动态创建或复制的元件过多时,这些元件会带来内存、渲染的消耗(和编辑状态下放置的元件是一样的),同时动态创建元件本身也是性能消耗很高的操作。在游戏设计上,可以对动态生成的频率和总量进行限制。在实现逻辑上,循环利用已经创建好的对象,可以有更好的性能表现,其实现思路见下图。
| 对象循环利用思路 |
|---|
![]() |
例如,我们要制作1个可以建造方块的玩法,如下图所示。
| 建造方块玩法示例 |
|---|
![]() |
在游戏机制上,需要设计好玩家建造方块的退出机制,如可被破坏、定时消失等,确保场景上所有玩家建造的方块总量是有上限的,且达到上限时不会有性能问题。然后在游戏时,可以把这些方块预先创建好放在玩家看不到的区域。每次玩家建造时,把空闲的预创建方块移到游戏区域;游戏区域的方块消失时,移回不可见区域备用。
| 方块预创建方案 |
|---|
![]() |
综合类
- 静态元件设置不合理:在编辑器中,每个元件都提供了大量的属性,包括物理、信号、运动器等等,这些功能在游玩时处于待机的状态,但仍然会占用性能。对于游玩过程中不会变化(如不会运动、不会切换皮肤、不会被逻辑控制等)的元件,我们可以把它们指定为静态元件,引擎会以性能更优的方式来处理静态元件。在游戏设置-静态元件优化中选择”识别后设置”,编辑器会自动将不会变化的元件设置为静态元件。
| 静态元件设置 |
|---|
![]() |
如果识别结果有错误(表现为多人测试与发布后显示为白块、针对元件的逻辑不生效),可以在元件属性中手动修改为”强制非静态”。
| 强制非静态设置 |
|---|
![]() |
自定义逻辑优化
该部分内容有一定门槛,建议对扣叮或Lua有一定实践之后阅读。
目前自定义逻辑有两个载体:扣叮和编程元件(即Lua)。
在扣叮中,所有逻辑在服务器中执行;编程元件中,逻辑可以在服务端执行,也可以在客户端执行。
- 服务端逻辑(扣叮及编程元件的服务端代码)性能较差时,会导致对玩家操作的响应不及时。
- 客户端逻辑(编程元件的客户端代码)性能较差时,会导致画面卡顿。
避免产生上述问题的核心在于:单帧执行逻辑的总用时不要超过最大帧率1帧的时长。在服务器中,最大帧率为每秒30帧,对应可用的计算时间约为0.033秒。在客户端,最大帧率为每秒60帧,对应可用的计算时间约为0.0167秒。一旦超过,未执行完的部分就会占用下一帧的执行时间,产生掉帧卡顿。
| 帧时间限制 |
|---|
![]() |
一般来讲,玩家感受到明显网络延迟的时间大概是100ms,对应服务器3帧的时间。如果服务器连续掉帧超过3帧,玩家就会觉得操作反应迟缓。玩家感受到画面卡顿的阈值大约是帧率突变20%,客户端连续掉帧12帧玩家就会明显感觉”卡了一下”。
| 玩家感知阈值 |
|---|
![]() |
用高效的方式来实现玩法设计,是逻辑性能良好的基础。当我们把玩法逻辑优化到没有冗余和低效的部分后,就可以把重点放在如何将逻辑安排在不同的帧里进行计算。那么哪些逻辑是会在一帧内连续执行的呢?以扣叮为例,事件积木及延时积木之间的积木,都会在一帧之内连续执行。如下面截图所示的扣叮逻辑,1到2之间的积木会在一帧内执行,2到3之间的积木会在之后的某一帧内执行,3到最后的积木会在之后的一帧内执行。
| 扣叮执行时序 |
|---|
![]() |
(Lua脚本的性能优化方案,可见《Lua脚本性能优化实战》)
下面我们来看下哪些写法的容易产生掉帧:
- 单帧过多循环次数:
| 过多循环示例 |
|---|
![]() |
这个例子中,设置变量、数值运算、添加值到数组都是性能负担很低的操作,但是当这些操作在一帧内执行了一百万次时,量变达到质变,产生掉帧现象。修改时,可以调整内外层循环的次数,并在内层循环后添加延迟一帧执行。
- 未成功分帧:
| 分帧失败示例 |
|---|
![]() |
这个例子中,为避免同时过多操作,延迟了0.01秒。然而0.01秒小于当前帧最小时间,可能延迟0.01秒后,仍然处于当前帧,导致整体逻辑超时。如果没有特殊的需要,仅在性能优化这一个维度考量,建议使用延迟n帧的积木而不是延迟n秒的积木。
- 连续创建物体:
| 连续创建示例 |
|---|
![]() |
这个例子中,虽然只创建了十个西瓜,但是这段逻辑会要求十个西瓜在一帧内创建完成,而创建元件的操作本身比较耗时,导致这次操作产生了严重掉帧。由于扣叮会在服务器执行,玩家的画面上会在少量延迟后逐渐看到这些西瓜,但在这个过程中,玩家的操作会有迟滞感,若这里的西瓜不是10个,而是10万个,可能会导致玩家长时间得不到服务器的响应反馈而掉线。所以务必在大量创建过程中增加一定的间隔,或不使用大量创建(复制也是创建)。创建速度上,音效>特效>元件>逻辑元件>=触发盒>模组>生物,可以按照这个顺序斟酌数量间隔。除了创建外,附加和解除附加也是相对复杂的计算,也应该控制频率。
- 单机事件在多人环境误用:
| 单机事件误用示例 |
|---|
![]() |
上图的三个事件,执行频率很高,主要用于制作单机玩法,若在多人环境使用,会导致高频网络通信




























