- Why would I want to use the AssetManager
- Creating an AssetManager
- Adding Assets to the queue
- Actually loading the assets
- Optimize loading
- Loading a TTF using the AssetHandler
- Getting Assets
- Disposing Assets
- I only supply Strings, where does the AssetManager load the assets from?
- Writing your own Loaders
- Resuming with a Loading Screen
Why would I want to use the AssetManager
AssetManager (code) helps you load and manage your assets. It is the recommended way to load your assets, due to the following nice behaviors:
- -
Still with me? Then read on.
Creating an AssetManager
This part is rather simple:
This sets up a standard AssetManager, with all the loaders libGDX has in store at the moment. Let’s see how the loading mechanism works. Caution: don’t make yourAssetManager manager = new AssetManager();
AssetManageror any other resources (likeTexture, etc.)static, unless you properly manage them. E.g. the following code will cause issues:public static AssetManager assets = new AssetManager();
AssetManagerinstance of a previous instance of your application might be used for the next instance, while the resources are no longer valid. This typically would cause black/missing textures or incorrect assets. this StackOverflow question for details.)Adding Assets to the queue
To load assets, the AssetManager needs to know how to load a specific type of asset. This functionality is implemented via AssetLoaders. There are two variants, SynchronousAssetLoader and AsynchronousAssetLoader. The former loads everything on the rendering thread, the latter loads parts of the asset on another thread, e.g., the Pixmap needed for a Texture, and then loads the OpenGL dependent part on the rendering thread. The following resources can be loaded out of the box with the AssetManager as constructed above. - PixmapLoader (code)
- TextureLoader (code)
- BitmapFontLoader (code)
- FreeTypeFontLoader
- TextureAtlasLoader (code)
- MusicLoader (code)
- SoundLoader (code)
- SkinLoader (code)
- ParticleEffectLoader (code)
- I18NBundleLoader (code)
- (code)
Loading a specific asset is simple:
AssetManager.load() method. Some loaders allow you to also pass parameters to them via AssetManager.load(). Say we want to specify a non-default filter and mipmapping setting for loading a texture:manager.load("data/mytexture.png", Texture.class);manager.load("data/myfont.fnt", BitmapFont.class);manager.load("data/mymusic.ogg", Music.class);
Look into the loaders mentioned above to find out about their parameters.TextureParameter param = new TextureParameter();param.minFilter = TextureFilter.Linear;param.genMipMaps = true;manager.load("data/mytexture.png", Texture.class, param);
Actually loading the assets
So far we only queued assets to be loaded. The AssetManager does not yet load anything. To kick this off we have to call AssetManager.update() continuously, say in our ApplicationListener.render() method:
You have to call AssetManager.update() to keep loading! If you want to block and make sure all assets are loaded you can call: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 ... }}
This will block until all the assets that have been queued are actually done loading. 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).manager.finishLoading();
Optimize loading
E.g. AssetManager.update(17) - In this case the AssetManager blocks for at least 17 milliseconds (only less if all assets are loaded) and loads as many assets as possible, before it returns control back to the render method. Blocking for 16 or 17 milliseconds leads to ~60FPS as 1/601000 = 16.66667. Note that it might block for longer, depending on the asset that is being loaded so *don’t take the desired FPS as guaranteed.Loading a TTF using the AssetHandler
Loading a TrueType file via the AssetHandler requires only a little bit extra tweaking. Before we can load a TTF, we need to set the type of loader we’re going to use for FreeType fonts. This is done with the following:FileHandleResolver resolver = new InternalFileHandleResolver();manager.setLoader(FreeTypeFontGenerator.class, new FreeTypeFontGeneratorLoader(resolver));manager.setLoader(BitmapFont.class, ".ttf", new FreetypeFontLoader(resolver));
FreeTypeFontLoaderParameterthat defines 1) our actual font file, and 2) our font size. There are other parameters we can define here, too, when you have time to dig more. Let’s say we want to create two different fonts: a smaller, sans-serif font that will be used for one type of writing text, and a larger, serif font for titles and other fun things. I’ve decided to use Arial and Georgia for these two fonts, respectively. Here’s how I can load them using the AssetManager:// First, let's define the params and then load our smaller fontFreeTypeFontLoaderParameter 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 fontFreeTypeFontLoaderParameter myBigFont = new FreeTypeFontLoaderParameter();myBigFont.fontFileName = "georgia.ttf";myBigFont.fontParameters.size = 20;manager.load("georgia.ttf", BitmapFont.class, myBigFont);
mySmallFontandmyBigFont, that we can use to display different text..loaded, we still need to set them. We can do this like so:
The name you give the manager doesn’t have to match the name of the font, like in the above example. If you want to use the same font for different sizes, just make sure the name you give the asset manager when loading the font is unique. For example, here’s how you could load the Arial font in both 10pt and 20pt:BitmapFont mySmallFont = manager.get("arial.ttf", BitmapFont.class);BitmapFont myBigFont = manager.get("georgia.ttf", BitmapFont.class);
FreeTypeFontLoaderParameter arial10 = new FreeTypeFontLoaderParameter();// This is the file that needs to exist in the assets directory.arial10.fontFileName = "arial.ttf";arial10.fontParameters.size = 10;// There is no file named arial10.ttf. This is just an identifier for the asset manager.// The .ttf extension is important, because it tells the asset manager which loader to use.manager.load("arial10.ttf", BitmapFont.class, arial10);// Now just change the font size, but use the same font.FreeTypeFontLoaderParameter arial20 = new FreeTypeFontLoaderParameter();arial20.fontFileName = "arial.ttf";arial20.fontParameters.size = 20;// And create a new BitmapFont in 20pt.manager.load("arial20.ttf", BitmapFont.class, arial20);
Getting Assets
That’s again easy:
This of course assumes that those assets have been successfully loaded. If we want to poll whether a specific asset has been loaded we can do the following: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);}
Disposing Assets
Easy again, and here you can see the real power of the AssetManager:
If that font references a Texture that you loaded manually before, the texture won’t get destroyed! It will be reference counted, getting one reference from the bitmap font and another from itself. As long as this count is not zero, the texture won’t be disposed. - If you want to get rid of all assets at once you can call:manager.unload("data/myfont.fnt");
ormanager.clear();
Both will dispose all currently loaded assets and remove any queued and not yet loaded assets. The AssetManager.dispose() method will also kill the AssetManager itself. After a call to this method you should not use the manager anymore. And that’s pretty much everything there is. Now for the nitty-gritty parts.manager.dispose();
I only supply Strings, where does the AssetManager load the assets from?
Every loader has a reference to a FileHandleResolver. That’s a simple interface looking like this:
By default, every loader uses an InternalFileHandleResolver. That will return a FileHandle pointing at an internal file (just like Gdx.files.internal(“data/mytexture.png”). You can write your own resolvers! Look into the assets/loaders/resolvers package for more FileHandleResolver implementation. One use case for this would be a caching system, where you check if you have a newer version downloaded to the external storage first, and fall back to the internal storage if it’s not available. The possibilities are endless. You can set the FileHandleResolver to be used via the second constructor of AssetManager:public interface FileHandleResolver { public FileHandle resolve(String file);}
This will make sure all default loaders listed above will use that loader.AssetManager manager = new AssetManager(new ExternalFileHandleResolver());
Writing your own Loaders
I can’t anticipate which other types of resources you want to load, so at some point you might want to write your own loaders. There are two interfaces called SynchronousAssetLoader and AsynchronousAssetLoader you can implement. Use the former if your asset type is fast to load, use the latter if you want your loading screen to be responsive. I suggest basing your loader on the code of one of the loaders listed above. Look into MusicLoader for a simple SynchronousAssetLoader, look into PixmapLoader for a simple AsynchronousAssetLoader. BitmapFontLoader is a good example of an asynchronous loader that also has dependencies that need to be loaded before the actual asset can be loaded (in that case it’s the texture storing the glyphs). Again, you can do pretty much anything with this.loadAsyncfunction can be used to load parts of the assets where the loading can be delegated to another thread (this is a requirement for responsive loading screen). TheloadSyncfunction must be used if some parts of the asset needs to be loaded on the main rendering thread. For example, OpenGL API function calls must be invoked on the main rendering thread, therefore any parts of the asset and its loading involving calls to OpenGL must be called inloadSync.loadAsyncwill be called first andloadSyncafterwards. You can pass information fromloadASynctoloadSyncwith the aid of your custom asset loader class’ attributes. Care must be taken to initialize these temporary variables to null between the loading of each separate asset or you might end up loading the same asset multiple times! Easiest way to achieve this is by setting your temporary variables to null at the beginning ofloadASyncimplementation.PixmapLoaderclass demonstrates a simple way of using the temporary variables correctly, whereasTextureLoadershows a more complex way to pass information betweenloadAsyncandloadSync. Once you are done writing your loader, tell the AssetManager about it:manager.setLoader(MyAssetClass.class, new MyAssetLoader(new InternalFileHandleResolver()));manager.load("data/myasset.mas", MyAssetClass.class);
Resuming with a Loading Screen
On Android, your app can be paused and resumed. Managed OpenGL resources like Textures need to be reloaded in that case, which can take a bit of time. If you want to display a loading screen on resume, you can do the following after you created your AssetManager.
In your ApplicationListener.resume() method you can then switch to your loading screen and call AssetManager.update() again until everything is back to normal. If you don’t set the AssetManager as shown in the last snippet, the usual managed texture mechanism will kick in, so you don’t have to worry about anything. And this concludes the long awaited article on the AssetManager.Texture.setAssetManager(manager);
