Menu Close

Creating an assets manager for libGDX using Kotlin | #5

Loading and managing the assets for your game, especially for a cross-platform mobile game is very important and should be done correctly. LibGDX has an AssetManager that they recommend to use. It is a single place to store all your assets and it prevents assets from being loaded multiple times into the memory. The loading is done asynchronously, so you could display a loading screen or a load progress bar.

Writing an assets manager in Kotlin

You can find the full code of the assets manager here: AssetsManager.kt. Because we want only one instance, we implement it as a Singleton:

object AssetsManager : Disposable {
    var manager: AssetManager = AssetManager()
        private set

    ...
}

That is all we need to write a Singleton class in Kotlin! We manage one instance of the AssetManager inside the Singleton, which will be disposed when the game is quit. Because we want to display a splash screen to the user while loading all assets, we need to load the splash-related assets first:

fun loadSplashAssets() {
    manager.load(Resources.SPLASH_IMAGE_PATH, Texture::class.java)
    manager.finishLoading()
}

Note that the constant variable SPLASH_IMAGE_PATH equals to the string and file name “splash.png”. The method finishLoading() is called to block until all assets are loaded. It does not block the user interface. After that, we load the other assets:

fun loadAssets() {
    manager.load(Resources.BACKGROUND_MUSIC, Music::class.java)
    manager.load(Resources.SOUND_BLOP, Sound::class.java)
    manager.load(Resources.SOUND_BUZZ, Sound::class.java)
    manager.load(Resources.SPRITES_ATLAS_PATH, TextureAtlas::class.java)
    manager.load(Resources.FONT_PATH, BitmapFont::class.java)
    manager.load(Resources.GAME_OVER_FONT_PATH, BitmapFont::class.java)
    manager.load(Resources.GAME_OVER_SCORE_FONT_PATH, BitmapFont::class.java)
    manager.finishLoading()
    loadAtlas()
}

In the loadAtlas() method, we go through all assets in the manager and store their references into a hash map, so we can easily access them later. Same for the animations. Remember that the sprites atlas is a big (or multiple) image map of all our sprites / assets, called TextureAtlas. This atlas is separated into regions, called TextureRegions. So our texture map is a hash map with strings as the key and TextureRegion as the value. Same for the animation map, except that the value is an Animation. We fill these hash maps using the following code:

private fun loadAtlas() {
    textureAtlas = manager.get(Resources.SPRITES_ATLAS_PATH, TextureAtlas::class.java)
    for (e in Resources.RegionNames.values()) {
        textureMap.put(e.name, textureAtlas.findRegion(e.str))
    }
    for (e in Resources.AnimationNames.values()) {
        animationMap.put(e.name, createAnimation(e.array))
    }
    makeFonts()
}

The fonts are made and customized by the following method. The font size of the BitmapFont can not be changed in game, you have to scale the image they rely on. That might lead to messy looking fonts, so either use a very big font (memory intensive) or consider implementing the gdx freetype extension. The advantage is that you can generate a BitmapFont of your desired size on the fly, shipping just the lightweight .ttf files with your game. As a disadvantage, this extension is not available in GWT for HTML5 builds. The linear texture filter is important to improve the quality of the font.

fun makeFonts() {
    smallFont = manager[Resources.FONT_PATH]
    smallFont.region.texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear)
    smallFont.data.scale(0.08f)
...
}

Creating the animations out of TextureRegions is done via the following code. A string array is passed as a parameter, which contains the texture region names.

private fun createAnimation(names: Array<String>): Animation {
    val frames = com.badlogic.gdx.utils.Array<TextureRegion>()
    frames.setSize(names.size)
    for (i in names.indices) {
        val path = names[i]
        frames[i] = textureAtlas.findRegion(path)
    }
    return Animation(GameSettings.ANIMATION_FRAME_DURATION, frames)
}

Creating an array using Kotlin lambda expressions

Each information to create an animation is stored in an array inside the Resources.AnimationNames enum class. The number at the end is an indicator to the specific frame, for example “jellybig_red_anim_0” is the first frame. With the use of lambda expressions, you can create the arrays on a smart way:

enum class AnimationNames(val array: Array<String>) {
    CIRCLE_RED_START_ANIMATIONS(Array(4, { i -> "jellybig_red_anim_" + i })),
    CIRCLE_BROWN_START_ANIMATIONS(Array(4, { i -> "mmstroke_brown_anim_" + i })),
    CIRCLE_PINK_START_ANIMATIONS(Array(4, { i -> "swirl_pink_anim_" + i })),
    CIRCLE_RED_START_ANIMATIONS_REVERSE(Array(4, { i -> "jellybig_red_anim_" + +(4 - i - 1) })),
    CIRCLE_BROWN_START_ANIMATIONS_REVERSE(Array(4, { i -> "mmstroke_brown_anim_" + +(4 - i - 1) })),
    CIRCLE_PINK_START_ANIMATIONS_REVERSE(Array(4, { i -> "swirl_pink_anim_" + +(4 - i - 1) })),
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *

fourteen − ten =

This site uses Akismet to reduce spam. Learn how your comment data is processed.