Making a Kotlin game in 20 minutes using KorGE engine

Few days ago I was trying to come with a new project, digging a bit i discovered KorGE, a 2D game engine, so i decided to make a Kotlin game, a simplified version of the google chrome dinosaur game.

Kotlin game with KorGE, Chrome dinosaur game

This was my first game ever, I’ve worked with Unity before, but only for 3D modelling stuff testing. This is not a tutorial, only a “walkthrough” of my way to design the program, so I won’t dig too much in the code implementation. Anyways, the source code is in github, here

Structuring the game / program

One thing I like to do when I write code, is to break everything into smaller and independent problems which doesn’t need to know about other parts of the program, sounds familiar?, this is one of the SOLID principles.

So if we break the game into smaller pieces, we will have this:

  • The player, which is the dino.
  • The world, which is the environment with obstacles and ground
  • The score board, which will display our current score while we progress
  • Some kind of abstraction to hold the game state, this will be the GameManager

Creating the World class

The world class contains the environment assets, like the background and the obstacles

The obstacles logic is pretty simple

1 – Game starts, create 3 obstacles and add them to a mutableList, update their X position

private suspend fun initializeWorld() {
        var obstacleX = originX
        val y = 320.0
        var floorX = 0.0
        for (i in 0..2) {
            obstacles.add(Obstacle(obstacleX,y).create())
            floor.add(FloorTile(floorX, floorY).create())
            obstacleX += 500
            floorX += 1200
        }
        obstacles.forEach {
            addChild(it)
        }
        floor.forEach {
            addChild(it)
        }
    }

2 – When the obstacle goes off screen,it gets removed from the Scene, and a new one is created, so the game will have a max number of 3 obstacles always, every time one is removed, another one is added

 val obstacleIterator = this.obstacles.iterator()
 while(obstacleIterator.hasNext()) {
    val obstacle = obstacleIterator.next()
    val x = obstacle.x - (1 * speedFactor)
    obstacle.position(x, obstacle.y)
    if(x < 0) {
       obstacleIterator.remove()
       removeChild(obstacle)
       addObstacle()
    }
}

Creating the Player class: The dinosaur

In KorGE, there are Views, a View is a class which has a graphic representation, like a rectangle, a circle or an Image

In my case, im going to use an Sprite, an image with multiple images inside, and KorGE anmates it using an SpriteAnimation. The dinosaur sprite has coordinates, x and y, they represent the dino position in the Scene. I’m going to animate only the Y coordinate, since the obstacles will move in the X axis going into the dinosaur.

Each view in KorGE has a method called addUpdater, which is called every frame, and inside we can update our view position.

I have this method, which will give return the coordinates based on the player status (running, jumping up, jumping down…)

private fun getCoordinates(startingX: Double, startingY: Double): Coordinates {
        val x: Double = startingX
        var y: Double = startingY

        when (status) {
            PlayerStatus.JUMPING_UP -> {
                y -= 1 * speedFactor
                if(y <= yLimit) {
                    status = PlayerStatus.JUMPING_DOWN
                }
            }

            PlayerStatus.JUMPING_DOWN -> {
                y += 1 * speedFactor
                if(y >= initialY) {
                    status = PlayerStatus.RUNNING
                }
            }
            PlayerStatus.RUNNING -> {
                this.startAnimation()

            }
        }

        return Coordinates(x,y)
    }

And then the dinosaur position can be updated like this

dino.addUpdater {
    if(game.isRunning) {
        val coordinates = this@Player.getCoordinates(this.x, this.y)
        dino.position(coordinates.x, coordinates.y)
    }
}

Also I needed collission detection, but KorGE has me covered

dino.onCollision({it.name === "obstacle"}) {
    game.finish()
    stopAnimation()
}

Once the dino steps into an obstacle, the sprite animation will stop, and the game will finish

Creating the GameManager

I decided to create a class to hold all the game state, the GameManager class purpose is to inform to the other game entities like the Player and the World the game status, if the game is running, paused or restarted for example.

It also takes care of the game messages, it receives a Container instance which is the one coming from our scene

class GameManager(private val container: Container) {

    var isRunning = false
    var status = GameStatus.NOT_STARTED
    private var message: Text? = null
    private var gameOverMessage: Text? = null

    fun start(){
        isRunning = true
        status = GameStatus.RUNNING
    }

    fun finish() {
        isRunning = false
        status = GameStatus.FINISHED
        if(message == null &amp;&amp; gameOverMessage == null) {
            displayGameOverMessage()
        }
    }

    suspend fun restart() {
        status = GameStatus.RESTARTED
        removeGameOverMessage()
        delay(TimeSpan(500.0))
        start()
    }

    private fun displayGameOverMessage() {
        val gameOver = container.text("GAME OVER")
        gameOver.centerOnStage()
        gameOver.y += 50
        val text = container.text("Press Space to restart")
        text.centerOnStage()
        text.y += 100
        container.addChild(text)
        container.addChild(gameOver)
        message = text
        gameOverMessage = gameOver
    }

    private fun removeGameOverMessage() {
        container.removeChild(gameOverMessage)
        container.removeChild(message)
        message = null
        gameOverMessage = null
    }
}

This class has all the game state in 40 lines, the Player is the entity which communicates with the game manager, so if the dino has a crash with an obstacle, it will inform the game manager

The whole project took me about 20 minutes after I read the korGE docs, really fun and easy to use

Pixo