当前位置: 代码网 > it编程>游戏开发>游戏引擎 > Cocos Creator 游戏性能优化指南

Cocos Creator 游戏性能优化指南

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

引言

游戏开发中,流畅的游戏体验是玩家最关心的问题之一。一个卡顿的游戏会严重影响玩家的体验,甚至让玩家失去继续玩的兴趣。因此,优化游戏性能是每个游戏开发者必须掌握的技能。本文将详细介绍在使用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)是否与视锥体相交来判断对象是否需要渲染。
// 伪代码示例
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. 在自动图集属性中,可以调整合并规则和参数。

使用图集

// 示例代码
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 端默认开启,如果想要禁用,则需要调用:

// 禁用动态图集
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里面,使用时直接获取。

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

四、减少动画开销

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

五、代码优化

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

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

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

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

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com