首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >OPPO 快游戏在浏览器复现运行(七)之远程资源离线化

OPPO 快游戏在浏览器复现运行(七)之远程资源离线化

作者头像
LiesAuer
发布2026-08-10 09:07:32
发布2026-08-10 09:07:32
220
举报
文章被收录于专栏:LiesAuer's BlogLiesAuer's Blog

完全的离线化仅适用于走快游戏 API 的资源,部分资源是直接通过 img 标签或者其他方式加载的(最终会 fallback 到 XHR)还是会到本地服务器发起请求。web.sdk.js 不对此类资源做特殊拦截处理,做离线化的本意是解决版本更新后在线资源的失效问题,对快游戏做 web 移植本身就需要一个静态服务器托管,所以这个并不算大问题。如果需要完完全全的离线化,可自行对游戏逻辑做魔改拦截。

针对使用 Cocos Creator 框架制作的游戏,在 _CCSettings 中一般能看到以下字段:remoteBundlesbundleVersremoteBundles是远程 Bundle 列表,bundleVers是所有 Bundle 列表,比如:

代码语言:javascript
复制
window._CCSettings = {
    remoteBundles: ["resources"],
    bundleVers: {
        internal:"b3cad",
        b0:"10607",
        b1:"f37a4",
        data:"5bf88",
        resources:"011ae",
        main:"c0c83"
    }
};

远程 Bundle 可能会依赖其他 Bundle,为了远程 Bundle 能顺利的加载,需要先通过 loadBundle 拿到 Bundle 配置中的 deps,统计完整依赖链,再按依赖顺序逐个加载。

代码语言:javascript
复制
async function loadCCBundle(names, unload = false) {
    names = Array.isArray(names) ? names : [names];

    const bundles = Object.create(null);
    const visited = Object.create(null);
    const visiting = Object.create(null);
    const order = [];

    function unloadBundle(name) {
        const bundle = cc.assetManager.getBundle(name);

        if (bundle) {
            bundle.releaseAll();
            cc.assetManager.removeBundle(bundle);
        }
    }

    function loadBundle(name) {
        return new Promise(function (resolve, reject) {
            if (unload) {
                unloadBundle(name);
            }

            const exists = cc.assetManager.getBundle(name);

            if (exists) {
                resolve(exists);
                return;
            }

            cc.assetManager.loadBundle(name, function (err, bundle) {
                if (err) {
                    reject(err);
                    return;
                }

                resolve(bundle);
            });
        });
    }

    function loadDir(bundle) {
        return new Promise(function (resolve, reject) {
            window.qg.consolelog('loadDir start: ' + bundle.name);

            bundle.loadDir('', function (err) {
                if (err) {
                    reject(err);
                    return;
                }

                window.qg.consolelog(bundle.name + ' resources loaded');
                resolve(bundle);
            });
        });
    }

    async function visit(name, stack) {
        if (visited[name]) {
            return;
        }

        if (visiting[name]) {
            throw new Error('Circular bundle deps: ' + stack['concat'](name).join(' -> '));
        }

        visiting[name] = true;

        const bundle = await loadBundle(name);
        const deps = bundle.deps || [];

        bundles[name] = bundle;
        window.qg.consolelog('bundle deps: ' + name + ' -> ' + JSON.stringify(deps));

        for (let i = 0; i < deps.length; i++) {
            await visit(deps[i], stack['concat'](name));
        }

        visiting[name] = false;
        visited[name] = true;
        order.push(name);
    }

    try {
        for (let i = 0; i < names.length; i++) {
            await visit(names[i], []);
        }

        window.qg.consolelog('bundle load order: ' + order.join(' -> '));

        for (let i = 0; i < order.length; i++) {
            const name = order[i];
            const bundle = bundles[name] || await loadBundle(name);

            await loadDir(bundle);
        }

        window.qg.consolelog('all bundle resources loaded');
    } catch (err) {
        window.qg.consoleerror(err);
    }
}

// 然后就可以正式下载远程 Bundle 了
loadCCBundle(window._CCSettings.remoteBundles);

此时应该可以看到控制台已经在疯狂下载资源了,但还不够,因为我们需要将这些资源保存放到本地,以便后续离线化加载。

这时候就到了 remote-cache 上场了,它本质上就是一个转发保存程序,收到请求后,重定向到真正的远程资源,并将资源原样返回的同时将资源保存到本地。

修改 _CCSettings 中的 server 字段:

代码语言:javascript
复制
window._CCSettings = {
    server: `http://127.0.0.1:9999/${originServer}`,
};

然后再次尝试 loadCCBundle,当所有远程 Bundle 都进行 loadCCBundle之后,就可以对 _CCSettings 中的 server 字段做一个小调整,将远程资源改成本地资源:

代码语言:javascript
复制
window._CCSettings = {
    server: `/https.xxx.com/xxxxxxxxxx`,
};

此时游戏已经完成远程资源的本地化了。

某些游戏是运行时通过 JS 动态加载的,此类逻辑没法提前预加载,只能通过人工的方式去到对应的场景下触发下载,做这类逻辑的游戏一般都比较捞。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档