Using Resource System

dom edited this page Jul 7, 2014 · 36 revisions

使用 Egret 的资源加载机制(RES)

1. RES模块是什么?

Egret提供了一套默认的资源加载管理模块,在"RES"这个命名空间下。RES是一个可选的模块,与Egret核心库完全独立。开发者可以自行选择是否采用它,或使用自定义的第三方资源加载管理库。

2. RES配置文件格式

这里我们以Egret模板项目的resource.json为例:

{

"resources": [ {"name":"bgImage","type":"image","url":"assets/bg.jpg"}, {"name":"egretIcon","type":"image","url":"assets/egret_icon.png"}, {"name":"description","type":"json","url":"config/description.json"} ],

"groups": [ {"name":"preload","keys":"bgImage,egretIcon"} ] }

resources下是每一项资源的配置信息,name表示这个资源的唯一短名标识符,type表示资源类型,url是资源文件的路径。

groups下是预加载资源组的配置,每项是一个资源组。name表示资源组的组名,keys表示这个资源组包含哪些资源,里面的逗号分隔的每一个字符串,都与resources下的资源name对应。同一个资源可以存在于多个资源组里。

附上RES配置文件可视化管理工具:ResTool ( beta ) ,

3. 如何初始化?

调用RES.loadConfig()加载之前的resource.json配置文件即可完成初始化。loadConfig()第一个参数是resource.json文件的完整路径,第二个参数是resource.json里每个资源项url的前缀。例如配置里的bgImage资源项填的url是assets/bg.jpg,在这里实际加载时将会采用resources/assets/bg.jpg来加载:


RES.addEventListener(RES.ResourceEvent.CONFIG_COMPLETE,this.onConfigComp,this); RES.loadConfig("resources/resource.json","resources/");

若需要在初始化完成后再做一些处理,监听ResourceEvent.CONFIG_COMPLETE事件即可。

注意:RES.loadConfig()通常应写在整个游戏最开始初始化的地方,并且只执行一次。

4. 如何预加载资源组?

在配置文件加载完成后,我们可以调用RES.loadConfig()开始预加载配置中的一组资源。第一个参数"preload"对应配置文件中的资源组名。预加载可以在游戏启动时,也可以是某个面板被打开前,调用时机由具体项的目逻辑确定:

RES.addEventListener(RES.ResourceEvent.GROUP_COMPLETE,this.onResourceLoadComplete,this);

RES.addEventListener(RES.ResourceEvent.GROUP_PROGRESS,this.onResourceProgress,this); RES.loadGroup("preload");

这里需要注意下组加载事件回调函数里的写法,需要使用event.groupName判断下这个事件是属于哪个资源组,因为可能有多个资源组同时在加载:

private onResourceProgress(event:RES.ResourceEvent):void {
if(event.groupName=="preload"){
    this.loadingView.setProgress(event.itemsLoaded,event.itemsTotal);
}

}

若同时启动多个资源组一起加载,比如在加载"preload"前,我们希望先加载一个更小的"loading"资源组,以提供显示"preload"组加载进度的素材,可以使用RES.loadGroup()的第二个参数,为"loading"组传入一个优先级更大的数字,来迫使loading组在preload前加载完成:


RES.loadGroup("loading",1); RES.loadGroup("preload",0);

5. 如何运行时动态创建资源组?

若资源组无法预先配置在文件中,需要运行时才能动态确定的,我们可以通过调用RES.createGroup(groupName:string,keys:Array)方法,动态创建一个资源组,再加载它。

groupName是要创建的资源组组名。keys这个资源组包含的资源列表。里面的key对应配置文件里的name属性或一个资源组名,若key是资源组名将类似合并资源组的功能。你可以同时传入多个已存在资源组名,合并成一个新资源组,再调用loadGroup()一次加载完。注意:createGroup()方法是基于已存在的配置属性操作的,调用这个方法前请先确认RES的配置文件已经加载完成了。也就是在监听到ResourceEvent.CONFIG_COMPLETE事件之后。

6. 如何获取资源?(三种获取方式以及如何直接获取文件中的子资源)

RES一共含有三种资源获取方式:

  • RES.getRes(name:string):any
    同步获取资源 这种方式只能获取已经缓存过的资源,例如之前调用过loadGroup()被预加载的资源。
  • RES.getResAsync(name:string,compFunc:Function,thisObject:any):void
    异步获取资源,这种方式可以获取配置中含有的所有资源项。如果缓存中存在,直接调用回调函数返回,若不存在,就启动网络加载文件并解析后回调。
  • RES.getResByUrl(url:string,compFunc:Function,thisObject:any,type:string=""):void
    通过url获取不在配置中的资源,通常不建议使用这个接口,只有那些不合适填写在配置中,比如获取网络上其他服务器的资源时,才采用这种方式。

前两种获取方式的name参数都对应配置文件里资源项的name属性。如果name对应的文件是SpriteSheet等含有多个子资源的类型,可以使用"."语法直接获取到子资源。例如配置里有一个name为"icons"的SpriteSheet文件,它里面含有一个"activity_10"的子位图,我们要获取这个子位图。以下两种写法是等效的:

var spriteSheet:egret.SpriteSheet = RES.getRes("icons");

var texture = spriteSheet.getTexture("activity_10"); //等同于这样取: var texture:egret.Texture = RES.getRes("icons.activity_10");

这种方式在与GUI的UIAsset无缝桥接时将会非常有用。

7. 资源的缓存机制

resources节点下配置的每个资源加载项,在第一次加载成功时会用name属性作为key缓存下来。以后再请求它时,都直接从缓存里取。如果有两个组都含有一个资源,第二个组再加载这个资源时,也会直接从缓存里得到结果,不会重复发起加载请求。通过RES.getResByUrl()获取的资源,使用url作为name缓存下来。

8. 如何销毁缓存的资源?

Resource在第一次加载资源后,会缓存下来这个资源。使用RES.destroyRes(name:string):boolean,传入资源文件的name,即可清理对应的缓存。传入资源组名,即可清理整个资源组里所有资源对应的缓存。如果要销毁通过RES.getResByUrl(url)加载的资源,传入url作为name即可。

由于目前JS里没有弱引用字典,无法实现资源的自动回收,所以还需要手动去销毁缓存的资源。未来ECMAScript 6普及后,会有WeakMap类,那时我们将不再需要手动清理缓存的资源。

9. RES内置了哪些文件类型的解析器?

目前RES内置支持的文件类型有:

  • RES.ResourceItem.TYPE_BIN(bin):解析为原始的二进制文件
  • RES.ResourceItem.TYPE_IMAGE(image):解析为egret.Texture对象
  • RES.ResourceItem.TYPE_TEXT(text):解析为string变量
  • RES.ResourceItem.TYPE_JSON(json):解析为json对象
  • RES.ResourceItem.TYPE_SHEET(sheet):解析为egret.SpriteSheet对象
  • RES.ResourceItem.TYPE_FONT(font):解析为egret.BitmapTextSpriteSheet对象
  • RES.ResourceItem.TYPE_SOUND():解析为egret.Sound对象

10. 如何扩展文件格式以及自定义文件解析器?

自定义解析器请参考内置解析器的写法,继承自AnalyzerBase实现相关接口即可。然后在调用RES.loadConfig()之前,使用下面的方式注入你的自定义解析器到框架内:

egret.Injector.mapClass(RES.AnalyzerBase,YourAnalyzer,"yourType");

这里的"yourType"就是你在配置中填写的那个type值。RES在加载时,会根据你填写的type值,调用你注入的对应解析器来加载解析文件。另外,这种注入方式同样也支持替换掉默认的内置解析器.例如把第三个参数"yourType"改成RES.ResourceItem.TYPE_IMAGE,默认的图片解析器就被替换掉了。

11. 图片类型如何配置九宫格参数?

图片类型的解析器是支持九宫格参数的,只要在资源项上加上"scale9grid"属性即可,示例:

{"name":"button","scale9grid":"22,0,10,60","type":"image","url":"assets/button.png"}

其中scale9grid属性的四个值分别表示九宫格矩形的:x,y,width,height,与Flash里的九宫格参数表现一致。

12. 如何与GUI系统无缝桥接?

通过前面的说明,相信大家已经明白了RES的基本思路:就是通过一个简短的唯一字符串,映射一个长url,文件类型以及其他参数。这样在代码里我们可以用非常优雅的方式来获取资源,只需要一个字符串短名,其他的初始化和参数配置都已经自动完成了。这样做还有个好处,隔离了具体的url,当需要制作多语言版本的资源时,只需要换一个配置文件,把url映射到其他文件,代码里引用的name字符串都无需修改。

虽然现在获取资源的方式已经很简洁了,但是没有预加载的资源仍需要异步回调的方式获取,还是略微不太方便,现在我们来实现最后一步优化,注入RES到GUI的UIAsset解析器里,屏蔽掉所有异步加载的细节。

首先,实现一个自定义的素材解析器AssetAdapter:

class AssetAdapter implements egret.IAssetAdapter{
public getAsset(source:any,compFunc:Function,thisObject:any,oldContent:any):void{
    var content:any = source;
    if(source.prototype){
        content = new source();
    }
    if(content instanceof egret.DisplayObject||content instanceof egret.Texture){
        compFunc.call(thisObject,content,source);
    }
    else if(typeof(source)=="string"){
        if(RES.hasRes(source)){
            RES.getResAsync(source,onGetRes,this);
        }
        else{
           RES.getResByUrl(source,onGetRes,this);
        }
        function onGetRes(data:any):void{
            compFunc.call(thisObject,data,source);
        }
    }
    else{
        compFunc.call(thisObject,content,source);
    }
}

} 关键就是这一句:RES.getResAsync(source,onGetRes,this);若传入的source是字符串,就调用RES去解析这个字符串,并回调结果。

接下来我们在GUI初始化前加上这句,把我们自定义的AssetAdapter注入到框架内:
egret.Injector.mapClass("egret.IAssetAdapter",AssetAdapter);

做完这个注入后我们基本上就可以忘记RES的存在了。要显示一个位图,可以直接这样写:

var sky:egret.UIAsset = new egret.UIAsset();

sky.source = "bgImage";

或前面提到的显示一个SpriteSheet里的一张子位图:

var icon:egret.UIAsset = new egret.UIAsset();

icon.source = "icons.activity_10";

你不用关心它怎么加载的,不用写任何回调监听。直接把这个显示对象加到显示列表即可。

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.