Chapter 4

Controlling the Flow of the Code

Up until this point, almost all the code we have written executes strictly from top to bottom. The computer reads the first line, executes it, moves to the second line, executes it, and so on.

That is great for setting up a scene or doing simple math, but games are all about decisions. If the player's health reaches zero, the game needs to stop. If the bullet hits the alien, the alien needs to explode. If the screen is tapped on the left side, the character needs to jump left.

To make these things happen, we need to teach our code how to make decisions based on the current state of our variables. We call this controlling the flow of the code.

In this chapter, we will:

Booleans and Comparisons

Back in Chapter 2, we met the Boolean data type, which holds either true or false. Controlling the flow of code is entirely built on Booleans. We ask the computer a question, the computer evaluates it to either true or false, and the code branches based on that answer.

To ask questions, we use Comparison Operators.

val playerScore = 100
val highScore = 250

val isNewRecord = playerScore > highScore  // Evaluates to false
val isTied = playerScore == highScore      // Evaluates to false

Here are the comparison operators you will use every day in Kotlin:

Whenever you use one of these operators, the result is a Boolean.

The if Statement

Once we have a true or false answer, we use an if statement to act on it. You actually already used one of these in the Bouncing Ball project!

var playerLives = 3

if (playerLives == 0) {
    // This code only runs if playerLives is exactly 0
    showGameOverScreen()
}

The syntax is always the same:

  1. The word if.
  2. A condition inside parentheses (). The condition must evaluate to true or false.
  3. A block of code inside curly braces {}.

If the condition is true, the computer runs the code inside the braces. If the condition is false, the computer skips the braces entirely and continues with the rest of the program.

Remember our bouncing ball?

if (ballX - ballRadius < 0f) {
    ballX = ballRadius
    ballVelX = -ballVelX
}

"If the ball has gone past the left edge of the screen, push it back and reverse the velocity." That is an if statement doing exactly what it was designed to do.

else and else if

Sometimes, skipping the code isn't enough. You want to say, "If this is true, do this, otherwise, do that." For that, we use else.

var hasGoldKey = false

if (hasGoldKey == true) {
    openTreasureChest()
} else {
    playLockedSound()
}

If hasGoldKey is true, the chest opens. If it is false, the locked sound plays. Only one of those blocks of code will ever run.

Pro tip: Because hasGoldKey is already a Boolean, we don't actually need the == true part. We can just write if (hasGoldKey). It reads much more like natural English!

What if you have more than two choices? We can chain them together using else if.

val playerHealth = 45

if (playerHealth > 75) {
    drawHealthyCharacter()
} else if (playerHealth > 25) {
    drawInjuredCharacter()
} else {
    drawCriticalCharacter()
}

The computer checks these from top to bottom. As soon as it finds one that is true, it runs that block of code and skips the rest.

if as an Expression

Here is something Kotlin does that many other languages do not. In Kotlin, if does not just run code — it can also produce a value that you store in a variable. This is one of the small touches that makes Kotlin feel clean once you get used to it.

Suppose we want to store the larger of two scores. The long-winded way uses a var:

var winningScore = 0
if (playerOneScore > playerTwoScore) {
    winningScore = playerOneScore
} else {
    winningScore = playerTwoScore
}

That works, but Kotlin lets us collapse it. Because if produces a value, we can assign the whole thing directly to a val:

val winningScore = if (playerOneScore > playerTwoScore) {
    playerOneScore
} else {
    playerTwoScore
}

Whichever branch runs, its final value becomes the value of the whole if, and that is what gets stored in winningScore. Notice we could use val this time — the value is decided once and never changes.

For short cases like this, we don't even need the braces:

val winningScore = if (playerOneScore > playerTwoScore) playerOneScore else playerTwoScore

If you have programmed before, you may have met the "ternary operator" (? :) in other languages. Kotlin deliberately doesn't have one, because if as an expression does the same job and reads more clearly. We will use this pattern throughout the book to pick a value cleanly in a single line.

Logical Operators

Often, a game needs to check multiple things at once. "Can the player cast this spell?" The answer is only yes if they have enough mana AND they have learned the spell.

To combine questions, we use Logical Operators.

AND (&&)

The && operator checks if the condition on the left and the condition on the right are both true. If either one is false, the whole thing evaluates to false.

val hasMana = true
val knowsSpell = false

if (hasMana && knowsSpell) {
    castFireball()
} else {
    // This code will run because knowsSpell is false
    playErrorSound()
}

OR (||)

The || operator (two vertical pipes) checks if at least one of the conditions is true.

val hasRedKey = false
val hasMasterKey = true

if (hasRedKey || hasMasterKey) {
    // This code will run because hasMasterKey is true!
    openDoor()
}

NOT (!)

We already saw the ! symbol in the "not equal to" (!=) operator. You can also place it in front of any Boolean variable to flip it. True becomes false, and false becomes true.

val isPaused = false

if (!isPaused) {
    // Reads as "If NOT paused"
    updateGameLogic()
}

The when Expression

If you string too many else if statements together, your code can get messy and hard to read. Kotlin gives us a beautiful alternative called the when expression.

If you have ever programmed in Java or C++, you might know this as a switch statement. Kotlin's when is much more powerful.

val currentPowerUp = "Speed"

when (currentPowerUp) {
    "Speed" -> increasePlayerSpeed()
    "Shield" -> activateShield()
    "FireRate" -> shootFaster()
    else -> doNothing() // The default if nothing else matches
}

Instead of writing if (currentPowerUp == "Speed") ... else if (currentPowerUp == "Shield") ..., we just pass the variable to when and list out the possible matches. The little arrow -> points to the code that should run for that match.

Just like if, when can also produce a value. This is where it really shines. Suppose each power-up should give the player a different speed boost. We can let when hand back the right number directly:

val speedBoost = when (currentPowerUp) {
    "Speed" -> 50
    "Shield" -> 0
    "FireRate" -> 10
    else -> 0
}

Here, when looks at currentPowerUp, finds the matching line, and the value after the arrow becomes the value of the whole expression — which we store in speedBoost. When you use when this way, Kotlin requires an else branch, because the variable must always end up with some value. There can be no gap.

The when expression is fantastic for managing "game states" (like deciding whether to draw the Main Menu, the Playing State, or the Game Over screen). We will use it heavily later in the book.

Summary

You now know how to make your code think.

You can use Comparison Operators (==, <, >) to check the state of your variables. You can use if, else if, and else to execute different blocks of code based on those checks. You also saw that, unlike many languages, Kotlin lets if and when hand back a value you can store directly in a variable. You can combine complex rules using Logical Operators (&&, ||, !), and you can keep long lists of choices clean using the when expression.

In the next chapter, we are going to put all of this into practice. We are going to build a new Android project that introduces player interaction. When the player taps the screen, the game will use if statements to figure out where they tapped, and change the behavior of the game based on their input.

AI Exercise (Optional)

Time to test your AI assistant on controlling flow.

Open your AI chatbot and paste this prompt:

"I am a beginner learning Kotlin for Android game development. I just learned about if/else, logical operators (&&, ||, !), and the when expression. Can you write a short, text-based Kotlin example simulating a player trying to open a locked door in a dungeon? The player needs to have a 'gold key' OR a 'lockpick' AND their 'stealth level' needs to be greater than 5. Use an if/else block with logical operators to see if they succeed. Then, explain the order in which Kotlin evaluates the && and || operators in your example."

This prompt asks the AI for a specific scenario that combines && and || in the same if statement. Combining them can sometimes cause confusing results depending on which one the computer checks first (known as operator precedence). Read the AI's explanation carefully to understand how Kotlin handles it!