【心得】CocosCreator3.0.0 构建广告用playable单一Html

Creator 版本:3.0.0 - 3.0.1
目标平台:Html

先放上Demo连结,大佬们可以下载index.html来玩玩:
https://github.com/LoS-Light/CocosCreator3.0_SingleHtmlPlayable

部分Demo图片:
(视频)

[Ammo物理]

[模型动画]

正文:
因公司要發展Playable Ads, 故投入这方面的研究。
在CocosCreator2.x版,可以参考这两位大佬的文章来做到建构单一Html,
感谢(fkworld & chongshengzhujue)
fkworld:https://github.com/fkworld/cocos-to-playable-ad
chongshengzhujue:https://github.com/chongshengzhujue/playableFBCompile

但从3.0版开始,因引擎使用了SystemJs来管控模块,
故上述提供的工具皆无法使用了。
但因公司的需求,
故只好从零自己着手开發3.0专用的自动化转换单一Html的工具,
首先遇到的问题就是SystemJs,
因SystemJs引用模块都是透过路径来取用外部档案,
没有提供任何可以直接引用同一份文件内的脚本 ( <- 或许可能有提供,但我不知道也说不定),
故在这裡折腾了几天去魔改了SystemJs,
让它能够以相同的路径,但将它取用的资源从外部档案转为指向同一份Html裡的script tag区块,
这样第一步就算完成了。
接下来是引擎资源&游戏资源的部分,
这部分可以参考上述大佬们之前的作法,
包进引擎资源到Html裡:
style.css
cc.js
index.js
bundle.js
system.bundle.js
polyfills.bundle.js
settings.json
import-map.json

然后改写application.js,
自定义自己的游戏资源载入方案,
cc.assetManager.downloader.register(‘bundle’, downloadBundleHandler);
cc.assetManager.downloader.register(’.png’, arrayBufferHandler);
cc.assetManager.downloader.register(’.jpg’, arrayBufferHandler);
cc.assetManager.downloader.register(’.jpeg’, arrayBufferHandler);
cc.assetManager.downloader.register(’.ttf’, downloadFont);
cc.assetManager.downloader.register(’.plist’, downloadText);
cc.assetManager.downloader.register(’.json’, downloadJson);
cc.assetManager.downloader.register(’.bin’, downloadArrayBuffer);
cc.assetManager.downloader.register(’.mp3’, downloadWebAudio);
cc.assetManager.downloader.register(’.ogg’, downloadWebAudio);
cc.assetManager.downloader.register(’.wav’, downloadWebAudio);
cc.assetManager.downloader.register(’.mp4’, downloadVideo);
这样第二部引擎&游戏资源的部分也完成了。

再来是物理的部分,因Ammo Physics的资源并没有包进cc.js裡,而是产生6个外部档案,
wait-for-ammo-instantiation.js
_commonjsHelpers.js
ammo.js
ammo-instantiated.js
ammo.wasm.js
ammo.wasm
故大佬您也得将它们的代码包进Html裡。

这样前前后后折腾了两个礼拜后,终于作出了上述的展示用单一Html,
但这自动化工具还有很多内容没有测试以及实作,
例如整个Html的资源压缩,各playable广告渠道的支持等等,
故还无法用于production上,
所以暂时还不会公开本自动化工具,
但如果有能力的大佬,
可以按照上述提的思路来自行实作。

13赞

试玩了一下,这种方式很有意思。

1赞

厉害了, 期待开放自动化工具。。

1赞

我用 2.4 合并 json 引擎修改
var downloadJson = function downloadJson(url, options, onComplete) {
options.responseType = “json”;
let resg = url.split("/")[url.split("/").length -1];
if (window.resJsons[resg]) {
if (“string” === typeof window.resJsons[resg]) {
let data = JSON.parse(window.resJsons[resg]);
onComplete && onComplete(null, data);
} else {
onComplete && onComplete(null, window.resJsons[resg]);
}

  } else {
    downloadFile(url, options, options.onFileProgress, (function(err, data) {
      if (!err && "string" === typeof data) try {
        data = JSON.parse(data);
      } catch (e) {
        err = e;
      }
      onComplete && onComplete(err, data);
    }));
  }
};

请指点一下 问题 vconsole.min.js:10 Please load bundle internal first Error: Please load bundle internal first

我不太确定您的错误是什麽,
但我记得没错的话,
2.4版json的加载变成透过bundle了,
以下是改写的参考代码,
您试试。

cc.assetManager.downloader.register(‘bundle’, downloadBundleHandler);

function downloadBundleHandler(nameOrUrl, options, onComplete) {
    let bundleName = cc.path.basename(nameOrUrl);
    let url = nameOrUrl;
    if (!REGEX.test(url)) url = 'assets/' + bundleName;
    var version = options.version || cc.assetManager.downloader.bundleVers[bundleName];
    var count = 0;
    var config = `${url}/config.${version ? version + '.' : ''}json`;
    let out = null, error = null;

    downloadJson(config, options, function (err, response) {
        if (err) {
            error = err;
        }
        out = response;
        out && (out.base = url + '/');

        count++;
        if (count === 2) {
            onComplete(error, out);
        }
    });

    var js = `${url}/index.${version ? version + '.' : ''}js`;
    downloadScript(js, options, function (err) {
        if (err) {
            error = err;
        }
        count++;
        if (count === 2) {
            onComplete(error, out);
        }
    });
};

function downloadScript(url, options, onComplete) {
    var d = document, s = document.createElement('script');

    s.type = "text/javascript";
    s.charset = "utf-8";
    s.text = window.resMap[url];

    d.body.appendChild(s);

    onComplete(null);
}

function downloadJson(url, options, onComplete) {
    let data = window.resMap[url];
    data = JSON.parse(data);
    onComplete(null, data);
};

厉害了~~~

1赞

我的流程 是 2.4.3版本 修改web 打包后 把所有 的 json 合并到 ,引擎找 json 导致 找不 我就 修改了 一下 downloadBundleHandler 下载方式 如果 合并 中有 直接使用 ,onComplete(err, data); 回掉参数 报错了 说
Please load bundle internal first Error: Please load bundle internal first

目的是 把 这个时间优化掉 2.4.4 合并了 大多数 json 但是 加载速度没有明显变化

厉害了~~~

抱歉这么晚才回覆,
请问您是要问2.x还是3.x的版本,
两种版本写法是不一样的。

(帖子被作者删除,如无标记将在 24 小时后自动删除)

这里是我的写法,您可能要视情况修改一下后,成为您的版本。

cc.assetManager.downloader.register('.png', arrayBufferHandler);
cc.assetManager.downloader.register('.jpg', arrayBufferHandler);
cc.assetManager.downloader.register('.jpeg', arrayBufferHandler);
cc.assetManager.downloader.register('.ttf', downloadFont);

function arrayBufferHandler(url, options, callback, img) {
var data = window.resMap[url];

    var img = new Image();
    function loadCallback() {
        img.removeEventListener('load', loadCallback);
        img.removeEventListener('error', errorCallback);

        callback(null, img);
    }
    function errorCallback() {
        img.removeEventListener('load', loadCallback);
        img.removeEventListener('error', errorCallback);

        callback(new Error('Load image (' + url + ') failed'));
    }

    img.addEventListener('load', loadCallback);
    img.addEventListener('error', errorCallback);
    img.src = data;
};

function getFontFamily(fontHandle) {
    var ttfIndex = fontHandle.lastIndexOf(".ttf");
    if (ttfIndex === -1) {
        ttfIndex = fontHandle.lastIndexOf(".tmp");
    }
    if (ttfIndex === -1) return fontHandle;

    var slashPos = fontHandle.lastIndexOf("/");
    var fontFamilyName;
    if (slashPos === -1) {
        fontFamilyName = fontHandle.substring(0, ttfIndex) + "_LABEL";
    } else {
        fontFamilyName = fontHandle.substring(slashPos + 1, ttfIndex) + "_LABEL";
    }
    return fontFamilyName;
}

function downloadFont(url, options, onComplete) {
    let fontFamilyName = getFontFamily(url);
    let data = "url(data:application/x-font-woff;charset=utf-8;base64,PASTE-BASE64-HERE) format(\"woff\")";
    data = data.replace("PASTE-BASE64-HERE", window.resMap[url]);

    let fontFace = new FontFace(fontFamilyName, data);
    document.fonts.add(fontFace);

    fontFace.load();
    fontFace.loaded.then(function () {
        onComplete(null, fontFamilyName);
    }, function () {
        cc.warnID(4933, fontFamilyName);
        onComplete(null, fontFamilyName);
    });
}

:+1::+1::+1::+1::+1:

您指的FBX修改是什麽意思,能够更加详细说明您的问题吗?

请问downloadWebAudio这个是怎么实现的。

mark.

不好意思,回覆晚了,抱歉

function downloadWebAudio(url, options, onComplete) {
    var context = new (window.AudioContext || window.webkitAudioContext)();

    var data = window.resMap[url];
    data = base64toArray(data);

    context["decodeAudioData"](data.buffer, function (buffer) {
        onComplete(null, buffer);
    }, function (e) {
        onComplete(e, null);
    });
}

非常感谢你的回复,但原来我就发现,Audio是无法cc.assetManager.downloader.register拦截的。
因为Web平台最终还是会调用这个函数。https://forum.cocos.org/t/topic/122105

// 这个是player-web.ts文件
static loadNative (url: string): Promise<AudioBuffer> {

        return new Promise((resolve, reject) => {

            const xhr = new XMLHttpRequest();

            const errInfo = `load audio failed: ${url}, status: `;

            xhr.open('GET', url, true);

            xhr.responseType = 'arraybuffer';

            xhr.onload = () => {

                if (xhr.status === 200 || xhr.status === 0) {

                    audioContextAgent!.decodeAudioData(xhr.response).then((buffer) => {

                        resolve(buffer);

                    }).catch((e) => {});

                } else {

                    reject(new Error(`${errInfo}${xhr.status}(no response)`));

                }

            };

            xhr.onerror = () => { reject(new Error(`${errInfo}${xhr.status}(error)`)); };

            xhr.ontimeout = () => { reject(new Error(`${errInfo}${xhr.status}(time out)`)); };

            xhr.onabort = () => { reject(new Error(`${errInfo}${xhr.status}(abort)`)); };

            xhr.send(null);

        });

    }

// 所以我最后使用以下方法拦截。

function hookAudioHttp() {

    XMLHttpRequest.prototype.open = function (method, url, async, user, password) {

      this.url = url;

      Object.defineProperty(this, 'response', {

        get: function () { return this.data; },

        configurable: true

      });

    }

    XMLHttpRequest.prototype.send = function (body) {

      var data = window.assetsMap[this.url];

      this.data = decode(data);

      // this.response = decode(data);

      this.onload();

    }
  }

我有点好奇,几位的自定义ts文件是怎么加载的,我这里会跳到file://开头去,就加载失败了

大佬 可以给一套模板工具么 就是能直接使用的

这个文档已经写得挺清楚的了,你可以看一下,或者具体问题,具体提问。