第十二章:自定义资产路径
默认情况下,JME3 程序总是从 MyGame/assets
目录中加载资产。但这并不是唯一的选择,还可以在 AssetManager 中配置其他资产路径。
本文将介绍如何使用 jME3 提供的 AssetLocator 来配置自定义资产路径。后续章节中我会继续介绍 AssetLocator 的原理,并通过案例来演示如何实现从加密资源包中解析数据。在了解原理后,你可以根据具体的算法来实现自己的 AssetLocator。
AssetLocator
在介绍具体的做法之前,先考虑一下可能存在的情况:
- 游戏允许用户上传自定义模型和皮肤,这些文件不可能打包到游戏的可执行文件中,应该怎么加载它们?
- 我们的项目计划使用内容分发网络(CDN)和云存储(cloud storage)服务来管理游戏资产,这样用户只需要下载一个很小的客户端。应该如何访问网络服务器上的游戏资产呢?
- 我想和魔兽世界一样把游戏资产打成一个加密资源包,避免用户篡改数据,同时避免同行窃取游戏资产。应该如何从这些加密资源包中加载数据呢?
jME3 设计了抽象的资产定位器(AssetLocator),用于扩展支持上述所有情况。jME3的核心模块中提供了 5 种不同类型的 AssetLocator 实现。
- com.jme3.asset.plugins.ClasspathLocator 用于加载 classpath 中的资产,包括 jar 文件。
- com.jme3.asset.plugins.FileLocator 用于加载文件系统中的资产。
- com.jme3.asset.plugins.HttpZipLocator 用于加载网站上的zip压缩包。
- com.jme3.asset.plugins.UrlLocator 用于加载网络资产。
- com.jme3.asset.plugins.ZipLocator 用于加载 zip 压缩包中的资产。
通过调用 AssetManager 中的 registerLocator(String rootPath, Class locatorClass)
方法,即可注册资产路径。这个方法有两个参数:其一是资产的根目录,其二是 AssetLocator 类型。
ClasspathLocator
jME3程序能够从项目的 assets
目录中加载资产,是因为 SDK 已经把 assets 目录配置成了项目的 classpath,并且在 JME3 程序初始化时就执行下列语句:
assetManager.registerLocator("/", ClasspathLocator.class);
因此,AssetManager 才可以从 assets
目录中加载资产,还可以直接读取 jme3-core.jar
中的内置资产。
回顾 第五章:在其他IDE中管理资产,我在 Eclipse 中把 assets
目录配置成了 Build Path
,在 IDEA 中把 assets
目录配置成了 Resources Root
,实质上就是把它们添加到项目的 classpath 中,这样 AssetManager 就可以通过 ClasspathLocator 加载这些目录下的资产。
对于 Maven、Gradle 等项目来说, src/main/resources
也属于 classpath,因此存放其中的资产可以被加载。
手动打包jar文件
如果把资产打包成 assets.jar
文件,并其添加到项目的依赖库中,这样 ClasspathLocator 还可以直接加载 assets.jar
中的资产文件。
做法很简单:jar
文件本身使用的 zip
压缩,先把资源打包成 zip
文件,然后把后缀名改成 jar
即可。
注意:只压缩 assets
目录内的文件,不要压缩 assets
目录本身。
压缩好的 assets.zip
文件结构如下:
把后缀名改成 jar,然后添加到项目的依赖库中。下图是在 Eclispe + Gradle 环境中的配置截图。
然后就可以在 JME3 使用 Models/Monkey/monkey.j3o
加载模型, ClasspathLocator 会在幕后为我们做好一切工作。
一般来说,资产打包是由编译脚本自动化执行的。本文只是演示其原理,实际开发时请不要这样手动打包。
FileLocator
FileLocator 可以直接访问操作系统的文件目录,调用 AssetManager 的 registerLocator
方法来注册资产根目录即可。
例如:游戏资产保存在 F:\assets
目录中,目录结构如下:
F:\assets\Interface
F:\assets\MatDefs
F:\assets\Materials
F:\assets\Models
F:\assets\Models\Monkey
F:\assets\Models\Monkey\monkey.j3o
F:\assets\Scenes
F:\assets\Shaders
F:\assets\Sounds
F:\assets\Textures
F:\assets\Textures\Monkey
F:\assets\Textures\Monkey\DiffuseMap.png
在JME3中加载资源的代码如下:
@Override
public void simpleInitApp() {
// 配置文件目录
assetManager.registerLocator("F:\\assets", FileLocator.class);
// 加载j3o模型
Spatial model = assetManager.loadModel("Models/Monkey/monkey.j3o");
rootNode.attachChild(model);
// 添加光源
// ..
}
在实际开发中,不建议像这样使用绝对路径,而应该尽量使用相对路径来加载资产。
例如,可以在工程目录下创建一个 res
目录,用来存放游戏资产,然后通过 FileLocator 来管理它。
@Override
public void simpleInitApp() {
// 配置文件目录
assetManager.registerLocator("res", FileLocator.class);
// 加载j3o模型
Spatial model = assetManager.loadModel("Models/Monkey/monkey.j3o");
rootNode.attachChild(model);
// 添加光源
// ..
}
事实上,可以利用 FileLocator 来配置 assets
目录,这样在 Eclipse、IDEA等开发环境中就不需要把 assets
目录添加到 classpath 中了。
ZipLocator
ZipLocator 利用 Java 语言自带的 ZIP 算法实现了文件解压,JME3 可以通过 ZipLocator 直接从 zip 压缩文件中加载资产。
例如:把资产文件打包成 zip 文件,例如: assets.zip
文件结构如下:
把 assets.zip
放到 JME3 工程根目录下,然后在 AssetManager 中注册 assets.zip
文件所在的相对路径。
@Override
public void simpleInitApp() {
// 配置文件目录
assetManager.registerLocator("assets.zip", ZipLocator.class);
// 加载j3o模型
Spatial model = assetManager.loadModel("Models/Monkey/monkey.j3o");
rootNode.attachChild(model);
// 添加光源
// ..
}
当然,你也可以使用绝对路径,但我不建议你这么做。
@Override
public void simpleInitApp() {
// 配置文件目录
assetManager.registerLocator("D:\\WORKSPACE\\jME3Projects\\MyGame\\assets.zip", ZipLocator.class);
// 加载j3o模型
Spatial model = assetManager.loadModel("Models/Monkey/monkey.j3o");
rootNode.attachChild(model);
// 添加光源
// ..
}
HttpZipLocator
HttpZipLocator 与 ZipLocator 的用法几乎完全相同,只不过它是通过 HTTP 协议去访问网上的资源包。
假设我将 assets.zip 上传到网站上,提供静态资源路径 http://www.jmecn.net/examples/assets.zip
供用户下载,那么在 JME3 程序中就可以这样做:
@Override
public void simpleInitApp() {
// 配置文件目录
assetManager.registerLocator("http://www.jmecn.net/examples/assets.zip",
HttpZipLocator.class);
// 加载模型
Spatial model = assetManager.loadModel("Models/Monkey/monkey.j3o");
rootNode.attachChild(model);
// 添加光源
// ..
}
UrlLocator
UrlLocator 的用途与 FileLocator 相似,只不过它是通过 URL 来定位网络资产目录。
假设我将 F:\\assets
目录中的内容上传到网站上,提供静态路径 http://www.jmecn.net/examples/assets/
供用户访问,那么在 JME3 程序中就可以这样做:
@Override
public void simpleInitApp() {
// 配置文件目录
assetManager.registerLocator("http://www.jmecn.net/examples/assets/",
UrlLocator.class);
// 加载模型
Spatial model = assetManager.loadModel("Models/Monkey/monkey.j3o");
rootNode.attachChild(model);
// 添加光源
// ..
}
在这种方式下,用户应该可以通过超链接 http://www.jmecn.net/examples/assets/Models/Monkey/monkey.j3o
直接下载对应的模型文件。
多目录
AssetManager 允许开发者注册多个资产目录,它会按照注册的顺序来加载资产,这个特性很有用。通过这种方式非常容易实现用户自定义皮肤等功能,还可以对不同版本的资产包进行排序。
在项目中定义两个资产根目录,assets
目录存放游戏本身的资产,mod
目录存放用户文件。先注册 mod
再注册 assets
,AssetManager 就会先去加载用户文件;若文件不存在,才会去 assets
目录中加载文件。
@Override
public void simpleInitApp() {
// 配置文件目录
assetManager.registerLocator("mod", FileLocator.class);
assetManager.registerLocator("assets", FileLocator.class);
// 加载j3o模型
Spatial model = assetManager.loadModel("Models/Monkey/monkey.j3o");
rootNode.attachChild(model);
// 添加光源
// ..
}
需要注意的是,JME3 在初始化时就已经执行了 assetManager.registerLocator("/", ClasspathLocator.class)
。在上述规则下,它的顺位比所有 AssetLocator 都靠前。
如果你希望把其他资产目录排到 classpath 之前,先调用 unregisterLocator
方法取消注册 classpath,然后按自己期望的顺序添加 AssetLocator,再注册 classpath 即可。
代码如下:
// 取消注册 classpath
assetManager.unregisterLocator("/", ClasspathLocator.class);
// 注册mod资产目录
assetManager.registerLocator("mod", FileLocator.class);
// 重新注册 classpath
assetManager.registerLocator("/", ClasspathLocator.class);