/* loader.js:负责对资源的加载 */ "use strict"; function loader() { this._init(); } loader.prototype._init = function () {}; ////// 设置加载进度条进度 ////// loader.prototype._setStartProgressVal = function (val) { core.dom.startTopProgress.style.width = val + "%"; }; ////// 设置加载进度条提示文字 ////// loader.prototype._setStartLoadTipText = function (text) { core.dom.startTopLoadTips.innerText = text; }; loader.prototype._load = function (callback) { this._loadMusics(); if (main.useCompress) { this._load_async(callback); } else { this._load_sync(callback); } }; loader.prototype._load_sync = function (callback) { this._loadAnimates_sync(); this._loadSounds_sync(); core.loader._loadMaterials_sync(function () { core.loader._loadExtraImages_sync(function () { core.loader._loadAutotiles_sync(function () { core.loader._loadTilesets_sync(callback); }); }); }); }; loader.prototype._load_async = function (callback) { core.loader._setStartLoadTipText("正在加载资源文件..."); const all = {}; const _makeOnProgress = function (name) { if (!all[name]) all[name] = { loaded: 0, total: 0, finished: false }; return (loaded, total) => { all[name].loaded = loaded; all[name].total = total; let allLoaded = 0, allTotal = 0; for (const one of Object.values(all)) { allLoaded += one.loaded; allTotal += one.total; } console.log(allLoaded.allTotal); if (allTotal > 0) { if (allLoaded == allTotal) { core.loader._setStartLoadTipText("正在处理资源文件... 请稍候..."); } else { core.loader._setStartLoadTipText( "正在加载资源文件... " + core.formatSize(allLoaded) + " / " + core.formatSize(allTotal) + " (" + ((allLoaded / allTotal) * 100).toFixed(2) + "%)" ); } core.loader._setStartProgressVal((allLoaded / allTotal) * 100); } }; }; const _makeOnFinished = function (name) { return () => { setTimeout(() => { all[name].finished = true; for (var one in all) { if (!all[one].finished) return; } callback(); }); }; }; if (main.splitChunkMap) { this._loadAnimates_chunked( main.splitChunkMap.animates, _makeOnProgress, _makeOnFinished ); this._loadSounds_chunked( main.splitChunkMap.sounds, _makeOnProgress, _makeOnFinished ); this._loadMaterials_chunked( main.splitChunkMap.materials, _makeOnProgress, _makeOnFinished ); this._loadExtraImages_chunked( main.splitChunkMap.images, _makeOnProgress, _makeOnFinished ); this._loadAutotiles_chunked( main.splitChunkMap.autotiles, _makeOnProgress, _makeOnFinished ); this._loadTilesets_chunked( main.splitChunkMap.tilesets, _makeOnProgress, _makeOnFinished ); } else { this._loadAnimates_async( _makeOnProgress("animates"), _makeOnFinished("animates") ); this._loadSounds_async( _makeOnProgress("sounds"), _makeOnFinished("sounds") ); this._loadMaterials_async( _makeOnProgress("materials"), _makeOnFinished("materials") ); this._loadExtraImages_async( _makeOnProgress("images"), _makeOnFinished("images") ); this._loadAutotiles_async( _makeOnProgress("autotiles"), _makeOnFinished("autotiles") ); this._loadTilesets_async( _makeOnProgress("tilesets"), _makeOnFinished("tilesets") ); } }; // ----- 加载资源文件 ------ // loader.prototype._loadMaterials_sync = function (callback) { this._setStartLoadTipText("正在加载资源文件..."); this.loadImages( "materials", core.materials, core.material.images, function () { core.loader._loadMaterials_afterLoad(); callback(); } ); }; loader.prototype._loadMaterials_async = function (onprogress, onfinished) { this.loadImagesFromZip( "project/materials/materials.h5data", core.materials, core.material.images, onprogress, function () { core.loader._loadMaterials_afterLoad(); onfinished(); } ); }; loader.prototype._loadMaterials_chunked = async function ( chunks, makeOnProgress, makeOnFinished ) { await this._loadImagesFromChunks( chunks, core.materials, core.material.images, makeOnProgress, makeOnFinished ); core.loader._loadMaterials_afterLoad(); }; loader.prototype._loadMaterials_afterLoad = function () { const images = core.splitImage(core.material.images["icons"]); for (let key in core.statusBar.icons) { if (typeof core.statusBar.icons[key] == "number") { core.statusBar.icons[key] = images[core.statusBar.icons[key]]; if (core.statusBar.image[key] != null) core.statusBar.image[key].src = core.statusBar.icons[key].src; } } }; // ------ 加载使用的图片 ------ // loader.prototype._loadExtraImages_sync = function (callback) { core.material.images.images = {}; this._setStartLoadTipText("正在加载图片文件..."); core.loadImages("images", core.images, core.material.images.images, callback); }; loader.prototype._loadExtraImages_async = function (onprogress, onfinished) { core.material.images.images = {}; // Check .gif const gifs = images.filter(function (name) { return name.toLowerCase().endsWith(".gif"); }); // gif没有被压缩在zip中,延迟加载... this._loadExtraImages_loadLazy(gifs); images = images.filter((name) => !name.toLowerCase().endsWith(".gif")); this.loadImagesFromZip( "project/images/images.h5data", images, core.material.images.images, onprogress, onfinished ); }; loader.prototype._loadExtraImages_chunked = function ( chunks, makeOnProgress, makeOnFinished ) { core.material.images.images = {}; let images = core.images; // Check .gif const gifs = images.filter(function (name) { return name.toLowerCase().endsWith(".gif"); }); // gif没有被压缩在zip中,延迟加载... this._loadExtraImages_loadLazy(gifs); images = images.filter((name) => !name.toLowerCase().endsWith(".gif")); this._loadImagesFromChunks( chunks, images, core.material.images.images, makeOnProgress, makeOnFinished ); }; loader.prototype._loadExtraImages_loadLazy = function (list) { list.forEach(function (gif) { this.loadImage("images", gif, (id, image) => { if (image != null) { core.material.images.images[gif] = image; } }); }, this); }; // ------ 加载自动元件 ------ // loader.prototype._loadAutotiles_sync = function (callback) { core.material.images.autotile = {}; var keys = Object.keys(core.material.icons.autotile); var autotiles = {}; this._setStartLoadTipText("正在加载自动元件..."); this.loadImages("autotiles", keys, autotiles, function () { core.loader._loadAutotiles_afterLoad(keys, autotiles); callback(); }); }; loader.prototype._loadAutotiles_async = function (onprogress, onfinished) { core.material.images.autotile = {}; var keys = Object.keys(core.material.icons.autotile); var autotiles = {}; this.loadImagesFromZip( "project/autotiles/autotiles.h5data", keys, autotiles, onprogress, function () { core.loader._loadAutotiles_afterLoad(keys, autotiles); onfinished(); } ); }; loader.prototype._loadAutotiles_chunked = async function ( chunks, makeOnProgress, makeOnFinished ) { core.material.images.autotile = {}; const keys = Object.keys(core.material.icons.autotile); const autotiles = {}; await this._loadImagesFromChunks( chunks, keys, autotiles, makeOnProgress, makeOnFinished ); core.loader._loadAutotiles_afterLoad(keys, autotiles); }; loader.prototype._loadAutotiles_afterLoad = function (keys, autotiles) { // autotile需要保证顺序 keys.forEach(function (v) { core.material.images.autotile[v] = autotiles[v]; }); setTimeout(function () { core.maps._makeAutotileEdges(); }); }; // ------ 加载额外素材 ------ // loader.prototype._loadTilesets_sync = function (callback) { core.material.images.tilesets = {}; this._setStartLoadTipText("正在加载额外素材..."); this.loadImages( "tilesets", core.tilesets, core.material.images.tilesets, function () { core.loader._loadTilesets_afterLoad(); callback(); } ); }; loader.prototype._loadTilesets_async = function (onprogress, onfinished) { core.material.images.tilesets = {}; this.loadImagesFromZip( "project/tilesets/tilesets.h5data", core.tilesets, core.material.images.tilesets, onprogress, function () { core.loader._loadTilesets_afterLoad(); onfinished(); } ); }; loader.prototype._loadTilesets_chunked = async function ( chunks, makeOnProgress, makeOnFinished ) { core.material.images.tilesets = {}; await this._loadImagesFromChunks( chunks, core.tilesets, core.material.images.tilesets, makeOnProgress, makeOnFinished ); core.loader._loadTilesets_afterLoad(); }; loader.prototype._loadTilesets_afterLoad = function () { // 检查宽高是32倍数,如果出错在控制台报错 for (var imgName in core.material.images.tilesets) { var img = core.material.images.tilesets[imgName]; if (img.width % 32 != 0 || img.height % 32 != 0) { console.warn("警告!" + imgName + "的宽或高不是32的倍数!"); } if (img.width * img.height > 32 * 32 * 3000) { console.warn("警告!" + imgName + "上的图块素材个数大于3000!"); } } }; // ------ 实际加载一系列图片 ------ // loader.prototype.loadImages = function (dir, names, toSave, callback) { if (!names || names.length == 0) { if (callback) callback(); return; } var items = 0; for (var i = 0; i < names.length; i++) { this.loadImage(dir, names[i], function (id, image) { core.loader._setStartLoadTipText("正在加载图片 " + id + "..."); if (toSave[id] !== undefined) { if (image != null) toSave[id] = image; return; } toSave[id] = image; items++; core.loader._setStartProgressVal(items * (100 / names.length)); if (items == names.length) { if (callback) callback(); } }); } }; loader.prototype.loadImage = function (dir, imgName, callback) { try { var name = imgName; if (name.indexOf(".") < 0) name = name + ".png"; var image = new Image(); image.onload = function () { image.setAttribute("_width", image.width); image.setAttribute("_height", image.height); callback(imgName, image); }; image.onerror = function () { callback(imgName, null); }; image.src = "project/" + dir + "/" + name + "?v=" + main.version; if (name.endsWith(".gif")) callback(imgName, null); } catch (e) { console.error(e); } }; // ------ 从zip中加载一系列图片 ------ // loader.prototype.loadImagesFromZip = function ( url, names, toSave, onprogress, onfinished ) { if (!names || names.length == 0) { if (onfinished) onfinished(); return; } core.unzip( url + "?v=" + main.version, function (data) { var cnt = 1; names.forEach(function (name) { var imgName = name; if (imgName.indexOf(".") < 0) imgName += ".png"; if (imgName in data) { var img = new Image(); var url = URL.createObjectURL(data[imgName]); cnt++; img.onload = function () { cnt--; URL.revokeObjectURL(url); img.setAttribute("_width", img.width); img.setAttribute("_height", img.height); if (cnt == 0 && onfinished) onfinished(); }; img.src = url; toSave[name] = img; } }); cnt--; if (cnt == 0 && onfinished) onfinished(); }, null, false, onprogress ); }; loader.prototype._loadImagesFromChunks = async function ( chunks, names, toSave, makeOnProgress, makeOnFinished ) { await Promise.all( chunks.map((chunk) => { const onfinished = makeOnFinished(chunk); const onprogress = makeOnProgress(chunk); return new Promise((resolve) => { this.loadImagesFromZip(chunk, names, toSave, onprogress, () => { onfinished(); resolve(); }); }); }) ); }; // ------ 加载动画文件 ------ // loader.prototype._loadAnimates_sync = function () { this._setStartLoadTipText("正在加载动画文件..."); if (main.supportBunch) { if (core.animates.length > 0) { core.http( "GET", "__all_animates__?v=" + main.version + "&id=" + core.animates.join(","), null, function (content) { var u = content.split("@@@~~~###~~~@@@"); for (var i = 0; i < core.animates.length; ++i) { if (u[i] != "") { core.material.animates[core.animates[i]] = core.loader._loadAnimate(u[i]); } else { console.error("无法找到动画文件" + core.animates[i] + "!"); } } }, "text/plain; charset=x-user-defined" ); } return; } core.animates.forEach(function (t) { core.http( "GET", "project/animates/" + t + ".animate?v=" + main.version, null, function (content) { core.material.animates[t] = core.loader._loadAnimate(content); }, function (e) { console.error(e); core.material.animates[t] = null; }, "text/plain; charset=x-user-defined" ); }); }; loader.prototype._loadAnimates_async = function (onprogress, onfinished) { this._loadFileFromZip( "project/animates/animates.h5data", this._saveAnimate, true, onprogress, onfinished ); }; loader.prototype._loadAnimates_chunked = function ( chunks, makeOnProgress, makeOnFinished ) { this._loadFileFromChunks( chunks, this._saveAnimate, true, makeOnProgress, makeOnFinished ); }; loader.prototype._saveAnimate = function (animates, onfinished) { for (var name in animates) { if (name.endsWith(".animate")) { var t = name.substring(0, name.length - 8); if (core.animates.indexOf(t) >= 0) core.material.animates[t] = core.loader._loadAnimate(animates[name]); } } onfinished(); }; loader.prototype._loadAnimate = function (content) { try { content = JSON.parse(content); var data = {}; data.ratio = content.ratio; data.se = content.se; data.pitch = content.pitch; data.images = []; content.bitmaps.forEach(function (t2) { if (!t2) { data.images.push(null); } else { try { var image = new Image(); image.src = t2; data.images.push(image); } catch (e) { console.error(e); data.images.push(null); } } }); data.frame = content.frame_max; data.frames = []; content.frames.forEach(function (t2) { var info = []; t2.forEach(function (t3) { info.push({ index: t3[0], x: t3[1], y: t3[2], zoom: t3[3], opacity: t3[4], mirror: t3[5] || 0, angle: t3[6] || 0, }); }); data.frames.push(info); }); return data; } catch (e) { console.error(e); return null; } }; // ------ 加载音乐和音效 ------ // loader.prototype._loadMusics = function () { core.bgms.forEach(function (t) { core.loader.loadOneMusic(t); }); // 直接开始播放 core.playBgm(main.startBgm); }; loader.prototype._loadSounds_sync = function () { this._setStartLoadTipText("正在加载音效文件..."); core.sounds.forEach(function (t) { core.loader.loadOneSound(t); }); }; loader.prototype._loadSounds_async = function (onprogress, onfinished) { this._loadFileFromZip( "project/sounds/sounds.h5data", this._saveSounds, false, onprogress, onfinished ); }; loader.prototype._loadSounds_chunked = function ( chunks, makeOnProgress, makeOnFinished ) { this._loadFileFromChunks( chunks, this._saveSounds, false, makeOnProgress, makeOnFinished ); }; loader.prototype._saveSounds = function (data, onfinished) { // 延迟解析 setTimeout(function () { for (var name in data) { if (core.sounds.indexOf(name) >= 0) { core.loader._loadOneSound_decodeData(name, data[name]); } } onfinished(); }); }; loader.prototype.loadOneMusic = function (name) { var music = new Audio(); music.preload = "none"; if (main.bgmRemote) music.src = main.bgmRemoteRoot + core.firstData.name + "/" + name; else music.src = "project/bgms/" + name; music.loop = "loop"; core.material.bgms[name] = music; }; loader.prototype.loadOneSound = function (name) { core.http( "GET", "project/sounds/" + name + "?v=" + main.version, null, function (data) { core.loader._loadOneSound_decodeData(name, data); }, function (e) { console.error(e); core.material.sounds[name] = null; }, null, "arraybuffer" ); }; loader.prototype._loadOneSound_decodeData = function (name, data) { if (data instanceof Blob) { var blobReader = new zip.BlobReader(data); blobReader.init(function () { blobReader.readUint8Array(0, blobReader.size, function (uint8) { core.loader._loadOneSound_decodeData(name, uint8.buffer); }); }); return; } try { core.musicStatus.audioContext.decodeAudioData( data, function (buffer) { core.material.sounds[name] = buffer; }, function (e) { console.error(e); core.material.sounds[name] = null; } ); } catch (e) { console.error(e); core.material.sounds[name] = null; } }; loader.prototype.loadBgm = function (name) { name = core.getMappedName(name); if (!core.material.bgms[name]) return; // 如果没开启音乐,则不预加载 if (!core.musicStatus.bgmStatus) return; // 是否已经预加载过 var index = core.musicStatus.cachedBgms.indexOf(name); if (index >= 0) { core.musicStatus.cachedBgms.splice(index, 1); } else { // 预加载BGM this._preloadBgm(core.material.bgms[name]); // core.material.bgms[name].load(); // 清理尾巴 if (core.musicStatus.cachedBgms.length == core.musicStatus.cachedBgmCount) { this.freeBgm(core.musicStatus.cachedBgms.pop()); } } // 移动到缓存最前方 core.musicStatus.cachedBgms.unshift(name); }; loader.prototype._preloadBgm = function (bgm) { bgm.volume = 0; bgm.play(); }; loader.prototype.freeBgm = function (name) { name = core.getMappedName(name); if (!core.material.bgms[name]) return; // 从cachedBgms中删除 core.musicStatus.cachedBgms = core.musicStatus.cachedBgms.filter(function ( t ) { return t != name; }); // 清掉缓存 core.material.bgms[name].removeAttribute("src"); core.material.bgms[name].load(); core.material.bgms[name] = null; if (name == core.musicStatus.playingBgm) { core.musicStatus.playingBgm = null; } // 三秒后重新加载 setTimeout(function () { core.loader.loadOneMusic(name); }, 3000); }; loader.prototype._loadFileFromZip = function ( url, save, convertToText, onprogress, onfinished ) { core.unzip( url + "?v=" + main.version, function (data) { save(data, onfinished); }, null, convertToText, onprogress ); }; loader.prototype._loadFileFromChunks = async function ( chunks, save, convertToText, makeOnProgress, makeOnFinished ) { await Promise.all( chunks.map((chunk) => { const onfinished = makeOnFinished(chunk); const onprogress = makeOnProgress(chunk); return new Promise((resolve) => { this._loadFileFromZip(chunk, save, convertToText, onprogress, () => { onfinished(); resolve(); }); }); }) ); };