Kotlin Game Development: Finalizing The Game

This is the last part of the “Kotlin Game Development” series and we are going to add the “game over” scene to our snake game and also reflect on the whole process. It might be helpful to read the previous posts first in order to understand what’s going on.

  1. Part 1 - Introduction
  2. Part 2 - Creating a Scene
  3. Part 3 - Controller Input
  4. Part 4 - Game Loop
  5. Part 5 - Game Factory
  6. Part 6 - Main Menu
  7. Part 7 - Model
  8. Part 8 - Game Scene
  9. Part 9 - Finalizing The Game (you are here)

Game Over Scene

Our game needs one more scene in order to be completed: GameOverScene, let’s add it to our project:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.bubelov.snake.scene

import com.bubelov.snake.engine.Game
import com.bubelov.snake.engine.Input
import com.bubelov.snake.engine.Scene
import java.awt.Color
import java.awt.Font
import java.awt.Font.BOLD
import java.awt.Graphics2D
import java.awt.event.KeyEvent

class GameOverScene(game: Game) : Scene(game) {
    override fun update(nanosecondsPassed: Long) {
        game.input.consumeEvents().forEach {
            when (it) {
                is Input.Event.KeyPressed -> {
                    when (it.data.keyCode) {
                        KeyEvent.VK_ENTER -> game.scene = GameScene(game)
                    }
                }
            }
        }
    }

    override fun draw(graphics: Graphics2D) {
        graphics.apply {
            color = Color.black
            fillRect(0, 0, game.screenSize.width, game.screenSize.height)

            font = Font("Default", BOLD, 16)
            color = Color.white

            val message = "Press <Enter> to start new game"
            val messageBounds = fontMetrics.getStringBounds(message, this)
            val messageWidth = messageBounds.width.toInt()
            val messageHeight = messageBounds.height.toInt()

            drawString(
                message,
                game.screenSize.width / 2 - messageWidth / 2,
                game.screenSize.height / 2 - messageHeight / 2
            )
        }
    }
}

There are 2 major steps happening here:

  • scanning the user input
  • drawing hint text in the center of our new scene

Let’s go through those steps one by one:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
override fun update(nanosecondsPassed: Long) {
    game.input.consumeEvents().forEach {
        when (it) {
            is Input.Event.KeyPressed -> {
                when (it.data.keyCode) {
                    KeyEvent.VK_ENTER -> game.scene = GameScene(game)
                }
            }
        }
    }
}

This step is pretty straightforward, we need to scan through all of the input events in order to find out if the ENTER key was pressed. Pressing the ENTER key means we should navigate to the GameScene and restart our game.

Let’s move to the next step:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
override fun draw(graphics: Graphics2D) {
    graphics.apply {
        color = Color.black
        fillRect(0, 0, game.screenSize.width, game.screenSize.height)

        font = Font("Default", BOLD, 16)
        color = Color.white

        val message = "Press <Enter> to start new game"
        val messageBounds = fontMetrics.getStringBounds(message, this)
        val messageWidth = messageBounds.width.toInt()
        val messageHeight = messageBounds.height.toInt()

        drawString(
            message,
            game.screenSize.width / 2 - messageWidth / 2,
            game.screenSize.height / 2 - messageHeight / 2
        )
    }
}

The first 2 lines are responsible for filling the screen in black. The next two lines are just initializing the font that we want to use for drawing the text and the rest of the code is responsible for actually drawing it. Luckily for us, the Java SDK provides us with the getStringBounds method which can predict the size of the text that we’re going to draw. Using those metrics, we can place our text at the center of the screen:

Game Over scene

Conclusion

In this series we’ve covered the basics of game development using Kotlin and we’ve also created a fully playable snake game.

There are far more in the game development than just that. Here is the steps that I recommend if you want to go further (the order is irrelevant):

  • learn how to create graphics (raster, vector, 3D objects rendered to 2D images, doesn’t matter)
  • learn how to create and add sound effects and music to your game
  • learn a game engine, the best choice depends on your experience in programming. I’d recommend Game Maker for total noobs, LibGDX for people who have solid programming skills and something like Unity if you want to sell your game or pursue a career in game development
  • copy a few successful games with simple mechanics. It can teach you a lot about the art of making games
  • don’t be too hard on yourself. There are tons of things to learn but it is worth it only if you enjoy the process!