第十六章:AssetManager接口介绍
下面的文章,是我早期阅读jME3源代码时整理的笔记,主要介绍了 AssetManager 中的主要接口。结合上一篇文章,可以深入了解AssetManager的具体用法。
核心组件介绍
AssetManager
这是JME3资源管理器的核心接口,它提供了统一的方式来管理各种资源。
(1) 注册资源加载器
public void registerLoader(Class loaderClass, String ... extensions)
根据后缀名来注册资源加载器。没有注册过的资源类型是无法被AssetManager识别的。
例:
assetManager.registerLoader(AWTLoader.class, "jpg");
assetManager.registerLoader(WAVLoader.class, "wav");
(2) 注册资源定位器
public void registerLocator(String rootPath, Class locatorClass)
注册资源位置,以及定位器。加载资源的时候,AssetManager会到注册过的位置来查找资源。例:
assetManager.registerLocator("/", ClasspathLocator.class);
assetManager.registerLocator("res/models.zip", ZipLocator.class);
(3) 定位资源位置
public AssetInfo locateAsset(AssetKey<?> key)
这个方法将根据AssetKey中的路径,按顺序遍历所有注册过的资源位置,直到查询到了一个匹配的资源为止。
如果找到了资源,就会返回一个AssetInfo对象,否则将返回null。
AssetInfo对象包含了资源的InputStream,我们可以直接解析资源数据,也可以通过AssetLoader来加载资源。
(4) 加载资源
public <T> T loadAsset(AssetKey<T> key);
这个方法用于加载资源,具体的加载过程我们后面再详细分析。
AssetKey中包含了资源的后缀名,若该资源类型的AssetLoader没有在AssetManager中注册过,程序就会抛出异常。
AssetKey
AssetKey是用来从缓存中寻找资源的钥匙,可以使用资源路径来构造一个AssetKey。
AssetKey = new AssetKey("Common/MatDefs/Misc/Unshaded.j3md");
一旦资源路径设置完成后,它的值就无法改变了,因为AssetKey没有提供任何方法来修改资源路径。
AssetKey会自动帮我们计算资源的后缀名、文件夹。
(1) 资源全路径
public String getName()
返回资源的全路径。
"Common/MatDefs/Misc/Unshaded.j3md"
(2) 资源文件夹
public String getFolder()
返回资源所在的文件夹。
"Common/MatDefs/Misc/"
(3) 资源后缀名
后缀名不分大小写。
public String getExtension()
例:
"j3md"
(4) 资源的缓存类型
public Class<? extends AssetCache> getCacheType()
资源加载的同时,会在缓存中保存一份,防止直接被GC回收。
AssetKey默认使用SimpleCacheType,这意味着直接使用JME3自带的AssetKey的话,我们就需要自己手动去释放缓存。。
(5) 资源加载后的处理器
public Class<? extends AssetProcessor> getProcessorType()
默认为null
jpg、tga等图片资源作为纹理加载时,首先会变成一个Image对象。通过TextrueProcesser处理后才会变成一个程序中所需要的Texture对象。
AssetLocator
AssetLocator是一个接口,用于从指定位置查询资源信息。
(1)资源根目录
public void setRootPath(String rootPath)
资源定位器允许我们在指定一个资源加载的根路径。
定位资源的时候,调用AssetKey的getName()方法可以获得资源在这个根目录中的相对位置。
举个例子:注册一个ZipLocatoer,设置资源根目录为"res/models.zip"。查找资源"img/avatar.png"的时候,这个ZipLocatoer就会在models.zip文件找去查询img/avatar.png文件。
(2)定位资源
public AssetInfo locate(AssetManager manager, AssetKey key)
在AssetLocator定位了资源位置后,将会返回一个AssetInfo对象。
AssetInfo
AssetInfo是AssetLocater定位资源后返回的结构,其中提供了指定资源的InputStream。
(1)资源数据
public abstract InputStream openStream();
AssetInfo是一个抽象类,调用openStream()方法即可获得资源的InputStream,通过这个InputStream就可以读取实际的资源数据了。
(2)getKey
public AssetKey getKey()
通过这个方法可以获得资源的AssetKey
(3)getManager
public AssetManager getManager()
通过这个方法可以获得加载该资源的AssetManager
AssetLoader
AssetLoader用于加载指定类型的资源,资源类型通过文件的后缀名来匹配。
AssetLoader接口中只有一个用于加载的接口:
public Object load(AssetInfo assetInfo) throws IOException;
AssetLoader将调用AssetInfo的openStream()方法来获得资源的输入流,并将数据解析成一个我们所需要的对象。
AssetLoader和AssetLocator
AssetManager加载资源前,首先要注册各种AssetLoader和AssetLocator,否则AssetManager将不知道怎么去加载资源。
注册AssetLoader
AssetLoader由AssetManger管理,加载资源时,AssetManager通过后缀名来匹配AssetLoader。
例如:
assetManager.loadAsset("Interfaces/background.jpg");
当这段代码执行时,assetManager会根据后缀名"jpg"去查找AssetLoader实例。如果AssetManager中没有匹配"jpg"后缀的AssetLoader,那么这个资源就在加载不了了。幸好JME3中自带了一些常用类型的资源加载器,并且默认在启动时就给它们注册了,如下:
assetManager.registerLoader(AWTLoader.class, "jpg");
AssetManager使用 Map<String, AssetLoader> 来保存资源后缀名与AssetLoader之间的映射管理。如果2个AssetLoader都注册了同样的后缀名,那么后注册的AssetLoader会挤掉先定义的AssetLoader。
注册AssetLocator
JME3允许你从不同的位置加载资源,诸如:
- Classpath: assets.jar
- 压缩包:assets.zip
- 文件夹:MyGame/Pictures
- URL:http://yourhost/assets/
这些都是通过AssetLocator实现的。
初学JME3时,我们一般是在项目中新建一个assets源码文件夹(source folder),然后在这个文件夹下创建Interface、Model、Material等包(package),然后再需要使用的资源放在这些目录下。加载这些资源时,其实是ClasspathLocater在起作用。
如果你不满足于这种模式,想使用另外地方的资源,那么可以尝试如下的方式:
assetManager.registerLocator("res", FileLocator.class);//注册程序相对路径res
assetManager.registerLocator("C:/", FileLocator.class);// 注册绝对路径(Windows)
assetManager.registerLocator("/usr/yan/myassets/", FileLocator.class);// 注册绝对路径(Linux)
assetManager.registerLocator("/", ClasspathLocator.class);
AssetManager把所有注册过的AssetLocator保存在一个 List<AssetLocater> 中,使用时按从前往后的顺序查找。如果2个AssetLocator都能找到相同路径的资源,那么先注册的AssetLocator会被使用,后面的会被忽略。
默认注册
JME3 Desktop应用启动时,默认注册了下面这些AssetLoader和AssetLocater。
LOCATOR / com.jme3.asset.plugins.ClasspathLocator
LOADER com.jme3.texture.plugins.AWTLoader : jpg, bmp, gif, png, jpeg
LOADER com.jme3.audio.plugins.WAVLoader : wav
LOADER com.jme3.audio.plugins.OGGLoader : ogg
LOADER com.jme3.cursors.plugins.CursorLoader : ani, cur, ico
LOADER com.jme3.material.plugins.J3MLoader : j3m
LOADER com.jme3.material.plugins.J3MLoader : j3md
LOADER com.jme3.material.plugins.ShaderNodeDefinitionLoader : j3sn
LOADER com.jme3.font.plugins.BitmapFontLoader : fnt
LOADER com.jme3.texture.plugins.DDSLoader : dds
LOADER com.jme3.texture.plugins.PFMLoader : pfm
LOADER com.jme3.texture.plugins.HDRLoader : hdr
LOADER com.jme3.texture.plugins.TGALoader : tga
LOADER com.jme3.export.binary.BinaryImporter : j3o
LOADER com.jme3.export.binary.BinaryImporter : j3f
LOADER com.jme3.scene.plugins.OBJLoader : obj
LOADER com.jme3.scene.plugins.MTLLoader : mtl
LOADER com.jme3.scene.plugins.ogre.MeshLoader : meshxml, mesh.xml
LOADER com.jme3.scene.plugins.ogre.SkeletonLoader : skeletonxml, skeleton.xml
LOADER com.jme3.scene.plugins.ogre.MaterialLoader : material
LOADER com.jme3.scene.plugins.ogre.SceneLoader : scene
LOADER com.jme3.scene.plugins.blender.BlenderModelLoader : blend
LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, glsl, glsllib
上面的数据来自JME3 Desktop自带的配置文件:com/jme3/asset/Desktop.cfg
对于Android来说,JME3 Android启动时还加载了drawable文件夹和asset文件夹的Locator,具体是哪些类就不列出来了。
AssetConfig
除了在代码里面直接调用AssetManager的方法来注册,我们还可以利用配置文件来进行注册。
配置文件的格式就和上面的代码一样,AssetConfig类专门用于解析这种配置文件。然而实际上我们在编程的时候几乎不上AssetConfig,只要注意配置的方式就行了。我们这里主要谈谈怎么使用配置文件。
配置文件的使用有3个关键点:
- 配置文件必须放在工程的classpath之下,否则无法识别。
- 要在AppSettings中添加参数"AssetConfigURL",指定配置文件的加载路径。
- 如果使用自定义配置文件,那么jme3默认的配置文件就不会生效了!
你可以在自己的资源目录夹下面创建一个cfg文件,格式和内容可参考JME3自带的cfg文件,然后采用如下方式在程序启动时加载它:
public static void main(String[] args) {
AppSettings settings = new AppSettings(false);
// 设置文件路径
settings.set("AssetConfigURL", "your/asset/path/Assets.cfg");
// 启动程序
SimpleApplication game = new MyGame();
game.setSettings(settings);
game.start();
}
AssetManager初始化时,会从AppSettings中读取"AssetConfigURL"这个参数,然后再读取配置文件。如果找不到配置文件的话,就会使用默认配置文件。源码如下:
private void initAssetManager(){
if (settings != null){
String assetCfg = settings.getString("AssetConfigURL");
if (assetCfg != null){
URL url = null;
try {
url = new URL(assetCfg);
} catch (MalformedURLException ex) {
}
if (url == null) {
url = Application.class.getClassLoader().getResource(assetCfg);
if (url == null) {
logger.log(Level.SEVERE, "Unable to access AssetConfigURL in asset config:{0}", assetCfg);
return;
}
}
assetManager = JmeSystem.newAssetManager(url);
}
}
if (assetManager == null){
assetManager = JmeSystem.newAssetManager(
Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Desktop.cfg"));
}
}
资源加载流程
JME3在加载资源的过程中,AssetManager会先根据AssetKey去缓存中查找资源,如果找得到的话就直接使用,找不到的话才会去AssetLocator注册的路径下搜索。
具体加载的流程是这样的:
在缓存中查找资源
检查AssetCache中的资源,若找不到就进行下一步,若找到就直接返回了。
AssetCache cache = handler.getCache(key.getCacheType());
Object obj = cache != null ? cache.getFromCache(key) : null;
匹配AssetLoader
根据资源后缀名来匹配AssetLoader,找不到的话抛出异常。
AssetLoader loader = handler.aquireLoader(key);
资源定位
遍历所有注册过的AssetLocater,返回AssetInfo,找不到的话会抛出异常。
AssetInfo info = handler.tryLocate(key);
加载资源
调用AssetLoader的load(AssetInfo info)方法,返回资源对象。
obj = loader.load(info);
后续处理
AssetLoader返回Object类型的对象,经过AssetProcessor处理后,转换成实际的对象类型。
比如AWTLoader读取图片数据后,返回Image类型的对象。再通过TextureProcessor处理后才变成Texture对象。
AssetProcessor proc = handler.getProcessor(key.getProcessorType());
if (proc != null){
// do processing on asset before caching
obj = proc.postProcess(key, obj);
}
保存到缓存
资源加载结束后,对象会保存到缓存中。
if (cache != null){
// At this point, obj should be of type T
cache.addToCache(key, (T) obj);
}