Libgdx AssetManager
1. 资源管理器(AssetManager)概述
资源管理器(AssetManager)主要负责统一加载和管理 纹理(Texture)、纹理图集(TextureAtlas)、位图字体(BitmapFont)、音乐(Music)、音效(Sound) 等 assets 文件夹中的资源。
2. 为什么要使用 AssetManager
如果你的游戏非常简单,加载资源不需要很多时间,那么你可以选择不使用 AssetManager,在其他任何情况下都建议使用它。使用 AssetManager 的好处如下所示:
所有资源的加载采用异步加载方式,不阻塞渲染线程,因此在加载资源时可以显示一个正在加载数据的场景(在场景中可使用进度条实时显示加载进度)。
AssetManager 对资源使用引用计数。如果 资源A 和 资源B 同时依赖于 资源C,那么 资源C 只有在 资源A 和 资源B 都被释放时才释放。这也就意味着如果同一个资源被加载多次,实际上在内存中只会存在一份实例被共享使用。
在同一个地方存储所有的资源。
可以透明地实现像缓存一样的事物。
3. 创建一个 AssetManager
创建 AssetManager 实例非常简单,直接 new 创建即可:
AssetManager manager = new AssetManager();
这里创建了一个标准的资源管理器,并同时存储所有类型的资源加载器。
注意: AssetManager 和 其他所有的资源(例如 Texture 等)的实例 不能被静态变量引用 ,除非你能够很好的管理它们。比如下面的代码将可能会引起问题:
public static AssetManager assets = new AssetManager();
这在 Android 平台将会导致问题,因为静态变量的生命周期和 LibGDX 应用的生命周期不能保证一定相同。因此你的一个 LibGDX 应用实例提供的 AssetManager 实例有可能会被下一个 LibGDX 应用实例使用,而加载过的资源却不再有效。这通常会导致黑块/缺失的纹理或错误的资源。
在 Android 平台中,甚至会导致同一时间在你的 Activity 中有多个 LibGDX 应用实例被激活,所以即使你妥善处理了生命周期方法也不要认为你是安全的!
4. 加载 assets 资源
加载资源时,资源管理器需要知道如何去加载一个明确类型的资源,这个功能通过 AssetLoader(资源加载器)来实现。AssetLoader 是一个抽象类,它有两个实现,分别是 SynchronousAssetLoader(同步资源加载器) 和 AsynchronousAssetLoader(异步资源加载器),前者在渲染线程直接加载,后者在另外的一个线程中加载。
具体资源类型的加载器定义在 com.badlogic.gdx.assets.loaders 包中,下面列出 AssetManager 默认支持加载的部分资源:
Texture
BitmapFont
TextureAtlas
Music
Sound
Pixmap
ParticleEffect
Skin
I18NBundle
…
加载一个指定类型的资源非常简单:
manager.load(“data/step.png”, Texture.class);
manager.load(“data/font.fnt”, BitmapFont.class);
manager.load(“data/music.ogg”, Music.class);
调用 load 方法会将资源的加载加入到一个待加载的队列中,然后按照 load 的顺序依次加载。
把所有需要加载的资源都添加到队列后,可以调用下面的方法实时获取加载进度:
// 返回加载进度的百分比
float percent = manager.getProgress();
5. 获取已加载的资源
根据文件名称和指定的类型获取加载后的资源实例:
Texture tex = manager.get(“data/mytexture.png”, Texture.class);
BitmapFont font = manager.get(“data/myfont.fnt”, BitmapFont.class);
资源实例必须要加载完毕之后才能获取,可以使用下面方法判读是否加载完毕:
boolean texIsLoaded = manager.isLoaded(“data/mytexture.png”);
boolean fontIsLoaded = manager.isLoaded(“data/myfont.fnt”);
也可以在资源全部 load 到队列后,调用下面的等待加载完毕的方法,方法执行完再获取资源:
// 调用这个方法将阻塞线程, 直到待加载队列中的所有资源都加载完毕
manager.finishLoading();
6. 释放资源
通过 AssetManager 加载的资源,不再需要使用到某个资源,要对资源进行释放时,通常不调用资源对象本身的 dispose() 方法进行释放,而是统一由 AssetManager 中的方法进行释放。
释放单个资源:
manager.unload(“data/myfont.fnt”);
释放所有资源(包括已加载完毕的和已在待加载队列中的):
manager.clear();
释放所有资源(包括已加载完毕的和已在待加载队列中的),并销毁资源管理器(不再需要用到资源管理器时也必须手动销毁):
manager.dispose();
7. 恢复加载场景
在 Android 平台,应用可以被暂停(paused)和恢复(resumed),在这种情况下,管理的像纹理这样的 OpenGL 资源会重新加载,恢复时可能需要一些时间。如果你想在恢复时显示一个正在加载的场景,当创建好一个 AssetManager 后可以做下面这样的设置:
Texture.setAssetManager(manager);
然后在 ApplicationListener.resume() 生命周期方法中切换到正在加载的场景,并且再一次调用 AssetManager.update() 方法直到一切恢复正常。
如果你没有做 Texture.setAssetManager(manager) 的操作,通常纹理的管理机制会发挥作用自动处理,所以你不需要担心任何事情。
8. 代码示例: AssetManager 在代码中的使用
为了方便演示不再引入资源,这里只演示前面章节中常用的两张图片(badlogic.jpg 和 logo.png,直接放到 assets 文件夹下)的加载。
package com.libgdx.test;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
/**
-
游戏主程序的启动入口类
*/
public class MainGame extends ApplicationAdapter {// 资源在 assets 文件夹中的相对路径
private static final String BADLOGIC = “badlogic.jpg”;
private static final String LOGO = “logo.png”;private SpriteBatch batch;
private AssetManager manager;
private Texture badlogicTexture;
private Texture logoTexture;@Override
public void create() {
batch = new SpriteBatch();// 创建资源管理器 manager = new AssetManager(); // 加载资源 manager.load(BADLOGIC, Texture.class); manager.load(LOGO, Texture.class); // 阻塞线程, 等待资源加载完毕 manager.finishLoading(); // 加载完毕后获取资源实例 badlogicTexture = manager.get(BADLOGIC, Texture.class); logoTexture = manager.get(LOGO, Texture.class);
}
@Override
public void render() {
// 白色清屏
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);batch.begin(); batch.draw(badlogicTexture, 0, 0); batch.draw(logoTexture, 10, 300); batch.end();
}
@Override
public void dispose() {
// 当应用退出时释放资源
if (manager != null) {
/*
* 释放所有资源, 并销毁 AssetManager。
*
* 注意: 本例中的 Texture 通过 AssetManager 进行管理,
* 因此这里不需要再调用 Texture 的 dispose() 方法。
*/
manager.dispose();
}
}
}