社交直播游戏场景前端解决方案专栏(三): 通用资源管理器

超导体存储
• 阅读 505
本文作者:Gisercyw

背景

应用程序包含两个部分,代码和资源,资源通常包括配置文件、图标、图片、字体等,他们都直接影响到应用程序的包大小并且一定程度会影响应用程序的运行速度。在社交直播业务开发中不难发现,以下的两类场景对资源管理的诉求会相对强烈:

  1. 在游戏的开发过程中,一般需要使用到大量的图片、音频等资源来丰富整个游戏内容,而大量的资源就会带来管理上的困难, 一个好的资源管理也会为以后性能优化提供很大的帮助。
  2. 运营活动需要使用资源管理方式并配合浏览器缓存来完成活动资源的预加载/预请求, 以提升页面性能与用户体验。

基于上面两个场景的目的,我们需要一个通用的资源管理方案,让我们在游戏或者活动开发中,无需关心资源加载的细节,只需要指定加载的资源,并且在对应的逻辑位置中添加相应的执行加载代码即可完成对项目资源的管理。

调研

游戏框架通常具有较完备的资源管理方案,而这些资源管理方案具备下面共性和功能:

  1. 完善的资源加载基本机制,比如加载资源、查找资源、销毁资源、缓存资源
  2. 多资源配置文件管理与分组
  3. 支持资源进程状态监视
  4. 资源模块化
  5. 支持预加载/预请求
  6. 支持自定义资源处理,这样能够让加载更具有灵活性

这里我分为两类管理器,一类是资源预加载库,一类是游戏的资源管理库,并根据资源管理具备的功能点对比下目前已有的资源管理方案的特点。

社交直播游戏场景前端解决方案专栏(三): 通用资源管理器

在调研完这些方案后,我认为上述方案并不完全适合我们,我们需要的是能够覆盖我们游戏与活动业务开发更为通用的资源管理方案,不会与任何游戏引擎绑定,这是与其他方案最本质的不同。其次在设计上需要考虑更多的是性能问题,采用插拔式的代码组织方式,在保证主包体积稳定的基础上,通过插件扩展特定场景的功能需求,例如将预请求功能可以作为核心功能,而面向各个场景的资源转换、缓存功能可以作为独立的插件。具体的不同如下:

  1. 相对于游戏资源管理,大部分的游戏资源管理更多的目的是为其游戏引擎提供开箱即用的资源管理工作,跟游戏引擎耦合较深;另外虽然大型游戏引擎具有完备的资源管理体系,但是在预请求等场景下并不支持;
  2. 相对于这些预加载库,其作用主要是资源加载,而资源管理器的定位不只是资源的加载器,还包括资源管理,缓存,解析,转换等功能,并且在资源优先级等方面都做了完整的定义。另外在经过团队的测试后,发现 resource-loader 与 PxLoader 这类预加载库,在音视频资源的处理与加载上会存在一些兼容性问题,不支持 SSR/SSG 等服务端渲染等。

整体设计

针对业务开发中的核心场景,在保持资源管理核心模块基础上,通过插件化架构,设计出资源管理器的整个体系,如下图所示。

社交直播游戏场景前端解决方案专栏(三): 通用资源管理器

依赖能力

资源管理器主要依赖 Extension 模块的注册能力和 WebWorker 的多线程能力。

资源解析与转换是一项耗时的操作,特别是在需要大量资源的游戏场景中,如果只是并发的加载、解析大量的资源,由于 Javascript 单线程的原因,会容易产生卡顿现象,导致页面无法及时响应,而 Web Worker 使得网页中进行多线程编程成为可能。当主线程在处理界面事件时,Worker 可以在后台运行,帮你处理大量的资源加载、解析、转换、缓存工作,当完成这些操作后,将加载结果或者缓存数据返回给主线程,由主线程更新UI。资源管理器内置了 WebWorker 来解析、加载资源,每种类型资源的处理都可以通过开启 Worker 通道来完成,默认是开启的,同时也提供了开启参数能够覆盖默认配置,需要注意的是并非所有的环境都支持 Workers,在一些场景下设置不开启可能更合适。如下所示,以处理 Image 资源转换 Buffer 为例,通过指定资源转换脚本的 URI 来执行 Worker 线程。

import WorkController from 'music/WorkController';

const MAX_WORKER_NUM = navigator.hardwareConcurrency || 6;

const loadBufferImageCode = `
 async function loadBufferImage(url) {
    const result = await fetch(url);

    if (!result.ok)
    {
        throw new Error('failed to load');
    }

    const imageBuffer =  await result.arrayBuffer();

    return imageBuffer;
 }
 onmessage = async (e) =>
    {
        const {
            data: {
                uuid,
                id,
            }
        } = e
        try
        {
            const bufferImage = await loadBufferImage(e.data.data[0]);

            postMessage({
                data: bufferImage,
                uuid,
                id,
            }, [bufferImage]);
        }
        catch(error)
        {
            postMessage({
                error,
                uuid,
                id,
            });
        }
    };
`;

let worker = WorkController.workerPool.pop();

if (!worker && WorkController.WorkersNumber < MAX_WORKER_NUM) {
    const workerURL = URL.createObjectURL(
        new Blob([loadBufferImageCode], { type: 'application/javascript' })
    );

    WorkController.WorkersNumber++;
    worker = new Worker(workerURL);

    worker.addEventListener('message', (event: MessageEvent) => {
        WorkController.complete(event.data);
        WorkController.next();
    });
}

插件注册能力是资源管理器的一个基本能力,方式是主功能通过主包引入,其他功能通过插件的形式按需引入,既能够保证主包的稳定,又能够减小整个包体积。资源管理器的核心功能是资源预加载,而针对特定类型资源的解析、缓存、转换则是通过对应插件来完成,插件模块的主要方法类型定义如下所示,提供了插件处理的基本功能。

declare const ExtensionModule: {
  /**
   * 移除插件
   */
  remove(...extensions: Array<ExtensionOptionType>): any;
  /**
   * 注册插件
   */
  add(...extensions: Array<ExtensionOptionType>): any;
  /**
   * 添加/删除扩展时的处理功能
   */
  registerHandler(type: ExtensionType, onAdd: ExtensionHandler, onRemove: ExtensionHandler): any;
  /**
   * 处理插件列表
   */
  handleExtensions(type: ExtensionType, list: any[]): any;
};

核心模块

对于特定类型的资源,在资源管理器底层会经过资源检测、 资源映射、加载解析、资源缓存的流程,每个环节都是独立的,其中部分环节并不是必需的,因此不是每个资源都会完全走完这几步,例如如果是预请求资源,则不需要缓存,因为预请求利用的是浏览器缓存,对于需要使用的功能,可以通过插件或者参数设置开启。

社交直播游戏场景前端解决方案专栏(三): 通用资源管理器

外部接口

外部接口主要提供了两类接口,一类单独的资源接口(Resource),一类是缓存接口(Cache)。而资源为了满足模块化的场景,我们又将其分为 Resource 与 Bundle ,Resource 提供全局资源的操作,Bundle 提供模块化资源的操作。在这些简洁易用的接口基础上,我们可以轻松完成资源的预请求、资源预加载、手动加载与自动加载,资源缓存处理等操作。

功能使用

下面选取几种业务中常见的的场景来介绍资源管理器的实际使用方式,可以满足小游戏或者活动开发中资源加载与转换的需求。

预加载

预加载是一种浏览器机制,使用浏览器空闲时间来预先下载/加载用户接下来很可能会浏览的页面/资源,当用户访问某个预加载的链接时,如果从缓存命中,页面就得以快速呈现。预加载一般会配合loading或者加载页来呈现,合理的有效加载交互设计可以减少用户焦虑,减轻用户等待的压力,而每个阶段预加载资源的分配能够有效降低页面访问速度,减少页面切换时的闪烁问题,进而达到提升用户体验的目的。

社交直播游戏场景前端解决方案专栏(三): 通用资源管理器

资源管理器的预加载功能可以通过简洁的api来实现, 如下所示:

const loadAssets = [
            {
                src: 'https://someurl.png',
                type: 'IMAGE', // 图片
            },
            {
                src: 'https://someurl.mp3',
                type: 'AUDIO', // 音频资源
            },
            {
                src: 'http://someurl.mp4',
                type: 'VIDEO', // 视频资源
            },
            {
                src: 'https://someurl.ttf',
                type: 'FONT', // 字体资源
                subType: 'ttf',
            },
            {
                src: 'https://someurl.json',
                type: 'JSON', // JSON资源
            }
    ];

const LoadingPage = () => {
    const [progress, setProgress] = React.useState(0);

    React.useEffect(() => {
        const load = async () => {
            const res = await Resource.loadResource(loadAssets, (progress) => {
                setProgress(Number(progress.toString().match(/^\d+(?:\.\d{0,2})?/)) * 100);
            });
        };

        load();
    }, []);

    return (
        <div>资源加载进度:{progress}%</div>
    );
};

资源模块化

在游戏开发中,我们会需要将资源按照不同的功能和场景划分与使用,如下图所示,资源管理器中可以将图片,脚本,多媒体等资源指定为多个 Bundle,其中每种类型资源还可以根据页面划分成多个 Bundle,比如图片可以根据首屏图片、弹窗与浮层图片、非首屏图片分成多个 Bundle,然后在游戏运行过程中,按照需求去加载不同的 Bundle,以减少启动时需要加载的资源数量,从而减少首次下载和加载游戏时所需的时间。

社交直播游戏场景前端解决方案专栏(三): 通用资源管理器


// 添加
Resource.addBundle('first-scene', {
    mainBg: 'backgroundA.png',
    avatar: 'avatarA.png',
    font: 'fontA.ttf',
});

// 添加
Resource.addBundle('next-scene', {
    mainBg: 'backgroundB.png',
    avatar: 'avatarB.png',
    font: 'fontB.ttf',
});


// 加载
const firstSceneResource = await Resource.loadBundle('first-scene');

// 加载
const nextSceneResource = await Resource.loadBundle('next-screen');

资源转换

以图片类型资源转换为例,首先要启用图片转换插件,主要通过以下方式注册插件

import ResourceImagePlugin from 'resource-image-plugin';

Resource.addPlugin(ResourceImagePlugin)

然后通过 formatType 参数指定转换类型,resource-image-plugin可以支持以下类型转换: Buffer、Blob、BitMap、PixiTexture
png转Bitmap

const res = await Resource.loadResource(
        {
            src: 'https://p5.music.126.net/obj/wo3DlcOGw6DClTvDisK1/24086412116/de58/ecc0/3ef8/d0ce5485ed549eeb0e77b8a2e54bb4c4.png',
            formatType: 'Bitmap',
        }
    );

png转Pixi Texture

const res = await Resource.loadResource(
        {
            src: 'https://p5.music.126.net/obj/wo3DlcOGw6DClTvDisK1/24086412116/de58/ecc0/3ef8/d0ce5485ed549eeb0e77b8a2e54bb4c4.png',
            formatType: 'Texture',
        }
    );

总结

目前资源管理器已经社交直播多个业务中落地,其不仅为 Alice.js 底层提供开箱即用的资源管理能力,同时为社交直播运营活动提供了预加载的手段,未来还会针对内部其他场景适配与支持,例如支持3D资源/模型、智能化加载等。
本文主要分析了资源管理的现状与存在问题,在业务游戏化背景下,探索了符合社交直播业务发展的资源管理解决方案,并介绍了不同场景下的使用方式,如果您对此内容感兴趣,可以评论交流。

参考资料

本文发布自网易云音乐技术团队,文章未经授权禁止任何形式的转载。我们常年招收各类技术岗位,如果你准备换工作,又恰好喜欢云音乐,那就加入我们 grp.music-fe(at)corp.netease.com!
点赞
收藏
评论区
推荐文章
Dax Dax
4年前
前端性能优化
前端性能优化1、减少资源的请求次数和大小压缩合并js和css文件,减少http请求次数和请求资源的大小;在项目中使用webpackglup等打包编译工具2、尽量使用字体图标或者svg图标代替传统的png(jpg)图渲染更快,减少代码体积,且放大不会出现变形等3、使用图片懒加载目的是减少页面第一次加载的http请求次数,实现思路:
浩浩 浩浩
4年前
【Flutter实战】资源管理
2.4资源管理FlutterAPP安装包中会包含代码和 assets(资源)两部分。Assets是会打包到程序安装包中的,可在运行时访问。常见类型的assets包括静态数据(例如JSON文件)、配置文件、图标和图片(JPEG,WebP,GIF,动画WebP/GIF,PNG,BMP和WBMP)等。指定assets和包管理一样,Flutter
Stella981 Stella981
4年前
LiveVideoStack线上分享第五季(十三):高性能视频硬件编码
面向4G/5G场景下视频业务的爆发以及用户对于更高画质的要求(直播、短视频、视频点播等业务),更高清的画质意味着需要更大的计算资源以及网络带宽资源,而海量用户侧的画质提升/宽带降低,会导致前端以及视频平台厂商带宽的线性增长,增加运营开销。基于高性能视频编解码技术的解决方案,可以实现高清画质更快的压缩速度、更少的带宽消耗,使得视频内容生产方及运营平台,在满足用
Wesley13 Wesley13
4年前
D3D资源管理
摘要受管贴图(Managedtextures,也就是我们通常所谓的“自动管理贴图”),在DX6中首次被引入,经过一系列的改进和增强,在DX9中自动管理的资源类型增加到贴图,顶点缓冲,顶点索引缓冲,所有这些资源使用统一的公共接口。通过使用D3D资源管理器,应用程序可以轻松的处理设备丢失、处理稍微过量的显存使用。有时开发者在使用受管资源
Wesley13 Wesley13
4年前
MySQL查询缓存
1、mysql分布式事务在mysql中,使用分布式事务的应用程序涉及一个或多个资源管理器和一个事务管理器,分布式事务的事务参与者、资源管理器、事务管理器等位于不同的节点上。这些不同的节点相互协作共同完成一个具有逻辑完整性的事务。分布式事务主要作用在与确保事务的一致性和完整性。1.1、了解分布式事务的原理  资源管理器(RM):用于向事务提供资
「零售数据通道」数据炼金术:千亿级流量资产湖仓架构转型
作者:京东零售陈美航0前言在流量领域的转化分析、搜索推广算法及AI等数据分析应用场景中,流量资产的质量直接影响到业务的监测和运营。作为流量资产的基石,流量数仓在应对快速变化和多样化的业务需求时,如何在提高效率、优化用户体验和控制成本方面做到最佳?本文将方案
公孙晃 公孙晃
2年前
4K视频壁纸推荐:花见Live Wallpaper & Themes 4K Pro
该应用程序提供了大量的动态壁纸和主题,包括自然、动物、城市、抽象等各种类别,可以满足用户不同的需求。除了壁纸和主题之外,该应用程序还提供了许多其他功能,如屏幕锁定、桌面图标、字体等,可以让用户个性化定制他们的设备...
绣鸾 绣鸾
2年前
花见Live Wallpaper 4K Pro for mac视频壁纸软件
是一款提供各种高清动态壁纸和主题的应用程序。该应用程序提供了大量的动态壁纸和主题,包括自然、动物、城市、抽象等各种类别,可以满足用户不同的需求。除了壁纸和主题之外,该应用程序还提供了许多其他功能,如屏幕锁定、桌面图标、字体等,可以让用户个性化定制他们的设备
实战剖析-vue项目首屏加载时长优化
现状分析:首屏速度是用户体验的最关键一环,而首屏速度最大的决定性因素就是资源的加载速度,资源加载速度等于资源大小网速,老的前端项目随着不断增长,代码可能会变得混乱,冗余难以理解,不断的做加法,久而久之,前端性能上就会受到影响,相信大家在工作当中一定遇到,
taskbuilder taskbuilder
1年前
项目资源管理
点击系统侧边栏里的项目图标,会在系统资源列表里显示当前任擎服务器上所有项目的各种资源列表,包括数据模型、后台服务、前端文件、数据表单和微信小程序等。项目资源管理器用来对开发者自己开发的软件项目进行管理,这里的“项目”是指仅供开发者自己或开发者指定的特定用户