美文网首页
libGDX 游戏开发之 - 资源管理

libGDX 游戏开发之 - 资源管理

作者: imagefancy | 来源:发表于2017-10-16 04:20 被阅读0次

    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!

    相关文章

      网友评论

          本文标题:libGDX 游戏开发之 - 资源管理

          本文链接:https://www.haomeiwen.com/subject/mugsyxtx.html