Edward Lim
Edward Lim

Reputation: 823

Libgdx textures are all weird when opening app after the back button is pressed?

So this is my dilemma, I'm really close to publishing my game on the play store.

When testing my app on a device I realised that when the home button is clicked, and followed by opening the app again. Everything is handled fine.

However, when the back button is pressed and the app is opened up again, the textures in my game load up weird, like really weird.

Heres are the screen shots of what is supposed to look like and what its not supposed to..

Here is the link that shows what is supposed to look like https://i.sstatic.net/Gj8wr.jpg

And here's the one that loads up after the back button is pressed https://i.sstatic.net/hRML7.jpg

After testing this for quite some time, I believe the problem is that when the back button is clicked, it triggers a "quit" type function that is supposed to EXIT the app. However, the app for some reason still exist in the background of the phone which leads to the weird textures being loaded after the back button is clicked. If I am correct what do I need to do on my end to handle this type of behaviour.

public class MainMenuScreen implements Screen{
    private static final int FLOOR_Y_OFFSET = -150;
    private static final int FOREGROUND_Y_OFFSET = -150;

    private FishyDash game;
    private OrthographicCamera cam;

    private Texture background;
    private Texture foreground;
    private Vector2 foregroundPos1, foregroundPos2;
    private Texture floor;
    private Vector2 floorPos1, floorPos2;
    private Fish fishy;

    //TABLE STUFF
    private Table table;
    private Table buttonTable;
    private Stage stage;

    private Image game_title;
    private Skin skin;
    private TextureAtlas buttonAtlas;
    private TextButton buttonPlay, buttonRate;
    private Viewport gamePort;

    public BitmapFont font;

    //Load all the sounds needed
    public Sound jumpSound;
    public Sound buttonClicked;
    public Music backgroundMusic;

    GameState gameState;

    //On Pause stuff
    public static Vector2 fish_pause_pos;

    public MainMenuScreen(FishyDash game) {
        this.game = game;
        gameState = GameState.READY;

        System.out.println("first");
    }


    @Override
    public void show() {
        System.out.println("second");
        //Initialize all the sounds
        jumpSound = Assets.manager.get(Assets.jumpSound, Sound.class);
        buttonClicked = Assets.manager.get(Assets.buttonClicked, Sound.class);
        backgroundMusic = Assets.manager.get(Assets.backgroundMusic, Music.class);
        backgroundMusic.setLooping(true);
        backgroundMusic.setVolume(.5f);
        backgroundMusic.play();

        //Initialize the textures needed in the MainMenuScreen class
        background = Assets.manager.get(Assets.background, Texture.class);
        floor = Assets.manager.get(Assets.floor, Texture.class);
        foreground = Assets.manager.get(Assets.foreground, Texture.class);

        fishy = new Fish();
        fishy.setPos(80, foreground.getHeight() + FOREGROUND_Y_OFFSET - 150);
        fishy.setMOVEMENT_X(200);
        fishy.setGRAVITY(-40);

        cam = new OrthographicCamera();
        cam.setToOrtho(false, background.getWidth(), background.getHeight());

        foregroundPos1 = new Vector2(cam.position.x - (cam.viewportWidth / 2), 0);
        foregroundPos2 = new Vector2(cam.position.x - (cam.viewportWidth / 2) + foreground.getWidth(), 0);

        floorPos1 = new Vector2(cam.position.x - (cam.viewportWidth / 2), 0);
        floorPos2 = new Vector2(cam.position.x - (cam.viewportWidth / 2) + floor.getWidth(), 0);

        createTable();

//        Gdx.input.setCatchBackKey(true);
//        game.getAdsController().showBannerAd();
    }

    public void createTable() {
        gamePort = new StretchViewport(FishyDash.V_WIDTH, FishyDash.V_HEIGHT, cam);
        stage = new Stage(gamePort, game.batch);
        stage.getViewport().setCamera(cam);

        table = new Table();
        table.setFillParent(true);
        table.setBounds(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());

        buttonTable = new Table();
        buttonTable.setFillParent(true);
        buttonTable.setBounds(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());

        game_title = new Image(new Texture("game_title.png"));
        font = Assets.manager.get(Assets.font, BitmapFont.class);
        buttonAtlas = Assets.manager.get(Assets.buttonAtlas, TextureAtlas.class);
        skin = new Skin(buttonAtlas);

        //Creating the TextStyle for the playButton
        TextButton.TextButtonStyle playButtonStyle = new TextButton.TextButtonStyle();
        playButtonStyle.up = skin.getDrawable("button_up");
        playButtonStyle.down = skin.getDrawable("button_down");
        playButtonStyle.pressedOffsetX = 1;
        playButtonStyle.pressedOffsetY = -1;
        playButtonStyle.font = font;

        //Creating the TextStyle for the rateButton
        TextButton.TextButtonStyle rateButtonStyle = new TextButton.TextButtonStyle();
        rateButtonStyle.up = skin.getDrawable("button_up");
        rateButtonStyle.down = skin.getDrawable("button_down");
        rateButtonStyle.pressedOffsetX = 1;
        rateButtonStyle.pressedOffsetY = -1;
        rateButtonStyle.font = font;

        //Setting the style and text for the playButton
        buttonPlay = new TextButton("PlAY", playButtonStyle);
        buttonPlay.pad(10);

        //Setting the style and text for the rateButton
        buttonRate = new TextButton("RATE", rateButtonStyle);
        buttonRate.pad(10);

        table.top();
        table.add(game_title).padTop(175).expandX();

        buttonTable.bottom().padBottom(200);
        buttonTable.add(buttonPlay).expandX().padLeft(30);
        buttonTable.add(buttonRate).expandX().padRight(30);

//        table.debug();
        stage.addActor(table);
        stage.addActor(buttonTable);

        buttonPlay.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                buttonClicked.play();
                dispose();
                game.setScreen(new GameScreen(game));
            }
        });

        buttonRate.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                buttonClicked.play();
                System.out.println("Nothing happens Yet");
            }
        });
    }

    public void updateWorld(float delta) {

        switch (gameState) {
            case READY:

                fishy.update(delta);

                if (fishy.getPosition().y < foreground.getHeight() + FOREGROUND_Y_OFFSET - 35) {
                    fishy.setMOVEMENT_X(100);
                }

                if (fishy.getPosition().y < floor.getHeight() + FLOOR_Y_OFFSET + 150) {
                    fishy.setMOVEMENT_X(100);
                    fishy.normalJump();
                    jumpSound.stop();
                }

                updateForeground();
                updateFloor();

                cam.position.x = fishy.getPosition().x + (cam.viewportWidth / 2) - 80;
                cam.update();

                table.setPosition(cam.position.x - cam.viewportWidth / 2, 0);
                buttonTable.setPosition(cam.position.x - cam.viewportWidth / 2, 0);

                Gdx.input.setInputProcessor(stage);
                break;

            case PAUSED:
                gameState = GameState.READY;
                break;
        }
    }

    public void drawWorld() {
        game.batch.setProjectionMatrix(cam.combined);
        switch (gameState) {

            case READY:
                game.batch.begin();

                game.batch.draw(background, cam.position.x - cam.viewportWidth / 2, 0);

                game.batch.draw(foreground, foregroundPos1.x, foregroundPos1.y + FOREGROUND_Y_OFFSET);
                game.batch.draw(foreground, foregroundPos2.x, foregroundPos2.y + FOREGROUND_Y_OFFSET);

                game.batch.draw(floor, floorPos1.x, floorPos1.y + FLOOR_Y_OFFSET);
                game.batch.draw(floor, floorPos2.x, floorPos2.y + FLOOR_Y_OFFSET);

                game.batch.draw(fishy.getAnimation().getKeyFrame(fishy.timePassed, true),
                        fishy.getPosition().x, fishy.getPosition().y);

                game.batch.end();

                stage.act();
                stage.draw();
                break;

            case PAUSED:
                break;
        }
    }

    @Override
    public void render(float delta) {
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        updateWorld(delta);
        drawWorld();
    }

    @Override
    public void dispose() {
        System.out.println("Main Menu Dispose Called");

        fishy.dispose();
        stage.dispose();
    }

    public void updateForeground() {
        if(cam.position.x - (cam.viewportWidth / 2) > foregroundPos1.x + foreground.getWidth())
            foregroundPos1.add(foreground.getWidth() * 2, 0);
        if(cam.position.x - (cam.viewportWidth / 2) > foregroundPos2.x + foreground.getWidth())
            foregroundPos2.add(foreground.getWidth() * 2, 0);
    }

    public void updateFloor() {
        if(cam.position.x - (cam.viewportWidth / 2) > floorPos1.x + floor.getWidth())
            floorPos1.add(floor.getWidth() * 2, 0);
        if(cam.position.x - (cam.viewportWidth / 2) > floorPos2.x + floor.getWidth())
            floorPos2.add(floor.getWidth() * 2, 0);
    }

    @Override
    public void resize(int width, int height) {
        stage.getViewport().update(width, height, true);
    }

    @Override
    public void pause() {
        System.out.println("pause");
        gameState = GameState.PAUSED;
    }

    @Override
    public void resume() {
        System.out.println("resume");
    }

    @Override
    public void hide() {
        System.out.println("hide");
    }

    public enum GameState {
        READY, PAUSED;
    }
}

EDIT:

from what i understand changing the asset manager to a what i have below should work, however when I do this change the I cant use manager anymore, it becomes highlighted in red and gives me the error Non-Static Field manager cannot be referenced from a non static content?

AssetManager manager = new AssetManager();

    public static final String font = "fonts/mario.fnt";

    public static final String jumpSound = "sounds/jumpSound.wav";
    public static final String buttonClicked = "sounds/buttonClicked.wav";
    public static final String passSound = "sounds/passSound.wav";
    public static final String gameOver = "sounds/gameOver.mp3";
    public static final String backgroundMusic = "sounds/backgroundMusic.wav";

    public static final String background = "background.png";
    public static final String floor = "floor.png";
    public static final String foreground = "foreground.png";

    public static final String fishAtlas = "sprite_animations/fishy_sprite_ani.atlas";
    public static final String buttonAtlas = "ui/buttons.atlas";


    public Assets() {
    }

    public static void load() {
        manager.load(font, BitmapFont.class);

        manager.load(jumpSound, Sound.class);
        manager.load(buttonClicked, Sound.class);
        manager.load(passSound, Music.class);
        manager.load(gameOver, Music.class);
        manager.load(backgroundMusic, Music.class);

        manager.load(background, Texture.class);
        manager.load(floor, Texture.class);
        manager.load(foreground, Texture.class);

        manager.load(fishAtlas, TextureAtlas.class);
        manager.load(buttonAtlas, TextureAtlas.class);

    }

Upvotes: 1

Views: 400

Answers (3)

Tenfour04
Tenfour04

Reputation: 93629

Here's the more dangerous answer. I only recommend doing this if you fully understand the Android lifecycle, the Libgdx lifecycle, and Java's class objects.

You can keep your static asset references, but you must ensure they are disposed of when the game closes.

public class Assets {

    //Safer to keep this private and use below public method for access
    private static AssetManager manager; //do not instantiate here

    public static AssetManager manager(){
        if (manager == null) load();
        return manager;
    }

    public static void load (){
        if (manager == null) manager = new AssetManager();

        //your existing load code
    }

    public static void dispose() {
        if (manager != null){
            manager.dispose();
            manager = null;
       }
    }

}

public class FishyDash extends Game {

    //...

    @Override
    public void dispose() {
        Assets.dispose();
    }
}

public class MainMenuScreen implements Screen{

    //...

    public void show() {
        System.out.println("second");

        //NOTE THE PARENTHESES AFTER manager below
        jumpSound = Assets.manager().get(Assets.jumpSound, Sound.class);
        //...
    }

    //...

}

Upvotes: 0

Tenfour04
Tenfour04

Reputation: 93629

You should avoid static asset references. When an Android Activity is backed out of, the Application stays alive, so statically referenced objects stay alive as well until the next time you launch your game (a new Activity instance) from the launcher.

You can technically have a static reference to assets, but you need to know exactly what you're doing if you do, and it's error prone. (You would have to be absolutely sure everything is disposed of when the Activity closes, clear your static references to null, and also ensure that you load new instances when the game starts up again.) It is safest to avoid any static reference to anything that implements Disposable.

You need to read up on Java's class objects (not classes, not objects, but "class objects") so you'll understand what's going on here. A static method cannot operate on a non-static member variable, so you also need to remove the static key word from your load method.

And then to access your asset manager, you need to create an instance of your Assets class and access it through a standard member variable:

public class FishyDash extends Game {

    Assets assets;

    public void create(){
        assets = new Assets();
        assets.load();
        //....
    }

}

public class MainMenuScreen implements Screen{

    //...

    public void show() {
        System.out.println("second");

        jumpSound = game.assets.manager.get(Assets.jumpSound, Sound.class);
        //...
    }

    //...

}

Upvotes: 2

m.antkowicz
m.antkowicz

Reputation: 13571

It is problem with static instances of objects existing in the background even if you close the app.

Make sure that you are disposing, nulling and creating as new when app open all instances of assets managers.

Use

AssetManager manager;

...

manager.clear();

also dispose skin if you use some and set null to your handlers if you use some as singletones for example

Upvotes: 0

Related Questions