The state of the game

We now have our Snake game at the point where it is almost a complete game; we just need to add a few more things to achieve this. Currently, our game stops when the snake collides with itself. What we need to add is a way to restart the game if this happens, so the player can have another go!

To do this, we will do the following:

  • Adding a game state
  • Displaying game-over text
  • Restarting the game

Adding a state

When you think about it, a game can have many states. Seriously, pick one of your favorite games and consider the states the game could be in. Let's list some out:

  • Start
  • Playing
  • Paused
  • Game over
  • Restart

It could be argued that start and restart are the same thing, but that really depends on the game and what you, as the game developer, would like to achieve. For our little Snake game, we are only going to focus on two states for now: playing and game over.

Let's first start off by creating an enumeration of our states, and let's create a member of GameScreen to use it:

private enum STATE {
   PLAYING, GAME_OVER
}

private STATE state = STATE.PLAYING;

By default, our game will start in the playing state, no different than how it is now!

Next, let's update our render() method to take this new state into consideration:

public void render(float delta) {
    switch(state) {
        case PLAYING: {
            queryInput();
            updateSnake(delta);
            checkAppleCollision();
            checkAndPlaceApple();
        }
        break;
        case GAME_OVER: {

        }
        break;
    }
    clearScreen();
    drawGrid();
    draw();
}

What we have achieved here is, essentially, only updating the game state while in playing mode. What this means is that we can now do away with that code-invasive hasHit flag, as we can now change the state of the game to GAME_OVER instead!

Let's update our checkSnakeBodyCollision() method to reflect this:

private void checkSnakeBodyCollision() {
    for (BodyPart bodyPart : bodyParts) {
        if (bodyPart.x == snakeX && bodyPart.y == snakeY) state = STATE.GAME_OVER;
    }
}

Now run the game. It should run exactly as it did earlier! The exception is that we now enter the GAME_OVER state on collision.

Player feedback

Now that we have the game set up to handle different states, let's provide the player with some feedback to the effect that they lost (aww) our little Snake game. Once you get comfortable with making games, particularly with the LibGDX framework, you can really make some amazing things happen in the game-over scenario. However, for our game, we will just display some text.

How are we going to achieve this? Luckily, for us, LibGDX has bitmap font support, so we can go ahead and start drawing text on the screen. By default, LibGDX comes with Arial font at size 15, which will do for now.

To use this bitmap font, we need to define and then create an instance in our GameScreen class, once again in the show() method; we update it as follows:

private BitmapFont bitmapFont;

public void show() {
    bitmapFont = new BitmapFont();
    // other code omitted
}

Next, we will make it draw some text to the screen. Let's update our draw() method with a call to the draw method of our font:

private void draw() {
   batch.begin();
   //Other render code omitted
   bitmapFont.draw(batch, "This Snake Game is AWESOME!", 0, 0);
   batch.end();
}

The bitmapFont.draw() method signature shows that it requires a batch object, the text, and the location to start at. Let's place it in the bottom-left corner for now!

Let's run our game and see our text on the screen!

Oh, it didn't render! Well, technically it would have, if it had been on screen. However, what I didn't point out is that it renders the text from the top-left corner rather than the bottom-left corner, as with our game textures.

The question is, How do we know where to draw it then, as different fonts will have different widths and heights? Well, we have to define an instance of a class called GlyphLayout. This class allows us to obtain the size of the string we wish to display. Perfect!

Let's update our code to utilize this then. First let's define the GlyphLayout class:

    private GlyphLayout layout = new GlyphLayout();

Next, we then set the text which we have to use.

String text = "This Snake Game is AWESOME!";
layout.setText(text);
bitmapFont.draw(batch, text, 0, layout.height);

What is happening here is that we are using the height of our font as the y coordinate to start the render from. Run the game and see for yourself. The text will now appear where we expected it to, and hopefully will look like the following screenshot:

Player feedback

However, as awesome as this is, this isn't what we are looking to achieve. What we are aiming to do is to have the text displayed when the game is over.

First, let's add a constant value with our game-over message, again in the GameScreen class:

private static final String GAME_OVER_TEXT = "Game Over!";

Finally, in our draw() method, we can update this to draw our text when in the GAME_OVER state:

if (state == STATE.GAME_OVER) {
  layout.setText(bitmapFont, GAME_OVER_TEXT);
  bitmapFont.draw(batch, GAME_OVER_TEXT, (viewport.getWorldWidth() - layout.width) / 2, (viewport.getWorldHeight() - layout.height) / 2);
}

The position at which we are setting the render of the font is the center of the screen, offset by the size of the text.

Let's run the game and get to a GAME_OVER state to see our text!

But before we do that, let's turn off the drawGrid() call so it doesn't get in the way! We can simply just comment it for now.

Hopefully, your game will look as shown in the following screenshot:

Player feedback

Restart your engines!

So, the player now knows that they have lost the game. However, what if they want to play again? More complex games will have different flows through which they might want to take the player during a restart scenario of the game. However, for us, we just want to have the game set itself back up and let the player go again!

First, let's define how to restart the game. Let's use the space bar to restart the game. Now, create a method called checkForRestart() and another called doRestart() in our GameScreen class. Inside it, we will query the input for the space bar:

private void checkForRestart() {
   if (Gdx.input.isKeyPressed(Input.Keys.SPACE)) doRestart();
}

private void doRestart() {
   state = STATE.PLAYING;
   bodyParts.clear();
   snakeDirection = RIGHT;
   directionSet = false;
   timer = MOVE_TIME;
   snakeX = 0;
   snakeY = 0;
   snakeXBeforeUpdate = 0;
   snakeYBeforeUpdate = 0;
   appleAvailable = false;
}

As you can see in our doRestart() method, we are resetting the game logic. This will put everything back to how it was when the game launched.

Finally, add a call to the checkForRestart() method in the GAME_OVER case in our render() call:

case GAME_OVER: {
    checkForRestart();
}

Let's run the game again. Now when we enter the GAME_OVER state, if we press the spacebar, the game will reset! Perfect!

As a final touch, we should update our GAME_OVER_TEXT constant to let the player know how to restart, as follows:

private static final String GAME_OVER_TEXT = "Game Over... Tap space to restart!";

Now your game over should look like this:

Restart your engines!
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.255.168