How to make a "Game Over" and start the game over again?

Question:

I am trying to program an Endless Running style video game in Android Studio with libGDX and I have encountered a problem in which I have been stalled for a long time, once the game detects the collision, the game goes to the "Game Over" state but I am not able to bring it back to startup state to restart the game. Another bug I've run into has been that I can't make the coins disappear when the character collides with them, although this is a minor bug.

 package es.rodriguez.dinojump;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Rectangle;
import com.sun.org.apache.bcel.internal.generic.RETURN;

public class Game extends ApplicationAdapter {
    SpriteBatch batch;

    //Fondos
    Texture background;
    Texture background2;
    int bg2altura = 200;//altura del suelo

    //Dinosaurio
    Texture[] dinos;
    float velocity = -30;
    float gravity = 2;
    float impulse = -30;
    float dinoY;
    int dinoAnim = 0;
    int salto = 0;
    Rectangle dinoR;

    //Bomba y moneda
    Texture bomb;
    Rectangle bombR;

    Texture coin;
    Rectangle coinR;

    float objectVelocity = 10;
    int numberOfObjects = 4;
    float[] objectX = new float[numberOfObjects];
    float distanceBetweenObjects;

    GameState gameState = GameState.TAP_TO_PLAY;


    //private ShapeRenderer shrend;
    //private boolean debug;

    @Override
    public void create() {
        batch = new SpriteBatch();

        SoundManager.loadSFX();

        background = new Texture("bg.png");
        background2 = new Texture("bg2.jpg");

        //Dino
        dinos = new Texture[5];
        dinos[0] = new Texture("frame-1.png");
        dinos[1] = new Texture("frame-2.png");
        dinos[2] = new Texture("frame-3.png");
        dinos[3] = new Texture("frame-4.png");
        dinos[4] = new Texture("dizzy-1.png");
        dinoY = (bg2altura);

        dinoR = new Rectangle(dinos[0].getWidth() / 2F, dinoY, dinos[0].getWidth() / 2, dinos[0].getHeight() / 2);

        //Bombas y monedas
        bomb = new Texture("bomb.png");
        bombR = new Rectangle();

        coin = new Texture("coin.png");
        coinR = new Rectangle();

        distanceBetweenObjects = Gdx.graphics.getWidth() * 3 / 4;
        for (int i = 0; i < numberOfObjects; i++) {
            objectX[i] = Gdx.graphics.getWidth() + i * distanceBetweenObjects;
        }

        /*
        //Para testear
        shrend = new ShapeRenderer();
        debug = true;*/
    }

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

        switch(gameState) {

            case TAP_TO_PLAY:
                if (Gdx.input.justTouched()) {

                    gameState = GameState.PLAY;
                    salto += 1;
                    SoundManager.play(SFX.JUMP);
                }
                break;

            case PLAY:

                SoundManager.play(SFX.GAME);

                for (int i = 0; i < numberOfObjects; i++) {

                    if (objectX[i] < -bomb.getWidth()) {
                        objectX[i] = objectX[i] + numberOfObjects * distanceBetweenObjects;
                    }
                    objectX[i] = objectX[i] - objectVelocity;
                }

                if (Gdx.input.justTouched()) {
                    if (salto < 2) { //
                        salto += 1;
                        SoundManager.play(SFX.JUMP);
                        velocity = impulse;
                    }
                } else {
                    velocity = velocity + gravity;
                    dinoR.y -= velocity;
                }
                //Para que no pase para abajo del suelo
                if (dinoR.y < bg2altura) {
                    dinoR.y = bg2altura;
                    salto = 0;
                }
                //Animar el dinosaurio
                if (dinoAnim == 3) {
                    dinoAnim = 0;
                } else {
                    dinoAnim += 1;
                }
                break;

            case GAME_OVER:

                if (dinoR.overlaps(bombR)) {

                    gameState = gameState.TAP_TO_PLAY; //falta reiniciar juego

                }

                if(dinoR.overlaps(coinR)){
                    SoundManager.play(SFX.SCORE);

                }



                    //Falta reiniciar juego


                break;
        }

        batch.begin();

        batch.draw(background, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        batch.draw(background2, 0, 0, Gdx.graphics.getWidth(), bg2altura);

        batch.draw(dinos[dinoAnim], dinoR.x, dinoR.y,dinos[0].getWidth()/2, dinos[0].getHeight()/2);

        for (int i = 0; i < 4; i++) {
            bombR = new Rectangle(objectX[i], bg2altura, bomb.getWidth()/2, bomb.getHeight()/2);
            batch.draw(bomb, bombR.x, bombR.y, bomb.getWidth(), bomb.getHeight());


            coinR = new Rectangle(objectX[i], bg2altura+coin.getHeight()*3,coin.getWidth()/2, coin.getHeight()/2);
            batch.draw(coin, coinR.x, coinR.y, coinR.width, coinR.height);

             if (dinoR.overlaps(bombR)) {

                SoundManager.play(SFX.HIT);
                dinos[dinoAnim] = dinos[4];
                SoundManager.hitSFX.dispose();
                SoundManager.gameSFX.stop();
                gameState = gameState.GAME_OVER; //falta reiniciar juego

            }else if(dinoR.overlaps(coinR)){
                SoundManager.play(SFX.SCORE);

                //Falta contador de monedas, arreglar o sonido
            }
        }
        batch.end();

    }

    @Override
    public void dispose() {
        batch.dispose();
        background.dispose();
        background2.dispose();
       // shrend.dispose();
    }
}

The code can be improved but I am starting.

Answer:

Alternative 1

You can encapsulate all the code you have in that class and then restart by calling new Game() me explain …

It would consist of 3 different classes

  • Assets which will handle all Disposables . Separating this will avoid creating them every time we call new Game()
  • GameManager this is the main class, which extends ApplicationAdapter this class will be in charge of creating a new game and rendering it.
  • Game which will handle all the logic of the game.

Assets

public class Assets {
    public Texture background;
    public Texture background2;
    public Texture dinos = new Texture[5];
    public Texture bomb;
    public Texture coin;
    public Texture shrend;

    public Assets(){
        SoundManager.loadSFX();

        background = new Texture("bg.png");
        background2 = new Texture("bg2.jpg");
        dinos[0] = new Texture("frame-1.png");
        dinos[1] = new Texture("frame-2.png");
        dinos[2] = new Texture("frame-3.png");
        dinos[3] = new Texture("frame-4.png");
        dinos[4] = new Texture("dizzy-1.png");
        bomb = new Texture("bomb.png");
        coin = new Texture("coin.png");
        shrend = new ShapeRenderer();
    }

    public void dispose(){
        for(Texture t: dinos)
            t.dispose();

        background.dispose();
        background2.dispose();
        bomb.dispose();
        coin.dispose();
        shrend.dispose();
    }
}

GameManager

public class GameManager extends ApplicationAdapter { // OJO ESTA ES LA CLASE PRINCIPAL, LA UNICA QUE EXTIENDE `ApplicationAdapter`

    private Assets assets;
    private batch;

    @Override
    public void create() {
        batch = new SpriteBatch();
        assets = new Assets();
        game = new Game(assets);
    }

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

        game.render(batch);

        if(game.gameState == GameState.GAME_OVER)
            game = new Game(assets);// aqui reseteas el juego


    }

    @Override
    public void dispose() {
        batch.dispose();
        assets.dispose();
    }
}

Game

public class Game {

    //Fondos
    int bg2altura = 200;//altura del suelo
    float velocity = -30;
    float gravity = 2;
    float impulse = -30;
    float dinoY;
    int dinoAnim = 0;
    int salto = 0;

    Rectangle dinoR;
    Rectangle bombR;
    Rectangle coinR;

    float objectVelocity = 10;
    int numberOfObjects = 4;
    float[] objectX = new float[numberOfObjects];
    float distanceBetweenObjects;

    public GameState gameState = GameState.TAP_TO_PLAY;
    private Assets assets;

    //private ShapeRenderer shrend;
    //private boolean debug;


    public Game(Assets assets) {
        this.assets = assets;

        dinoY = (bg2altura);
        dinoR = new Rectangle(assets.dinos[0].getWidth() / 2F, dinoY, assets.dinos[0].getWidth() / 2, assets.dinos[0].getHeight() / 2);

        //Bombas y monedas
        bombR = new Rectangle();
        coinR = new Rectangle();

        distanceBetweenObjects = Gdx.graphics.getWidth() * 3 / 4;
        for (int i = 0; i < numberOfObjects; i++) {
            objectX[i] = Gdx.graphics.getWidth() + i * distanceBetweenObjects;
        }

        /*
        //Para testear
        shrend = new ShapeRenderer();
        debug = true;*/
    }

    public void render(SpriteBatch batch) {
        switch(gameState) {

            case TAP_TO_PLAY:
                if (Gdx.input.justTouched()) {

                    gameState = GameState.PLAY;
                    salto += 1;
                    SoundManager.play(SFX.JUMP);
                }
                break;

            case PLAY:

                SoundManager.play(SFX.GAME);

                for (int i = 0; i < numberOfObjects; i++) {

                    if (objectX[i] < -bomb.getWidth()) {
                        objectX[i] = objectX[i] + numberOfObjects * distanceBetweenObjects;
                    }
                    objectX[i] = objectX[i] - objectVelocity;
                }

                if (Gdx.input.justTouched()) {
                    if (salto < 2) { //
                        salto += 1;
                        SoundManager.play(SFX.JUMP);
                        velocity = impulse;
                    }
                } else {
                    velocity = velocity + gravity;
                    dinoR.y -= velocity;
                }
                //Para que no pase para abajo del suelo
                if (dinoR.y < bg2altura) {
                    dinoR.y = bg2altura;
                    salto = 0;
                }
                //Animar el dinosaurio
                if (dinoAnim == 3) {
                    dinoAnim = 0;
                } else {
                    dinoAnim += 1;
                }
                break;
            // ESTO ES REDUDANTE, ahora `GameManager` se encarga de este caso
            //case GAME_OVER:

            //  if (dinoR.overlaps(bombR)) {

            //      gameState = gameState.TAP_TO_PLAY; //falta reiniciar juego

            //  }

            //  if(dinoR.overlaps(coinR)){
            //      SoundManager.play(SFX.SCORE);

            //  }



                //Falta reiniciar juego


            //  break;
        }

        batch.begin();

        batch.draw(assets.background, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        batch.draw(assets.background2, 0, 0, Gdx.graphics.getWidth(), bg2altura);

        batch.draw(assets.dinos[dinoAnim], dinoR.x, dinoR.y,assets.dinos[0].getWidth()/2, assets.dinos[0].getHeight()/2);

        for (int i = 0; i < 4; i++) {
            bombR = new Rectangle(objectX[i], bg2altura, assets.bomb.getWidth()/2, assets.bomb.getHeight()/2);
            batch.draw(assets.bomb, bombR.x, bombR.y, assets.bomb.getWidth(), assets.bomb.getHeight());


            coinR = new Rectangle(objectX[i], bg2altura+assets.coin.getHeight()*3,assets.coin.getWidth()/2, assets.coin.getHeight()/2);
            batch.draw(assets.coin, coinR.x, coinR.y, coinR.width, coinR.height);

            if (dinoR.overlaps(bombR)) {

                SoundManager.play(SFX.HIT);
                assets.dinos[dinoAnim] = dinos[4];
                SoundManager.hitSFX.dispose();
                SoundManager.gameSFX.stop();
                gameState = gameState.GAME_OVER; //falta reiniciar juego, ya no.

            }else if(dinoR.overlaps(coinR)){
                SoundManager.play(SFX.SCORE);

                //Falta contador de monedas, arreglar o sonido
            }
        }
        batch.end();

    }
}

Grades:

  • case GAME_OVER is now redundant, this case is handled by GameManager
  • new Rectangle() in render is a bad idea, creating this object at 60 times per second can be overwhelming for the Heap, instead you can use bombR.set(...);

Alternative 2

Another and probably the simplest but less elegant way is to reset all variables to their original state.

public void reset(){
   //aqui reinicias todas las variables a su estado original.
   dinoY = (bg2altura);
   ... etc ect 
}

and it would look like this …

public class Game extends ApplicationAdapter {
    SpriteBatch batch;

    //Fondos
    Texture background;
    Texture background2;
    int bg2altura = 200;//altura del suelo

    //Dinosaurio
    Texture[] dinos;
    float velocity = -30;
    float gravity = 2;
    float impulse = -30;
    float dinoY;
    int dinoAnim = 0;
    int salto = 0;
    Rectangle dinoR;

    //Bomba y moneda
    Texture bomb;
    Rectangle bombR;

    Texture coin;
    Rectangle coinR;

    float objectVelocity = 10;
    int numberOfObjects = 4;
    float[] objectX = new float[numberOfObjects];
    float distanceBetweenObjects;

    GameState gameState;


    //private ShapeRenderer shrend;
    //private boolean debug;

    @Override
    public void create() {
        batch = new SpriteBatch();

        SoundManager.loadSFX();

        background = new Texture("bg.png");
        background2 = new Texture("bg2.jpg");
        dinos = new Texture[5];
        dinos[0] = new Texture("frame-1.png");
        dinos[1] = new Texture("frame-2.png");
        dinos[2] = new Texture("frame-3.png");
        dinos[3] = new Texture("frame-4.png");
        dinos[4] = new Texture("dizzy-1.png");
        bomb = new Texture("bomb.png");
        coin = new Texture("coin.png");

        reset();

        /*
        //Para testear
        shrend = new ShapeRenderer();
        debug = true;*/
    }

    public void reset(){
        //TODO todas las variables que cambien se resetean al estado original.
        gameState = GameState.TAP_TO_PLAY;
        velocity = -30;
        gravity = 2;
        impulse = -30;
        dinoY;
        dinoAnim = 0;
        salto = 0;
        bg2altura = 200;

        dinoY = (bg2altura);
        dinoR = new Rectangle(dinos[0].getWidth() / 2F, dinoY, dinos[0].getWidth() / 2, dinos[0].getHeight() / 2);
        bombR = new Rectangle();
        coinR = new Rectangle();

        distanceBetweenObjects = Gdx.graphics.getWidth() * 3 / 4;
        for (int i = 0; i < numberOfObjects; i++) {
            objectX[i] = Gdx.graphics.getWidth() + i * distanceBetweenObjects;
        }
    }

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

        switch(gameState) {

            case TAP_TO_PLAY:
                if (Gdx.input.justTouched()) {

                    gameState = GameState.PLAY;
                    salto += 1;
                    SoundManager.play(SFX.JUMP);
                }
                break;

            case PLAY:

                SoundManager.play(SFX.GAME);

                for (int i = 0; i < numberOfObjects; i++) {

                    if (objectX[i] < -bomb.getWidth()) {
                        objectX[i] = objectX[i] + numberOfObjects * distanceBetweenObjects;
                    }
                    objectX[i] = objectX[i] - objectVelocity;
                }

                if (Gdx.input.justTouched()) {
                    if (salto < 2) { //
                        salto += 1;
                        SoundManager.play(SFX.JUMP);
                        velocity = impulse;
                    }
                } else {
                    velocity = velocity + gravity;
                    dinoR.y -= velocity;
                }
                //Para que no pase para abajo del suelo
                if (dinoR.y < bg2altura) {
                    dinoR.y = bg2altura;
                    salto = 0;
                }
                //Animar el dinosaurio
                if (dinoAnim == 3) {
                    dinoAnim = 0;
                } else {
                    dinoAnim += 1;
                }
                break;

            case GAME_OVER:

                if (dinoR.overlaps(bombR)) {

                    gameState = gameState.TAP_TO_PLAY; //falta reiniciar juego

                }

                if(dinoR.overlaps(coinR)){
                    SoundManager.play(SFX.SCORE);

                }



                //Falta reiniciar juego


                break;
        }

        batch.begin();

        batch.draw(background, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        batch.draw(background2, 0, 0, Gdx.graphics.getWidth(), bg2altura);

        batch.draw(dinos[dinoAnim], dinoR.x, dinoR.y,dinos[0].getWidth()/2, dinos[0].getHeight()/2);

        for (int i = 0; i < 4; i++) {
            bombR = new Rectangle(objectX[i], bg2altura, bomb.getWidth()/2, bomb.getHeight()/2);
            batch.draw(bomb, bombR.x, bombR.y, bomb.getWidth(), bomb.getHeight());


            coinR = new Rectangle(objectX[i], bg2altura+coin.getHeight()*3,coin.getWidth()/2, coin.getHeight()/2);
            batch.draw(coin, coinR.x, coinR.y, coinR.width, coinR.height);

            if (dinoR.overlaps(bombR)) {

                SoundManager.play(SFX.HIT);
                dinos[dinoAnim] = dinos[4];
                SoundManager.hitSFX.dispose();
                SoundManager.gameSFX.stop();
                reset();//TODO falta reiniciar juego, ya NO.


            }else if(dinoR.overlaps(coinR)){
                SoundManager.play(SFX.SCORE);

                //Falta contador de monedas, arreglar o sonido
            }
        }
        batch.end();

    }

    @Override
    public void dispose() {
        batch.dispose();
        background.dispose();
        background2.dispose();
        // shrend.dispose();
    }
}

Remove coins

A more organized idea to stop drawing or rendering the coins would be using OOP

You create a Coin class, in the Game class you make an Array<Coin> that contains all the coins, with a loop you draw all the coins of that Array<Coin> as soon as the player collides with a coin, you remove the coin from the coins.removeValue(coin,false); array coins.removeValue(coin,false);

public class Coin extends Rectangle{ //Nota que extiende la clase Rectangle para que podamos utilizar .overlaps y las variable x,y,width,height para dibujar
   public Texture texture;

   public Coin(Texture texture,float x,float y,float width, float height){
       this.texture = texture;
       set(x,y,width,height);
   }

   public void render(SpriteBatch batch){
       batch.draw(texture,x,y,width,height);
   }
}
public class Game{
   ...
   ...
   Array<Coin> coins = new Array<Coin>();

   public Game(){
     ...
     ...
     //a~ade las monedas que quieras
     coins.add(new Coin(assets.coin,0,0,10,10));
     coins.add(new Coin(assets.coin,100,0,10,10));
     coins.add(new Coin(assets.coin,200,0,10,10));
   }

   public void render(){
     ...
     batch.begin();
     for(Coin coin: coins){
         coin.render(batch); //dibujas la moneda
         if(dinoR.overlaps(coin)){
             //se ha capturado una moneda
             monedasCapturadas++;
             coins.removeValue(coin,false); //quitas la moneda del array
         }
     }
     batch.end();

   }
}
Scroll to Top