Reputation: 823
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
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
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
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