1. 程式人生 > >Laya資源加載小記

Laya資源加載小記

atl 實例 所有圖片 param dma 選擇 amp error url

  • 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方法。
        /**
         * 加載完成。
         * @param   data 加載的數據。
         */
        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> 。
         * @param   content 加載後的數據
         */
        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的Browser.window.Image方式加載。
    • 創建一個Browser.window.Image的實例。
    • 設置src、onload、onerror方法。
    • 使用imgCache緩存image對象,防止被gc掉。
    • 當圖片被加載完時,會觸發onload回調,清理image的onerror和onload方法,傳遞給下級。
  • nativeimage類型的圖片,會直接將Image的數據傳遞下去。其他類型圖片會使用HtmlImage(Canvas模式下)/WebGLImage(WebGL模式下)將原生Image數據包裝,然後再傳遞給後續調用。
        /**
         * @private
         * 加載圖片資源。
         * @param   url 資源地址。
         */
        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
         * 加載聲音資源。
         * @param   url 資源地址。
         */
        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),內存是不能被銷毀的,此圖片會被大圖合集管理器管理
         * @param   url 資源地址。
         * @param   forceDispose 是否強制銷毀,有些資源是采用引用計數方式銷毀,如果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),內存是不能被銷毀的,此圖片被大圖合集管理器管理
         * @param   url 圖集地址或者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);
                }
            }
        }

Laya資源加載小記