libGDX 游戏开发之 - 资源管理

libGDX 2D游戏开发之 - 资源管理

原文地址:https://github.com/libgdx/libgdx/wiki/Managing-your-assets.

译者:重庆好爸爸 game4kids@163.com
谢绝转载

译者总结 (需要完善)

相关的类

  • AssetManager
  • AssetLoader

术语对照

  • Dispose - 处置,释放
  • Asset - 资源
  • Loader - 加载器
  • TTF - TrueType File 美国苹果公司和微软公司共同开发的一种电脑轮廓字体(曲線描邊字)类型标准
  • nitty-gritty parts - 美国俚语,一些必不可少的细节部分

管理你的资源

为什么要使用AssetManager

AssetManager帮助你加载和管理你的资源。这是一个推荐的加载资源的方法,因为这个方法有如下诸多优点:

  • 异步完成大多数资源的加载。所以在资源加载完成前,你可以通过渲染进程去显示一个loading界面。==(译者注:“异步”是指资源的加载可以放在非渲染线程的另外一个线程中。)==
  • 资源是索引计数。如果资源A和资源B都依赖于另外一个资源C,那么在资源A和资源B都被释放之前,资源C是不会释放的。这也说明如果多次加载了同一个资源,实际上这个资源在内存中是只有1个实例的。
  • 你所有的资源可以放在同一个地方。
  • 允许透明(transparently)的实现一些功能,比如Caches(见下面的FileHandleResolver)
    继续把。

创建一个AssetManager

创建AssetManager很简单

AssetManager manager = new AssetManager();

上面的代码会创建一个标准的AssetManager。代码执行后,libgdx拥有的所有loaders(加载器)都也都创建了。让我们一起看看这个加载机制是如何工作的。
注意: 不要把AssetManager和其他的资源(比如Texture等)设置为静态变量,除非你有信心正确的管理他们。例如,下面的代码将会导致问题:

public static AssetManager assets = new AssetManager();

这条语句在Andriod上可能会引发问题。Andriod中的静态变量的生命周期和你的APP生命周期可能是不一样的。(译者注:关于静态变量这一段不是很理解,你们自行找资料看吧:- 因此,你的前一个APP实例的AssetManager实例可能被下一个APP实例拿去应用,然后那些资源其实都已经不可用了。这样会导致黑屏/丢失纹理或者错误资源的典型问题。

在Android中,你的Activity的多个实例甚至可能被同时被激活,所以即使你能正确的处理生命周期,你也不一定100%安全。(见Stackflow描述的场景

加载资源

AssetManager需要知道如何加载特定类型的资源。这个功能功过 AssetLoader 来实现。AssetLoader有2个变种:SynchronousAssetLoader(同步资源加载器) 和 AsynchronousAssetLoader(异步资源加载器)。SynchronousAssetLoader在渲染线程中加载所有的资源;AsynchronousAssetLoader在除渲染线程外的另外线程中加载部分资源,比如: Pixmap需要一个Texture,然后在渲染线程中加载OpenGL的依赖部分,但后面需要的资源就可以在其他线程中加载了。(译者注:原文为 The following resources can be loaded out of the box with the AssetManager as constructed above.)
下面列举了各种资源的加载器: (译者注:下面这些加载器都是从同步/异步加载器继承下来的)

  • Pixmaps: PixmapLoader
  • Textures : TextureLoader
  • BitmapFonts : BitmapFontLoader
  • FreeTypeFonts : FreeTypeFontLoader
  • TextureAtlases : TextureAtlasLoader
  • Music instances : MusicLoader
  • Sound instances : SoundLoader
  • Skins : SkinLoader
  • Particle Effects : ParticleEffectLoader
  • I18NBundles : I18NBundleLoader
  • FreeTypeFontGenerator : FreeTypeFontGeneratorLoader

加载特定的的资源也很简单,见如下代码

manager.load("data/mytexture.png", Texture.class);
manager.load("data/myfont.fnt", BitmapFont.class);
manager.load("data/mymusic.ogg", Music.class);

上面的.load()把将要加载的资源放入队列。资源将按照.load()方法的调用顺序被依次加载。有的加载器允许你在调用.load()方法的时候加入参数。比如说,我们想在加载texture的时候指定一个非默认的filter设置和mipmapping设置:

TextureParameter param = new TextureParameter();
param.minFilter = TextureFilter.Linear;
param.genMipMaps = true;
manager.load("data/mytexture.png", Texture.class, param);

你可以抽空研究一下上面提到各种loader可用的参数。

使用AssetHandler加载TTF(TrueType File)

通过AssetHandler加载TTF需要一点额外的调整, 因为在加载TTF之前,我们需要设置加载FFT需要的加载器类型:

FileHandleResolver resolver = new InternalFileHandleResolver();
manager.setLoader(FreeTypeFontGenerator.class, new FreeTypeFontGeneratorLoader(resolver));
manager.setLoader(BitmapFont.class, ".ttf", new FreetypeFontLoader(resolver));

接下来,我们想要创建一个FreeTypeFontLoaderParameter, 这个FreeTypeFontLoaderParameter定义了 1)我们实际的字体文件 2)我们的字体字号(size)。如果你有时间,你还可以研究下其他的参数,可以放进来。

比如说我们想创建2种不同的字体: 小一点的sans-serif字体,用作写作的字体;大一点的serif字体,用作标题和其他有趣的东西。我决定分别使用Arial和Georgia这两种字体。 以下是我可以使用AssetManager加载它们的方法:

// First, let's define the params and then load our smaller font
FreeTypeFontLoaderParameter mySmallFont = new FreeTypeFontLoaderParameter();
mySmallFont.fontFileName = "arial.ttf";
mySmallFont.fontParameters.size = 10;
manager.load("arial.ttf", BitmapFont.class, mySmallFont);

// Next, let's define the params and then load our bigger font
FreeTypeFontLoaderParameter myBigFont = new FreeTypeFontLoaderParameter();
myBigFont.fontFileName = "georgia.ttf";
myBigFont.fontParameters.size = 20;
manager.load("georgia.ttf", BitmapFont.class, myBigFont);

整齐! 现在我们有两种不同的字体,mySmallFont和myBigFont,可以用来显示不同的文本。
还没有完,现在字体已经".load()",但是我们还需要设置他们:

BitmapFont mySmallFont = manager.get("arial.ttf", BitmapFont.class);
BitmapFont myBigFont = manager.get("georgia.ttf", BitmapFont.class);

截至目前,我们只是把资源放入队列。AssetManager实际上还没有加载任何资源。为了触发加载动作,我们需要连续调用AssetManager.update()方法,比如把它放在我们的ApplicationListener.render() 方法中:

public MyAppListener implements ApplicationListener {

   public void render() {
      if(manager.update()) {
         // we are done loading, let's move to another screen!
      }

      // display loading information
      float progress = manager.getProgress()
      ... left to the reader ...
   }
}

只要AssetManager.update()方法还在返回false,你就知道资源加载未完成。要轮询具体的加载状态,您可以使用AssetManager.getProgress()方法,它返回一个介于0和1之间的数字,表示到目前为止加载的资源的百分比。AssetManager中还有其他方法为您提供类似的信息,如AssetManager.getLoadedAssets()或AssetManager.getQueuedAssets()。 你必须调用AssetManager.update()来保存加载状态。

如果要block并确保所有资产都已加载,您可以调用:

manager.finishLoading();

这个会block,直到所有队列中的资源完成实际的加载。Kinda defeats the purpose of asynchronous loading, but sometimes one might need it (e.g., loading the assets needed to display the loading screen itself).

获取资源

获取资源也很简单

Texture tex = manager.get("data/mytexture.png", Texture.class);
BitmapFont font = manager.get("data/myfont.fnt", BitmapFont.class);

当让我们假定这些资源已经被成功加载了。如果我们想轮询一个特定的资源是否已经成功加载了,我们用下面的方法:

if(manager.isLoaded("data/mytexture.png")) {
   // texture is available, let's fetch it and do something interesting
   Texture tex = manager.get("data/mytexture.png", Texture.class);
}

处置(释放)资源

也很简单,这里你可以看到AssetManager的威力:

manager.unload("data/myfont.fnt");

如果这个myfont.fnt是索引到你之前手工加载的一个Texture,那么这个texture不会被销毁!在这种情况下,它是索引计数将是2次:1次是来自bitmap字体的计数,另外1次是来自它自身。只要这个计数不为0,那么这个texture就不会被销毁。

不同通过手工销毁AssetManager管理的资源,而是要用AssetManager.upload()去销毁。如果你想一次性销毁所有的资源,那么调用manager.clear();或者manager.dispose(); 这两种方法都会销毁当前所有已经加载的资源,以及销毁队列中还未来得及完成加载的资源。AssetManager.dispose()方法会销毁AssetManager本身。所有在调用AssetManager.dispose()后,就要不能再使用这个manager了。

目前基本所有的东西都说完了,但是让我们来看看另外一些必不可少的细节部分。

问题: 我没有提供String,但是AssetManager从来加载的String资源呢?

每一个加载器都有一个指向FileHandleResolver的索引。这是一个简单的接口,
Every loader has a reference to a FileHandleResolver. That's a simple interface looking like this:

public interface FileHandleResolver {
   public FileHandle resolve(String file);
}

默认情况下,每个加载器会使用一个InternalFileHandleResolver。这会返回一个FileHandle指向一个内部文件(就像Gdx.files.internal("data/mytexture.png"))。你可以写你自己的resolver!研究一下assets/loaders/resolvers包去看看更多的FileHandleResolver的具体实现。一个例子是Caching系统,你可以检查是否有在外部存储中有一个新版本已经下载,如果它不可用则回退到内部存储。可能性是无限多的。

你可以通过AssetManager的第二个构造函数的形式来设置将要使用的FileHandleResolver。

AssetManager manager = new AssetManager(new ExternalFileHandleResolver());

这将确保上面列出的所有默认加载程序将使用该加载程序。

编写你自己的加载器

我无法预期您要加载哪些其他类型的资源,所以在某些时候您可能想要编写自己的加载程序。有两个接口叫做SynchronousAssetLoader和AsynchronousAssetLoader可以实现。如果您的资源可以快速加载,请使用SynchronousAssetLoader;如果您希望展现加载屏幕,则使用AsynchronousAssetLoader。我建议您的加载器在要基于上面列出的其中一个装载机的代码上。MusicLoader是一个简单的SynchronousAssetLoader;PixmapLoader是一个AsynchronousAssetLoader。BitmapFontLoader是AsynchronousAssetLoader的一个很好的例子,它在资源可以加载之前需要加载一些依赖项(在这种情况下,指存储字形Textrue)。 你可以做很多事情。
当你写完自己的加载器后,需要告诉AssetManager:

manager.setLoader(MyAssetClass.class, new MyAssetLoader(new InternalFileHandleResolver()));
manager.load("data/myasset.mas", MyAssetClass.class);

从Loading屏幕返回

在Andriod里,你的APP可以paused和resumed. 在这种情况下,需要重新加载像Texture这样的托管OpenGL资源,这可能需要花费一些时间。如果要在resume时显示加载屏幕,则可以在创建AssetManager后执行以下操作。

Texture.setAssetManager(manager);

在ApplicationListener.resume()方法中,您可以切换到加载屏幕,并再次调用AssetManager.update(),直到所有内容恢复正常。

如果没有像最后一个代码片段中所示的那样设置AssetManager,那么通常的托管纹理机制就会出现,所以你不用担心任何事情。

这就是大家期待已久的AssetManager文章。 good luck!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容