Laya資源載入小記
摘要:
Laya.Loader負責資源的載入邏輯,被LoaderManager管理。
Laya支援多種型別資源載入,也支援自定義型別載入。不同型別的載入方式可能不同。
Laya.Loader快取已經被載入過得資源,減少資源重複載入。
提供清理資源介面,由LoaderM...
- Laya.Loader負責資源的載入邏輯,被LoaderManager管理。
- Laya支援多種型別資源載入,也支援自定義型別載入。不同型別的載入方式可能不同。
- Laya.Loader快取已經被載入過得資源,減少資源重複載入。
- 提供清理資源介面,由LoaderManager封裝介面。
- 部分資源載入包含多步載入,如Atlas和Font都包含文字下載和圖片下載。
- 注意:Laya.loader是LoaderManager的例項,是Laya對外的通用載入介面。Laya.Loader由LoaderManager統一管理,一般情況下,開發是不需要自己建立Loader例項。
內建型別
- Laya內部支援的檔案型別有:
/** 文字型別,載入完成後返回文字。*/ public static const TEXT:String = "text"; /** JSON 型別,載入完成後返回json資料。*/ public static const JSON:String = "json"; /** XML 型別,載入完成後返回domXML。*/ public static const XML:String = "xml"; /** 二進位制型別,載入完成後返回arraybuffer二進位制資料。*/ public static const BUFFER:String = "arraybuffer"; /** 紋理型別,載入完成後返回Texture。*/ public static const IMAGE:String = "image"; /** 聲音型別,載入完成後返回sound。*/ public static const SOUND:String = "sound"; /** 圖集型別,載入完成後返回圖集json資訊(並建立圖集內小圖Texture)。*/ public static const ATLAS:String = "atlas"; /** 點陣圖字型型別,載入完成後返回BitmapFont。*/ public static const FONT:String = "font"; /** TTF字型型別,載入完成後返回null。*/ public static const TTF:String = "ttf"; /**@private */ public static const PKM:String = "pkm";
- Laya3D擴充套件型別:
/**@private 層級檔案資源標記。*/ private static const HIERARCHY:String = "SPRITE3DHIERARCHY"; /**@private 網格的原始資源標記。*/ private static const MESH:String = "MESH"; /**@private 材質的原始資源標記。*/ private static const MATERIAL:String = "MATERIAL"; /**@private PBR材質資源標記。*/ private static const PBRMATERIAL:String = "PBRMTL"; /**@private TextureCube原始資源標記。*/ private static const TEXTURECUBE:String = "TEXTURECUBE"; /**@private Terrain原始資源標記。*/ private static const TERRAIN:String = "TERRAIN";
這幾種型別通過擴充套件的方式,在Laya3D初始化時,註冊了對應的載入函式。
- Laya檔案字尾與檔案型別的對映:
//Laya內建型別 {"png": "image","jpg": "image","jpeg": "image", "txt": "text", "json": "json", "xml": "xml", "als": "atlas","atlas": "atlas", "mp3": "sound", "ogg": "sound", "wav": "sound", "part": "json", "fnt": "font", "pkm": "pkm", "ttf": "ttf"}; //Laya3D擴充套件 //通過擴充套件LoaderManager.createMap新增對應型別的解析。只對LoaderManager.create方法有效。 createMap["lh"] = [Sprite3D, Laya3D.HIERARCHY]; createMap["ls"] = [Scene, Laya3D.HIERARCHY]; createMap["lm"] = [Mesh, Laya3D.MESH]; createMap["lmat"] = [StandardMaterial, Laya3D.MATERIAL]; createMap["lpbr"] = [PBRMaterial, Laya3D.MATERIAL]; createMap["ltc"] = [TextureCube, Laya3D.TEXTURECUBE]; createMap["jpg"] = [Texture2D, "nativeimage"]; createMap["jpeg"] = [Texture2D, "nativeimage"]; createMap["png"] = [Texture2D, "nativeimage"]; createMap["pkm"] = [Texture2D, Loader.BUFFER]; createMap["lsani"] = [AnimationTemplet, Loader.BUFFER]; createMap["lrani"] = [AnimationTemplet, Loader.BUFFER]; createMap["raw"] = [DataTexture2D, Loader.BUFFER]; createMap["mipmaps"] = [DataTexture2D, Loader.BUFFER]; createMap["thdata"] = [TerrainHeightData, Loader.BUFFER]; createMap["lt"] = [TerrainRes, Laya3D.TERRAIN]; createMap["lani"] = [AnimationClip, Loader.BUFFER]; createMap["lav"] = [Avatar, Loader.JSON]; createMap["ani"] = [AnimationTemplet, Loader.BUFFER];//相容介面
資源載入基礎流程
public function load(url:String, type:String = null, cache:Boolean = true, group:String = null, ignoreCache:Boolean = false):void 載入資源。載入錯誤會派發 Event.ERROR 事件,引數為錯誤資訊。 Parameters url:String — 資源地址。 type:String (default = null) — (default = null)資源型別。可選值為:Loader.TEXT、Loader.JSON、Loader.XML、Loader.BUFFER、Loader.IMAGE、Loader.SOUND、Loader.ATLAS、Loader.FONT。如果為null,則根據檔案字尾分析型別。 cache:Boolean (default = true) — (default = true)是否快取資料。 group:String (default = null) — (default = null)分組名稱。 ignoreCache:Boolean (default = false) — (default = false)是否忽略快取,強制重新載入。
- 快取url、type、cache等資料,供載入完成或者後續載入使用。
- 如果資源已經載入過,並且沒有設定ignoreCache則直接出發COMPLETE事件,通知載入完成。
- 如果定製了載入方法,如Laya3D中註冊的方法,則直接使用對應方法進行載入。
- 根據type選擇對應載入方法載入資源,如果沒有傳type,則會根據資源字尾名確定型別。
- 資源載入完成後,觸發onLoaded方法,將載入完的資料根據型別進行封裝或者後續載入(如atlas型別載入完資源後,會解析配置,再去載入對應的圖片)。
- 呼叫complete方法,將data快取在loader中,再將loader放入到完成佇列。
- 執行endload方法,快取資源,通知COMPLETE事件,LoaderManager觸發傳入的complete方法。
- 如果累計回撥時長大於100毫秒時,延時一幀再執行後續loader的endload方法。
/** * 載入完成。 * @paramdata 載入的資料。 */ protected function complete(data:*):void { this._data = data; if (_customParse) { event(Event.LOADED, data is Array ? [data] : data); } else { _loaders.push(this); if (!_isWorking) checkNext(); } } /** @private */ private static function checkNext():void { _isWorking = true; var startTimer:Number = Browser.now(); var thisTimer:Number = startTimer; while (_startIndex < _loaders.length) { thisTimer = Browser.now(); _loaders[_startIndex].endLoad(); _startIndex++; //@防止單次回撥事件太長,卡程序 if (Browser.now() - startTimer > maxTimeOut) { console.warn("loader callback cost a long time:" + (Browser.now() - startTimer) + " url=" + _loaders[_startIndex - 1].url); Laya.timer.frameOnce(1, null, checkNext); return; } } _loaders.length = 0; _startIndex = 0; _isWorking = false; } /** * 結束載入,處理是否快取及派發完成事件 <code>Event.COMPLETE</code> 。 * @paramcontent 載入後的資料 */ public function endLoad(content:* = null):void { content && (this._data = content); if (this._cache) cacheRes(this._url, this._data); event(Event.PROGRESS, 1); event(Event.COMPLETE, data is Array ? [data] : data); }
圖片資源載入
- 字尾為png、jpg、jpeg以及型別為htmlimage或者nativeimage的資源,是使用圖片型別載入。
-
圖片型別的載入使用過使用H5的API/HTMLImageElement/Image" rel="nofollow,noindex" target="_blank">Browser.window.Image
方式載入。
- 建立一個Browser.window.Image的例項。
- 設定src、onload、onerror方法。
- 使用imgCache快取image物件,防止被gc掉。
- 當圖片被載入完時,會觸發onload回撥,清理image的onerror和onload方法,傳遞給下級。
- nativeimage型別的圖片,會直接將Image的資料傳遞下去。其他型別圖片會使用HtmlImage(Canvas模式下)/WebGLImage(WebGL模式下)將原生Image資料包裝,然後再傳遞給後續呼叫。
/** * @private * 載入圖片資源。 * @paramurl 資源地址。 */ protected function _loadImage(url:String):void { url = URL.formatURL(url); var _this:Loader = this; var image:*; function clear():void { image.onload = null; image.onerror = null; delete imgCache[url] } var onload:Function = function():void { clear(); _this.onLoaded(image); }; var onerror:Function = function():void { clear(); _this.event(Event.ERROR, "Load image failed"); } if (_type === "nativeimage") { image = new Browser.window.Image(); image.crossOrigin = ""; image.onload = onload; image.onerror = onerror; image.src = url; //增加引用,防止垃圾回收 imgCache[url] = image; } else { new HTMLImage.create(url, {onload: onload, onerror: onerror, onCreate: function(img:*):void { image = img; //增加引用,防止垃圾回收 imgCache[url] = img; }}); } }
文字型別載入
- 簡單型別如json、buffer等型別,直接通過http請求下載。
- Atlas/Font型別,會先通過這種方式下載配置檔案,再執行後續操作。
var contentType:String; switch (type) { case ATLAS: contentType = JSON; break; case FONT: contentType = XML; break; case PKM: contentType = BUFFER; break default: contentType = type; } if (preLoadedMap[url]) { onLoaded(preLoadedMap[url]); }else { if (!_http) { _http = new HttpRequest(); _http.on(Event.PROGRESS, this, onProgress); _http.on(Event.ERROR, this, onError); _http.on(Event.COMPLETE, this, onLoaded); } _http.send(url, null, "get", contentType); }
聲音型別載入
- 對聲音資源的載入,Laya封裝到Sound類裡面。Laya支援三種sound型別:H5方式、web audio api方式、微信小遊戲方式。
- H5方式通過原生audio標籤去載入聲音。
- web audio方式是通過http請求方式下載。
- 微信小遊戲是微信提供方式下載。
- 聲音載入完成後,外部接受的為Sound物件,而不是語音的資料。
/** * @private * 載入聲音資源。 * @paramurl 資源地址。 */ protected function _loadSound(url:String):void { var sound:Sound = (new SoundManager._soundClass()) as Sound; var _this:Loader = this; sound.on(Event.COMPLETE, this, soundOnload); sound.on(Event.ERROR, this, soundOnErr); sound.load(url); function soundOnload():void { clear(); _this.onLoaded(sound); } function soundOnErr():void { clear(); sound.dispose(); _this.event(Event.ERROR, "Load sound failed"); } function clear():void { sound.offAll(); } }
圖集載入
- 圖集型別一般包含一份配置檔案和一張或多張貼圖。
- 先用Http方式下載配置檔案。並且設定當前型別為ATLAS型別。
- 當配置檔案下載完成後,解析meta欄位,獲取需要下載的圖片地址,使用下載圖片的方式下載對應圖片。
- 所有圖片下載完成後,解析配置的frames,解析圖集內包含的圖片資訊,為每個圖片建立一個Texture,並將Texture放入到loadedMap中,key為圖片原始路徑。即使圖片在圖集中,也可以通過設定單張圖片的url來獲取圖片資源。
- 將圖集裡所有的圖片的url已陣列的形式存入atlasmap中,key為圖集地址。
if (type === ATLAS) { //處理圖集 if (!data.src && !data._setContext) { //@處理.atlas檔案 if (!_data) { this._data = data; //構造載入圖片資訊 if (data.meta && data.meta.image) { //帶圖片資訊的型別 var toloadPics:Array = data.meta.image.split(","); var split:String = _url.indexOf("/") >= 0 ? "/" : "\\"; var idx:int = _url.lastIndexOf(split); var folderPath:String = idx >= 0 ? _url.substr(0, idx + 1) : ""; //idx = _url.indexOf("?"); //var ver:String; //ver = idx >= 0 ? _url.substr(idx) : ""; for (var i:int = 0, len:int = toloadPics.length; i < len; i++) { toloadPics[i] = folderPath + toloadPics[i]; } } else { //不帶圖片資訊 toloadPics = [_url.replace(".json", ".png")]; } //保證圖集的正序載入 toloadPics.reverse(); data.toLoads = toloadPics; data.pics = []; } event(Event.PROGRESS, 0.3 + 1 / toloadPics.length * 0.6); return _loadImage(toloadPics.pop()); } else { //處理圖片 _data.pics.push(data); if (_data.toLoads.length > 0) { event(Event.PROGRESS, 0.3 + 1 / _data.toLoads.length * 0.6); //有圖片未載入 return _loadImage(_data.toLoads.pop()); } var frames:Object = this._data.frames; var cleanUrl:String = this._url.split("?")[0]; var directory:String = (this._data.meta && this._data.meta.prefix) ? this._data.meta.prefix : cleanUrl.substring(0, cleanUrl.lastIndexOf(".")) + "/"; var pics:Array = _data.pics; var atlasURL:String = URL.formatURL(this._url); var map:Array = atlasMap[atlasURL] || (atlasMap[atlasURL] = []); map.dir = directory; var scaleRate:Number = 1; if (this._data.meta && this._data.meta.scale && this._data.meta.scale != 1) { scaleRate = parseFloat(this._data.meta.scale); for (var name:String in frames) { var obj:Object = frames[name];//取對應的圖 var tPic:Object = pics[obj.frame.idx ? obj.frame.idx : 0];//是否釋放 var url:String = URL.formatURL(directory + name); tPic.scaleRate = scaleRate; cacheRes(url, Texture.create(tPic, obj.frame.x, obj.frame.y, obj.frame.w, obj.frame.h, obj.spriteSourceSize.x, obj.spriteSourceSize.y, obj.sourceSize.w, obj.sourceSize.h)); loadedMap[url].url = url; map.push(url); } }else{ for (name in frames) { obj = frames[name];//取對應的圖 tPic = pics[obj.frame.idx ? obj.frame.idx : 0];//是否釋放 url = URL.formatURL(directory + name); cacheRes(url, Texture.create(tPic, obj.frame.x, obj.frame.y, obj.frame.w, obj.frame.h, obj.spriteSourceSize.x, obj.spriteSourceSize.y, obj.sourceSize.w, obj.sourceSize.h)); loadedMap[url].url = url; map.push(url); } } delete _data.pics; /*[IF-FLASH]*/ map.sort(); complete(this._data); }
字型資源
- Laya有兩種字型,一種是TTF字型一種是bitmapfont。
- 載入bitmapfont是先載入配置檔案,再將.fnt改為.png去載入圖片。資源都加在完成後,使用BitmapFont去解析圖集字型資訊。
- TTF字型使用TTFLoader去載入,通過根據情況有多種載入方式,有使用FontFace方式,也有通過CSS等方式等。
var tArr:Array = fontPath.split(".ttf")[0].split("/"); fontName = tArr[tArr.length - 1]; if (Browser.window.conch) { _loadConch(); }else if (Browser.window.FontFace) { this._loadWithFontFace() } else { this._loadWithCSS(); }
資源清理方式
/** * 清理指定資源地址的快取。 * 如果是Texture,則採用引用計數方式銷燬,【注意】如果圖片本身在自動合集裡面(預設圖片小於512*512),記憶體是不能被銷燬的,此圖片會被大圖合集管理器管理 * @paramurl 資源地址。 * @paramforceDispose 是否強制銷燬,有些資源是採用引用計數方式銷燬,如果forceDispose=true,則忽略引用計數,直接銷燬,比如Texture,預設為false */ public static function clearRes(url:String, forceDispose:Boolean = false):void { url = URL.formatURL(url); //刪除圖集 var arr:Array = getAtlas(url); if (arr) { for (var i:int = 0, n:int = arr.length; i < n; i++) { var resUrl:String = arr[i]; var tex:Texture = getRes(resUrl); delete loadedMap[resUrl]; if (tex) tex.destroy(forceDispose); } arr.length = 0; delete atlasMap[url]; delete loadedMap[url]; } else { var res:* = loadedMap[url]; if (res) { delete loadedMap[url]; if (res is Texture && res.bitmap) Texture(res).destroy(forceDispose); } } } /** * 銷燬Texture使用的圖片資源,保留texture殼,如果下次渲染的時候,發現texture使用的圖片資源不存在,則會自動恢復 * 相比clearRes,clearTextureRes只是清理texture裡面使用的圖片資源,並不銷燬texture,再次使用到的時候會自動恢復圖片資源 * 而clearRes會徹底銷燬texture,導致不能再使用;clearTextureRes能確保立即銷燬圖片資源,並且不用擔心銷燬錯誤,clearRes則採用引用計數方式銷燬 * 【注意】如果圖片本身在自動合集裡面(預設圖片小於512*512),記憶體是不能被銷燬的,此圖片被大圖合集管理器管理 * @paramurl 圖集地址或者texture地址,比如 Loader.clearTextureRes("res/atlas/comp.atlas"); Loader.clearTextureRes("hall/bg.jpg"); */ public static function clearTextureRes(url:String):void { url = URL.formatURL(url); //刪除圖集 var arr:Array = Loader.getAtlas(url); var res:* = (arr && arr.length>0) ? Loader.getRes(arr[0]) : Loader.getRes(url); if (res && res.bitmap) { if (Render.isConchApp) { //相容老版本 if (res.bitmap.source.releaseTexture) { res.bitmap.source.releaseTexture(); } } else if (res.bitmap._atlaser == null) { res.bitmap.releaseResource(true); } } }