Cocos Creator 游戏性能优化指南

devtools/2024/10/19 0:21:47/

性能优化

  • 引言
  • 一、减少Draw Call
    • 什么是Draw Call?
    • 为什么要减少Draw Call?
    • 减少Draw Call的方法
        • 1、剔除
          • I、视锥剔除:摄像机的位置和视角形成一个视锥体,只有位于视锥体内的对象才会被渲染。可以通过检查对象的包围盒(Bounding Box)是否与视锥体相交来判断对象是否需要渲染。
          • II、遮挡剔除:如果一个对象被其他对象完全遮挡,则该对象不需要被渲染。
          • III、背面剔除:对于封闭的几何体来说,朝向摄像机背面的面不需要被渲染。
          • IV、距离剔除:距离相机太远的物体不需要渲染。
        • 2、LOD
        • 3、图集
          • I、静态图集
          • II、动态图集
          • III、示例
  • 二、使用对象池
    • 1、什么是对象池?
    • 2、对象池的优缺点
    • 3、对象池的实现
  • 三、优化资源加载
    • 1、延迟加载
    • 2、预加载资源
  • 四、减少动画开销
          • 1、**减少动画帧数**:减少动画帧数可以减少CPU和GPU的负担。对于快速动作,玩家不会注意到少量帧的减少。
          • 2、**骨骼动画优化**:骨骼动画可以通过控制少量骨骼来驱动大量顶点,减少计算量。减少骨骼数量和复杂度可以进一步降低计算开销。
          • 3、**使用对象池技术复用动画对象**:通过对象池技术复用动画对象,避免频繁创建和销毁动画对象,减少内存分配和回收的开销。
  • 五、代码优化

引言

游戏开发中,流畅的游戏体验是玩家最关心的问题之一。一个卡顿的游戏会严重影响玩家的体验,甚至让玩家失去继续玩的兴趣。因此,优化游戏性能是每个游戏开发者必须掌握的技能。本文将详细介绍在使用Cocos Creator进行游戏开发时的一些性能优化技巧。

一、减少Draw Call

什么是Draw Call?

Draw Call是指CPU向GPU发出的命令,用来告诉GPU绘制某个图形图像。

为什么要减少Draw Call?

每一次Draw Call都会涉及到CPU和GPU之间的通信,这种通信是有成本的。大量的Draw Call会导致以下问题:

  • CPU开销:每个Draw Call都需要CPU发出命令,如果命令太多,会占用大量的CPU资源。
  • GPU开销:GPU需要处理每一个Draw Call的命令,导致GPU资源的浪费。
    在这里插入图片描述

GPU的绘制能力非常强大,能一次处理大量数据。但每一次Draw Call前,CPU都要做一系列的准备工作,才能让GPU正确渲染出图像。举个例子,假设你的网速是10M/s,传输一个1000M的压缩包,和传输1000个1M的文件,谁的速度快。【传输 1 个 1000M 的文件要比传输 1000个 1M 的文件要快得多得多】。因为在每一个文件传输前,CPU 都需要做许多额外的工作来保证文件能够正确地被传输,而这些额外工作造成了大量额外的性能和时间开销,导致传输速度下降。

减少Draw Call的方法

1、剔除

将不满足条件的对象整体移除,以下是实现算法:

I、视锥剔除:摄像机的位置和视角形成一个视锥体,只有位于视锥体内的对象才会被渲染。可以通过检查对象的包围盒(Bounding Box)是否与视锥体相交来判断对象是否需要渲染。
typescript">// 伪代码示例
public isInFrustum(camera, object) {
let frustum = camera.getFrustum();
let boundingBox = object.getBoundingBox();
return frustum.intersects(boundingBox);
}
II、遮挡剔除:如果一个对象被其他对象完全遮挡,则该对象不需要被渲染。
III、背面剔除:对于封闭的几何体来说,朝向摄像机背面的面不需要被渲染。
IV、距离剔除:距离相机太远的物体不需要渲染。
2、LOD

LOD算法是一种优化技术,用于减少远处或不重要物体的渲染复杂度,以提高整体渲染性能。它通过动态调整模型的细节级别,根据对象与摄像机的距离或重要性来选择不同的细节级别进行渲染。

在这里插入图片描述

  • 为同一个物体,配置不同材质(少Pass、少贴图、少计算)
  • 为同一个物体,配置不同的Mesh。例如高细节(High Poly)、中细节(Medium Poly)和低细节(Low Poly)。在渲染时,根据对象与摄像机的距离选择合适的细节级别进行渲染。
3、图集
I、静态图集

将多个小图片合并成一个大图片的方法,这样在渲染多个小图片时,只需要一次Draw Call就可以完成绘制。

Cocos Creator提供了自动图集(Auto Atlas)功能,可以方便地将多个小图自动合并成一个大图。在Cocos Creator中,可以通过资源管理器创建自动图集:

  1. 打开资源管理器,右键选择要合并的小图片文件夹。
  2. 选择“创建 -> 自动图集”。
  3. 在自动图集属性中,可以调整合并规则和参数。

使用图集

typescript">// 示例代码
resources.load('path/to/atlas', cc.SpriteAtlas, (err, atlas) => {if (err) {console.error(err);return;}let frame = atlas.getSpriteFrame('sprite_name');this.node.getComponent(cc.Sprite).spriteFrame = frame;
});
II、动态图集

Cocos Creator 提供了动态图集功能,实现机制如下:

  1. 从动态图集中获得一张 2048 x 2048 的空白纹理
  2. 当渲染一张小图时(任何一边不超过 512),如果这张小图没有被合并过,则将这张小图合并到这张空白纹理上(如果图集空间不够,则会新开空白纹理)
  3. 修改当前 2D 元素的 uv和图集信息

这样一来,小图就可以根据顺序实现自动图集分布,相邻的两个元素使用的图集会尽可能的一致。

动态图集功能在 Web 端默认开启,如果想要禁用,则需要调用:

typescript">// 禁用动态图集
DynamicAtlasManager.instance.enabled = false;

这个机制会有几个缺点。

  1. 需要开启图片内存缓存,会增一倍的内存开销
  2. 由于需要内存数据支持,目前PVR/ETC等GPU压缩格式的纹理,不支持动态图集
  3. 如果需要渲染的2D元素过多,会很容易导致图集交叉使用的情况
  4. 图集只会在场景切换时清空,对单场景不友好
III、示例

cocos creator的渲染流程中,会先渲染父节点,然后逐个渲染子节点。简单讲:深度优先。

在这里插入图片描述

因为 item 节点下的 Sprite 与 Label 节点渲染类型不同,并相互间隔排列,引擎无法向 GPU 批量提交渲染数据。

因此渲染一个 item 需要 DrawCall 4次:Sprite → Label → Sprite → Label。

优化:将Cache Mode模式改成BITMAP。Cache Mode有三种模式。

在这里插入图片描述

  • NONE:每一个 Label 都会生成为一张单独的位图,且不会参与动态合图,所以每一个 Label 都会打断渲染合批
  • BITMAP:当 Label 组件开启 BITMAP 模式后,文本同样会生成为一张位图,只要符合动态合图要求就可以参与动态合图,和周围的精灵合并 DrawCall(注意 BITMAP 模式只适用于不频繁更改的文本)。
  • CHAR:当 Label 组件开启 CHAR 模式后,引擎会将该 Label 中出现的所有字符缓存到一张全局共享的位图中,相当于是生成了一个 BMFont(适用于文本频繁更改的情况,对性能和内存最友好)。

二、使用对象池

1、什么是对象池?

对象池技术(Object Pooling)是一种优化方法,用于管理和重复利用对象,减少频繁创建和销毁对象的开销,特别适用于需要频繁生成和回收对象的场景,如子弹、敌人、特效等。通过对象池技术,可以显著提升游戏性能,减少内存碎片和垃圾回收的压力。

2、对象池的优缺点

优点

  • 提高性能:对象池通过重复利用已经创建的对象,避免了频繁的对象创建和销毁操作,从而提高了系统的性能。相比于每次都创建新的对象,从对象池中获取已经存在的对象可以节省系统开销,并显著减少了系统响应时间。
  • 节约内存和减少垃圾回收:对象池可以减少垃圾回收的频率,因为对象的重复利用降低了新对象的创建量。这样可以减少系统对内存的占用,提高内存的利用效率。
  • 资源管理和控制:对象池可以对对象进行统一的管理和控制,包括对象的创建、初始化、回收和销毁。通过对象池,可以有效地管理系统对资源的占用和释放,避免资源泄露和浪费。

缺点

  • 增加初始内存占用:对象池在初始化时会创建大量对象,占用一定的内存。

3、对象池的实现

具体实现请移步:对象池的制作

三、优化资源加载

1、延迟加载

延迟加载是指在需要使用资源时才进行加载,而不是在游戏启动时一次性加载所有资源。这样可以减少初始加载时间,提高游戏的启动速度。可以使用场景切换或事件触发时进行加载。

2、预加载资源

游戏加载阶段预先加载一些必要资源,以减少游戏运行时的卡顿。

图像:常用的装备图片、角色展示图片等常用显示图片可以在加载场景的时候就先预加载,存放在Map里面,使用时直接获取。

typescript">// 示例代码
resources.preload(['path/to/resource1', 'path/to/resource2'], (err, assets) => {// 资源预加载完成
});

四、减少动画开销

1、减少动画帧数:减少动画帧数可以减少CPU和GPU的负担。对于快速动作,玩家不会注意到少量帧的减少。
typescript">// 示例代码
let animation = node.getComponent(cc.Animation);
let clip = animation.getClip('animationName');
clip.sample = 10; // 将帧率降低到10帧每秒
animation.play('animationName');
2、骨骼动画优化:骨骼动画可以通过控制少量骨骼来驱动大量顶点,减少计算量。减少骨骼数量和复杂度可以进一步降低计算开销。
3、使用对象池技术复用动画对象:通过对象池技术复用动画对象,避免频繁创建和销毁动画对象,减少内存分配和回收的开销。

五、代码优化

1、尽量减少临时对象的创建,避免频繁触发垃圾回收。

typescript">for (let i = 0; i < 1000; i++) {let obj = getObject(); // 避免在循环中频繁创建新对象}

2、减少不必要的更新逻辑。确保update函数中只包含必要的逻辑,避免频繁调用耗时的操作。少在UPDATE里面刷新内容。多用事件触发等方式刷新。

typescript">//减少不必要的更新逻辑。确保update函数中只包含必要的逻辑,避免频繁调用耗时的操作。
update(dt) {if (this.needUpdate) {this.performUpdate(); // 仅在需要时执行更新逻辑}
}
typescript">//多用事件触发等方式刷新内容,避免频繁调用 update。
this.node.on('customEvent', this.onCustomEvent, this);

http://www.ppmy.cn/devtools/57871.html

相关文章

【链表】【双指针】1、合并两个有序链表+2、分隔链表+3、删除链表的倒数第N个结点+4、链表的中间结点+5、合并两个链表

3道中等2道简单 数组和字符串打算告一段落&#xff0c;正好最近做的几乎都是双指针&#xff0c;所以今天做链表&#xff01; 1、合并两个有序链表&#xff08;难度&#xff1a;简单&#xff09; 该题对应力扣网址 AC代码 思路简单 /*** Definition for singly-linked list.…

ubuntu常用命令

常用命令 netstatpsps -auxps -eLf netstat netstat -tunlp这个命令用来显示网络连接、路由表和网络接口统计信息。 Proto (协议): 显示连接使用的协议&#xff0c;通常是 “tcp” 或 “udp”。 Recv-Q (接收队列): 表示接收缓冲区中未被进程读取的数据包数量。一般情况下&am…

nginx的知识面试易考点

Nginx概念 Nginx 是一个高性能的 HTTP 和反向代理服务。其特点是占有内存少&#xff0c;并发能力强&#xff0c;事实上nginx的并发能力在同类型的网页服务器中表现较好。 Nginx 专为性能优化而开发&#xff0c;性能是其最重要的考量指标&#xff0c;实现上非常注重效率&#…

MATLAB 2024b 更新了些什么?

MATLAB 2024b版本已经推出了预览版&#xff0c;本期介绍一些MATLAB部分的主要的更新内容。 帮助浏览器被移除 在此前的版本&#xff0c;当我们从MATLAB中访问帮助文档时&#xff0c;默认会通过MATLAB的帮助浏览器&#xff08;Help browser&#xff09;。 2024b版本开始&…

返回值处理器器【Spring源码学习】

定义返回值类型处理器的组合&#xff1b; public static HandlerMethodReturnValueHandlerComposite getReturnValueHandler(){HandlerMethodReturnValueHandlerComposite composite new HandlerMethodReturnValueHandlerComposite();// 处理ModelAndViewcomposite.addHandle…

elementui中日期/时间的禁用处理,使用传值的方式

项目中,经常会用到 在一个学年或者一个学期或者某一个时间段需要做的某件事情,则我们需要在创建这个事件的时候,需要设置一定的时间周期,那这个时间周期就需要给一定的限制处理,避免用户的误操作,优化用户体验 如下:需求为,在选择学年后,学期的设置需要在学年中,且结束时间大…

【Rust基础入门】Hello Cargo

文章目录 前言Cargo是什么&#xff1f;Cargo的作用查看cargo版本使用cargo创建项目Cargo.toml文件cargo build命令cargo runcargo check为发布构建 总结 前言 在Rust编程中&#xff0c;Cargo扮演着至关重要的角色。它是Rust的包管理器&#xff0c;负责处理许多任务&#xff0c…

论文辅助笔记:ST-LLM

1 时间嵌入 2 PFA&#xff08;Partial Frozen Architecture&#xff09; 3 ST_LLM 3.1 初始化 3.2 forward