Laya资源加载小记
Laya.Loader負責資源的加載邏輯,被LoaderManager管理。
Laya支持多種類型資源加載,也支持自定義類型加載。不同類型的加載方式可能不同。
Laya.Loader緩存已經(jīng)被加載過得資源,減少資源重復(fù)加載。
提供清理資源接口,由LoaderManager封裝接口。
部分資源加載包含多步加載,如Atlas和Font都包含文本下載和圖片下載。
注意:Laya.loader是LoaderManager的實例,是Laya對外的通用加載接口。Laya.Loader由LoaderManager統(tǒng)一管理,一般情況下,開發(fā)是不需要自己創(chuàng)建Loader實例。
內(nèi)置類型
Laya內(nèi)部支持的文件類型有:
/** 文本類型,加載完成后返回文本。*/
public static const TEXT:String = "text";
/** JSON 類型,加載完成后返回json數(shù)據(jù)。*/
public static const JSON:String = "json";
/** XML 類型,加載完成后返回domXML。*/
public static const XML:String = "xml";
/** 二進制類型,加載完成后返回arraybuffer二進制數(shù)據(jù)。*/
public static const BUFFER:String = "arraybuffer";
/** 紋理類型,加載完成后返回Texture。*/
public static const IMAGE:String = "image";
/** 聲音類型,加載完成后返回sound。*/
public static const SOUND:String = "sound";
/** 圖集類型,加載完成后返回圖集json信息(并創(chuàng)建圖集內(nèi)小圖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 網(wǎng)格的原始資源標記。*/
private static const MESH:String = "MESH";
/**@private 材質(zhì)的原始資源標記。*/
private static const MATERIAL:String = "MATERIAL";
/**@private PBR材質(zhì)資源標記。*/
private static const PBRMATERIAL:String = "PBRMTL";
/**@private TextureCube原始資源標記。*/
private static const TEXTURECUBE:String = "TEXTURECUBE";
/**@private Terrain原始資源標記。*/
private static const TERRAIN:String = "TERRAIN";
這幾種類型通過擴展的方式,在Laya3D初始化時,注冊了對應(yīng)的加載函數(shù)。
Laya文件后綴與文件類型的映射:
//Laya內(nèi)置類型
{"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添加對應(yīng)類型的解析。只對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];//兼容接口
資源加載基礎(chǔ)流程
public function load(url:String, type:String = null, cache:Boolean = true, group:String = null, ignoreCache:Boolean = false):void
加載資源。加載錯誤會派發(fā) Event.ERROR 事件,參數(shù)為錯誤信息。
Parameters
url:String — 資源地址。
type:String (default = null) — (default = null)資源類型??蛇x值為:Loader.TEXT、Loader.JSON、Loader.XML、Loader.BUFFER、Loader.IMAGE、Loader.SOUND、Loader.ATLAS、Loader.FONT。如果為null,則根據(jù)文件后綴分析類型。
cache:Boolean (default = true) — (default = true)是否緩存數(shù)據(jù)。
group:String (default = null) — (default = null)分組名稱。
ignoreCache:Boolean (default = false) — (default = false)是否忽略緩存,強制重新加載。
緩存url、type、cache等數(shù)據(jù),供加載完成或者后續(xù)加載使用。
如果資源已經(jīng)加載過,并且沒有設(shè)置ignoreCache則直接出發(fā)COMPLETE事件,通知加載完成。
如果定制了加載方法,如Laya3D中注冊的方法,則直接使用對應(yīng)方法進行加載。
根據(jù)type選擇對應(yīng)加載方法加載資源,如果沒有傳type,則會根據(jù)資源后綴名確定類型。
資源加載完成后,觸發(fā)onLoaded方法,將加載完的數(shù)據(jù)根據(jù)類型進行封裝或者后續(xù)加載(如atlas類型加載完資源后,會解析配置,再去加載對應(yīng)的圖片)。
調(diào)用complete方法,將data緩存在loader中,再將loader放入到完成隊列。
執(zhí)行endload方法,緩存資源,通知COMPLETE事件,LoaderManager觸發(fā)傳入的complete方法。
如果累計回調(diào)時長大于100毫秒時,延時一幀再執(zhí)行后續(xù)loader的endload方法。
/**
* 加載完成。
* @param data 加載的數(shù)據(jù)。
*/
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++;
//@防止單次回調(diào)事件太長,卡進程
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;
}
/**
* 結(jié)束加載,處理是否緩存及派發(fā)完成事件 <code>Event.COMPLETE</code> 。
* @param content 加載后的數(shù)據(jù)
*/
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方式加載。
創(chuàng)建一個Browser.window.Image的實例。
設(shè)置src、onload、onerror方法。
使用imgCache緩存image對象,防止被gc掉。
當圖片被加載完時,會觸發(fā)onload回調(diào),清理image的onerror和onload方法,傳遞給下級。
nativeimage類型的圖片,會直接將Image的數(shù)據(jù)傳遞下去。其他類型圖片會使用HtmlImage(Canvas模式下)/WebGLImage(WebGL模式下)將原生Image數(shù)據(jù)包裝,然后再傳遞給后續(xù)調(diào)用。
/**
* @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類型,會先通過這種方式下載配置文件,再執(zhí)行后續(xù)操作。
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對象,而不是語音的數(shù)據(jù)。
/**
* @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方式下載配置文件。并且設(shè)置當前類型為ATLAS類型。
當配置文件下載完成后,解析meta字段,獲取需要下載的圖片地址,使用下載圖片的方式下載對應(yīng)圖片。
所有圖片下載完成后,解析配置的frames,解析圖集內(nèi)包含的圖片信息,為每個圖片創(chuàng)建一個Texture,并將Texture放入到loadedMap中,key為圖片原始路徑。即使圖片在圖集中,也可以通過設(shè)置單張圖片的url來獲取圖片資源。
將圖集里所有的圖片的url已數(shù)組的形式存入atlasmap中,key為圖集地址。
if (type === ATLAS) {
//處理圖集
if (!data.src && !data._setContext) {
//@處理.atlas文件
if (!_data) {
this._data = data;
//構(gòu)造加載圖片信息
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];//取對應(yīng)的圖
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];//取對應(yīng)的圖
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去加載,通過根據(jù)情況有多種加載方式,有使用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,則采用引用計數(shù)方式銷毀,【注意】如果圖片本身在自動合集里面(默認圖片小于512*512),內(nèi)存是不能被銷毀的,此圖片會被大圖合集管理器管理
* @param url 資源地址。
* @param forceDispose 是否強制銷毀,有些資源是采用引用計數(shù)方式銷毀,如果forceDispose=true,則忽略引用計數(shù),直接銷毀,比如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殼,如果下次渲染的時候,發(fā)現(xiàn)texture使用的圖片資源不存在,則會自動恢復(fù)
* 相比clearRes,clearTextureRes只是清理texture里面使用的圖片資源,并不銷毀texture,再次使用到的時候會自動恢復(fù)圖片資源
* 而clearRes會徹底銷毀texture,導致不能再使用;clearTextureRes能確保立即銷毀圖片資源,并且不用擔心銷毀錯誤,clearRes則采用引用計數(shù)方式銷毀
* 【注意】如果圖片本身在自動合集里面(默認圖片小于512*512),內(nèi)存是不能被銷毀的,此圖片被大圖合集管理器管理
* @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);
}
}
}
總結(jié)
以上是生活随笔為你收集整理的Laya资源加载小记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 花胶为什么那么贵?
- 下一篇: T-SQL 之 存储过程