在这里插入图片描述

网罗开发 (小红书、快手、视频号同名)

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。

展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索“展菲”,即可纵览我在各大平台的知识足迹。
每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。


引言

很多开发者第一次做鸿蒙游戏时都有类似经历,项目刚开始的时候:

一个地图
几个角色
几个技能

运行非常流畅、FPS 稳定、内存占用也不高,但随着项目迭代:

地图越来越大
角色越来越多
特效越来越复杂

问题开始集中出现:

进入场景卡顿

切换地图等待

Boss首次出现掉帧

内存持续上涨

例如:

进入战斗

↓

黑屏2秒

↓

Boss出现

↓

FPS掉到30

很多开发者第一反应是:

GPU不够?

或者:

ArkUI性能有问题?

但 Profiling 后往往会发现:

CPU占用正常

GPU占用正常

真正耗时的是:

Resource Loading

也就是:

资源加载
资源解码
资源上传
资源回收

整个过程,在大型游戏中:

资源系统往往比渲染系统更容易成为性能瓶颈。

因为每一次场景切换、本质上都是一次资源重建。所以本文重点讨论:

ResourceSystem

如何从架构层解决:

加载慢
掉帧
内存膨胀
资源泄漏

等问题。

一、为什么游戏加载会卡顿?

先看一个典型流程,玩家点击:

开始游戏

此时系统实际执行:

加载地图
加载角色
加载技能
加载配置
加载音频
加载特效

例如:

Scene_A

包含:

200张图片

80个动画

40个音频

100个配置文件

很多项目会这样写:

async enterScene() {

  await loadMap()

  await loadHero()

  await loadSkill()

  await loadAudio()

}

看起来没有问题,实际上:

所有资源同步加载

导致:

IO阻塞
CPU解码
GPU上传

全部发生在同一个时间窗口,最终表现:

黑屏
卡顿
FPS下降

二、Profiling:资源加载到底耗在哪?

以一个实际项目为例,进入 Boss 场景:

FPS

60

↓

28

通过性能分析发现:

模块 耗时
图片读取 32%
图片解码 25%
GPU上传 18%
配置解析 12%
UI构建 13%

结果很明显:

75%以上时间
都浪费在资源处理

而不是渲染,这也是很多项目优化方向完全错掉的原因。

三、ResourceSystem:大型游戏的标准解法

资源管理最忌讳:

哪里需要
哪里加载

例如:

Image($r('app.media.hero'))

散落在几十个页面,后果:

重复加载

重复解码

重复上传GPU

最终导致:

内存暴涨

因此必须引入:

ResourceSystem

统一管理资源生命周期,架构如下:

                    ResourceSystem

                            │

       ┌────────────────────┼────────────────────┐

       ▼                    ▼                    ▼

   Loader              CacheManager          RefCounter

       ▼                    ▼                    ▼

 TextureResource     AudioResource      ConfigResource

核心原则:

资源只能通过
ResourceSystem访问

而不是:

UI直接读取

四、资源缓存架构设计

很多资源具有明显特点:

频繁使用

例如:

主角头像

金币图标

按钮素材

常用技能图标

如果每次重新加载:

读取磁盘
↓

解码
↓

上传GPU

性能浪费巨大,因此需要:

CacheManager

设计。

class CacheManager {

  private cache =
    new Map<string, Resource>()

}

读取流程:

请求资源

↓

缓存存在

↓

直接返回

↓

无需重新加载

性能收益通常能达到:

50%以上

五、对象池:解决资源频繁创建问题

大型项目经常出现:

子弹

技能特效

怪物实例

不断创建销毁,例如:

new Bullet()

每秒可能创建:

数百次

最终:

GC频繁触发

出现:

偶发掉帧

解决方案:

Object Pool
class BulletPool {

  get()

  recycle()

}

复用对象,避免:

频繁申请内存

六、预加载机制:消灭首次卡顿

Boss第一次出现为什么卡?因为:

图片未加载

特效未加载

音频未加载

全部集中在同一帧,解决方案:

await resourceSystem.preload([
  "boss_texture",
  "boss_skill",
  "boss_music"
])

进入场景前完成加载,这样:

Boss出现
≈
直接显示

用户几乎感知不到等待。

七、大型鸿蒙游戏资源架构

推荐采用:

                    Game Runtime

                           │

      ┌────────────────────┼────────────────────┐

      ▼                    ▼                    ▼

    Store              System          ResourceSystem

                                               │

                ┌──────────────────────────────┐

                ▼

            Cache Manager

                ▼

            Ref Counter

                ▼

             Loader

这里:

Store
负责状态
System
负责逻辑
ResourceSystem
负责资源生命周期

形成完整 Runtime。

总结

很多开发者以为:

游戏加载慢是资源太大。

实际上:

真正的问题往往是没有资源管理架构。

当项目规模达到一定程度后:

直接加载
↓
缓存管理
↓
引用计数
↓
资源生命周期
↓
ResourceSystem

几乎是必经之路,对于鸿蒙游戏开发而言,如果说:

Store
是世界状态中心

那么:

ResourceSystem
就是整个游戏运行时的资源调度中心。

而一个优秀的 ResourceSystem,往往比单纯优化几张图片更能决定游戏最终性能上限。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐