0
\$\begingroup\$

I'm building a game for Android using LibGDX and I'm attempting to add in a skin feature so players can change the look of the game on-the-fly. I've packed the skinnable assets into their own .pack files, separate from those of the rest of the assets, and I want to swap out the packs being used when the player selects a new skin.

In my Game Class I've got a function like this one:

public void loadSkinnedAssets(String skin){
    if(skinnableAtlas != null){
        //Tried both with and without disposing.
        skinnableAtlas.dispose();
    }
    skinnableAtlas = new TextureAtlas(Gdx.files.internal("skins/" + skin + "/Assets.pack"), true);

    if(gameScreen != null){
        gameScreen.refreshSkinnedSprites();
    }
}

Then in the GameScreen it does something like this:

public void refreshSkinnedSprites(){
    mySprite = new Sprite(game.getSkinAtlas().findRegion("myRegion");
    //Rest of the sprite setup not important.
    //Also attempted to just use mySprite.setRegion().
}

When the player chooses a skin from a list, it calls game.loadSkinAssets("skinName"). This is only possible from an options screen separate from the GameScreen.

These two functions work properly when called on launch, but the result of running them to replace existing assets is all of the replaced assets being drawn as solid black rectangles. If the game is left and then returned to (dispose and resume being called organically) everything loads properly, which again calls the same functions.

Is there something I'm missing in the posted code, or is there some entirely different way of accomplishing this?

\$\endgroup\$
0
\$\begingroup\$

After researching LibGDX and OpenGL further I've found the root of the problem, which was loss of OpenGL Context. All of the texture loading needs to be done when the thread has access to the context, which is true when the app first loads but becomes false later on. In order to fix the problem, the textures need to be loaded in a thread that has access to the OpenGl Context, which can be done very simply by posting the body of the function as a runnable to the app. This gave me a final implementation similar to this:

public void loadSkinnedAssets(final String skin){
    Gdx.app.postRunnable(new Runnable() {
        @Override
        public void run() {
        if(skinnableAtlas != null){
            //Tried both with and without disposing.
            skinnableAtlas.dispose();
        }
        skinnableAtlas = new TextureAtlas(Gdx.files.internal("skins/" + skin + "/Assets.pack"), true);

        if(gameScreen != null){
             gameScreen.refreshSkinnedSprites();
        }
    });
}

I still need to do a lot of testing to ensure that this is a good means by which to do this, but at the very least it accomplishes the end I'm looking for.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.