Creating custom asset loaders

Other than the already mentioned default loaders for the built-in asset types in Libgdx, you can implement your own loaders for other asset types you need to manage. When beginning work on this recipe, it is a good exercise to think about what types of custom assets you might need to define.

Some games require their actors to interact with each other and the environment. They are perfect candidates to make use of physics properties and bodies.

2D/3D animations are also a good example. It is necessary to store frames, duration, and so on.

The possibilities are endless. The preceding information about physics and animation can be stored in files in such a way as to load it at runtime.

Creating custom assets might seem complex, but contrary to first impressions, it will make your code more readable as well as save you a lot of time.

In this recipe, we will focus on a 2D animation sprite loader because it will only make use of concepts that were mentioned in previous chapters.

Getting ready

Remember to have the sample projects imported into your Eclipse workspace by right-clicking on the Package Explorer panel, selecting Import, and then clicking on Gradle Project again. Go back to Chapter 1, Diving into Libgdx, if you need some assistance.

How to do it…

The code for this recipe is in the CustomLoaderSample.java file within the [cookbook]/samples folder. It will lean on the com.cookbook.animation package.

The initial decision to make is whether your type will be loaded synchronously or asynchronously. As mentioned in the preceding recipes, using the synchronous loader means blocking the application (rendering thread) until the resource is loaded. On the other hand, an asynchronous loader allows your game to be responsive because the load will be performed on a secondary thread. Base your decision on the loading speed of your asset.

To carry out the implementation of custom loaders, Libgdx provides us with two abstract classes to extend, AsynchronousAssetLoader<T, P> and SynchronousAssetLoader<T, P>, where T represents the type to load, and P an extended type of the AssetLoaderParameters interface. As mentioned in the first recipe of this chapter, AssetLoaderParameters class allows you to load the data type with some initialization parameters.

A custom SynchronousAssetLoader class will have to override the load(...) method, whereas AsynchronousAssetLoader will do likewise with loadAsync(...) and loadSync(...).

It is important to understand that the example shown in this recipe is just to illustrate, with a practical case, the process to follow when creating your custom asset loader. Focusing on the idea is the best perspective to adopt in the next sections.

Writing our own asset type

It is highly recommended to package your asset content into a specific asset class. It will make your life easier while writing your loader and using an asset in your application. The SpriteAnimationData class will be the container storing useful animation information:

public class SpriteAnimationData {
...
Texture texture = null;
int rows = 0;
int columns = 0;
float frameDuration = 0.0f;
ObjectMap<String, Animation> animations = new ObjectMap<String, Animation>();
Animation defaultAnimation = null; 

Later, you will understand how we will fill these fields in.

Writing our own asset loader

As you can imagine, our 2D animation sprite loader will extend AsynchronousAssetLoader. So, it will begin with:

public class SpriteAnimationLoader extends AsynchronousAssetLoader<SpriteAnimationData, SpriteAnimationLoader.AnimationParameter> {
...
}

We must define a static public parameter class within SpriteAnimationLoader to fit the previous template, even if we don't really need it, and it remains empty:

Static public class AnimationParameter extends AssetLoaderParameters<SpriteAnimationData> { }

Do not forget to declare and initialize the variable where the content data is being stored to null:

private SpriteAnimationData animationData = null;

Since we define an asset loader, it needs FileHandleResolver when being constructed. In this way, the resolve() method can be used, which makes it clear where to look for the files:

public SpriteAnimationLoader(FileHandleResolver resolver) {
   super(resolver);
}

Due to the strictly asynchronous nature of our loader, the synchronous method will just retrieve the animation data as is:

@Override
public SpriteAnimationData loadSync(AssetManager manager, String filename, FileHandle, AnimationParameter parameter) {
   return animationData;
}

Fitting the suggested AsynchronousAssetLoader interface is something that you must accomplish, but now is when the real deal goes into action. The loadAsync(...) method has to parse a JSON file (it can be any other type, such as XML, but it is a good idea to get familiar with it after its explanation in Chapter 5, Audio and File I/O). A simple example JSON file for our animation format can be:

{
   "rows": "2",
   "columns": "5",
   "frameDuration": "0.025f",
   "animations" : [
     {
      "name": "walk",
      "frames": "0,1,2,3,4,5,6"
     }
   ]
}

As you can see, it contains almost all the information that SpriteAnimationData demands. Once this information is extracted, we have to organize it along with the animation sheet within the class. At the end of the loadAsync(...) method, all the required data will be stored in the animationData variable.

The animation-related code will add too much complexity and will divert the real goal of this recipe. If you are interested, take a look at the source code that comes together with this book; more specifically, check out the SpriteAnimationLoader.java file.

Summarizing...

In order to ensure that you know the real process to follow when creating a custom asset loader, use these steps:

  1. Create your custom data type.
  2. Decide whether your loader will work synchronously, asynchronously, or both.
  3. Extend your loader class with the proper abstract class depending on its type: AsynchronousAssetLoader<T, P>, or SynchronousAssetLoader<T, P>.
  4. Override the load() method for synchronous loaders and the loadAsync() and loadSync() methods for asynchronous loaders. Be aware of the dependencies.

How it works…

Both AsynchronousAssetLoader and SynchronousAssetLoader extend the AssetLoader abstract class, which is really the one in charge of calling the resolve(...) method from its set FileHandleResolver, already discussed in the first recipe of this chapter.

Here is a table with a classification of built-in type loaders:

Asynchronous

Synchronous

TextureLoader

MusicLoader

BitmapFontLoader

ParticleEffectsLoader

ModelLoader

TextureAtlasLoader

SkinLoader

ModelLoader

SoundLoader

 

PixmapLoader

 

I18NBundleLoader

 

TextureLoader is an OpenGL-descendant part that is performed on the rendering thread. BitmapFontLoader, SkinLoader, TextureAtlasLoader, and ModelLoader are AssetManagers that will load its dependencies before the actual asset.

As seen in Chapter 4, Detecting User Input, note that music resources are streamed, so it won't have to load the whole thing.

There's more…

Custom asset loading is not fully covered in this chapter because there are some particular cases where dependencies exist between resources. To deal with this scenario, the assets loader abstract classes provide us with the getDependencies(...) method, which when applied to the SpriteAnimationLoader context, result in the following:

@Override
public Array<AssetDescriptor> getDependencies( String filename, fileHandle file, AnimationParameter parameter) {
   Array<AssetDescriptor> dependencies = new Array<AssetDescriptor>();
   dependencies.add(new AssetDescriptor<Texture>(stripExtension(filename) + ".png", Texture.class));

   return dependencies;
   ...
}

The first thing to pay attention to here is the dependency on the animation sheet image file. However, if you look beyond it, there is a seemingly new concept to describe, and this is AssetDescriptor. As its name suggests, it describes an asset with its filename, type, and parameters, which will be used in the asset's subsequent load. In fact, it is not new at all as it is used in the already mentioned AssetLoadingTask class. Moreover, this class contains two methods, handleSynchLoader and handleAsynchLoader, which are in charge of injecting the dependencies.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.24.180