Everything a computer does eventually boils down to moving numbers and text around in its memory. Variables are how we, as programmers, give those numbers and text names that we can actually remember and work with.
Without variables, we would have no way to store a player's score, track the position of a spaceship, or remember whether the player has collected the gold key. In other words, without variables, we have no games.
In this chapter, we will cover the most important Kotlin concepts that our games will rely on for the rest of the book. None of it is difficult in isolation, but taken together, this is the foundation everything else is built upon.
In this chapter, we will:
- Understand what a variable is and how the computer stores data.
- Learn the difference between
varandval. - Meet the most common Kotlin data types:
Int,Float,Boolean, andString. - Get a first glimpse of
nulland why Kotlin is so strict about it. - See how Kotlin is smart enough to guess your data types (Type Inference).
- Perform mathematical operations on variables.
- Convert a value from one type to another.
- Try an optional AI exercise to explore variables further.
Let's start at the beginning.
What Is a Variable?
A variable is a named container for a piece of data. That is it. You tell the computer, "I want a tiny chunk of memory, I want to call it score, and I want it to hold a whole number."
From that moment on, whenever you type the word score in your code, the computer knows exactly which piece of memory you mean and what kind of data lives there.
A useful way to picture this is a row of labeled boxes on a shelf. Each box holds exactly one thing, and each box has its name written on the front. When you want to know what is in score, you look at the score box. When you want to change it, you reach in, throw away the old number, and put a new number inside.
There are two things we always tell the computer when we make a variable: its name and, sooner or later, its type. The name is the label on the box — what we will type whenever we want to use it. The type tells the computer what kind of thing lives inside: a whole number, a decimal, a true/false value, a piece of text. We will meet the common types in a moment.
There are a few rules about variable names in Kotlin. They can contain letters, numbers, and underscores, but they cannot start with a number. They cannot contain spaces. Programmers typically use a naming style called camelCase — lowercase for the first word, and uppercase for the start of every word after that, like playerScore, enemyHealth, or isGameOver. We will stick to this convention throughout the book.
A well-chosen name is worth its weight in gold. The letter x might be fine for a throwaway example, but playerScore tells you — and anyone reading your code later, including future you — exactly what the value represents. Good names are one of the cheapest and most effective things you can do to keep your code easy to work with. Spend the extra second to name things well.
var vs val
When you create a variable in Kotlin, you must decide right away whether it is allowed to change later. You do this by starting your line of code with either var or val.
var (Variable)
If you use var, the value inside the box can be changed as many times as you like. var is short for variable.
var playerScore = 0
// Sometime later in the game...
playerScore = 10
In the preceding code, we create a box named playerScore and put 0 in it. Later, we throw the 0 away and put 10 in it. This works perfectly.
val (Value)
If you use val, the value inside the box is locked forever. val is short for value. It is constant.
val maxLives = 3
// Sometime later in the game...
maxLives = 4 // ERROR! The compiler will stop you here.
If you try to change a val, Android Studio will draw a jagged red line under your code, and the game will refuse to compile.
Why would you want a box that can't be changed? Because games get complicated quickly. If you know that the maximum number of lives in your game is exactly 3, making it a val guarantees that a typo somewhere else in your code can't accidentally change it. It makes your code safer and easier to trust.
Rule of thumb: Always use val unless you know for an absolute fact the number needs to change. If it needs to change, use var.
Kotlin Data Types
When you create a variable, the computer needs to know what type of data is going to live in that box. Kotlin has several types, but we will use four of them constantly.
Int (Whole Numbers)
The Int type stores a whole number — positive, negative, or zero. No decimal point. No fractions. Just whole numbers like 0, 7, -42, or 1000.
In games, Int is everywhere. Scores, lives, ammo counts, the number of enemies on screen — all of these are naturally Int variables.
var ammo: Int = 30
val level: Int = 1
Notice the syntax: we state var or val, then the name of the variable, then a colon :, then the type (Int), and finally an equals sign = to give it a starting value.
Float (Decimal Numbers)
Not every number we want to work with is a whole number. The position of a spaceship on screen might be 342.7 pixels across. A timer might need to count down in fractions of a second. For numbers with a decimal point, we use Float.
var shipX: Float = 320.0f
var shipY: Float = 240.5f
Notice the little f at the end of the numbers. That f is absolutely required. Without it, Kotlin assumes any number with a decimal point is a different, more precise data type called a Double. A Double uses twice as much memory as a Float and can hold roughly twice as many digits of precision. For graphics and drawing on Android, we almost exclusively use Float, because that is what the Canvas and Paint drawing system expects. Double is for situations where precision genuinely matters, which is rare in the kind of games we are building. Get in the habit of typing that f!
There is one thing about decimal numbers that catches every programmer out eventually. They are not always perfectly precise. A Float set to 0.1f might actually be stored as something like 0.0999999. Most of the time this makes no difference — you will never notice a spaceship being one ten-millionth of a pixel off. But if you ever compare two Float values and get a result you didn't expect, this tiny imprecision is almost certainly why. We will note it again when it matters.
Boolean (True / False)
Not every question in a game has a numeric answer. Sometimes you just want to know yes or no. Is the game paused? Has the player collected the key? Did the bullet hit the enemy?
For these situations, we use Boolean. A Boolean variable can hold only two possible values: true or false.
var isGameOver: Boolean = false
var hasKey: Boolean = true
Boolean variables are usually named as a question, like isAlive or hasKey. We will really put Booleans to work in Chapter 4 when we learn how to make our code branch and make decisions.
String (Text)
When we want to work with words, sentences, or messages, we use String. A String is simply a string of characters wrapped in double quotes.
val playerName: String = "Alex"
var welcomeMessage: String = "Welcome to the game!"
Notice the double quotes around the text. That is how Kotlin knows where the text starts and stops.
A First Look at null
You may have already spotted a lonely question mark back in Chapter 1, tucked into the very first function we wrote: onCreate(savedInstanceState: Bundle?). That ? is one of the most important characters in the whole Kotlin language, so let's take a quick look at it now.
In Kotlin, an ordinary variable is never allowed to hold "nothing." If you declare a String, it must contain an actual piece of text. If you declare an Int, it must hold an actual number. This rule prevents an entire category of crashes that has plagued programmers for decades.
But sometimes "nothing" is a genuine, valid answer. Which enemy did the player tap on? Maybe none of them. What is in the player's hand? Maybe nothing. To allow for that, Kotlin lets you mark a type with a ?, which means "this is allowed to hold nothing." That special "nothing" value is called null.
var targetName: String = "Goblin" // Must always hold some text
var maybeTarget: String? = null // The ? means it is allowed to hold nothing
In the preceding code, targetName can never be null — Kotlin guarantees it. But maybeTarget, with its ?, is allowed to be null. The moment you add that ?, Kotlin starts watching you like a hawk, forcing you to check for null before you use the value, so your game can't crash by trying to use something that isn't there.
That is the whole idea for now. A ? after a type means "this might hold nothing." It is Kotlin quietly protecting you from one of the most common bugs in all of programming. We will cover null properly, including how to handle it gracefully, in Chapter 14.
Type Inference
You might have looked at var ammo: Int = 30 and thought, "Obviously that is an integer. Why do I have to tell the computer that?"
Kotlin agrees with you.
Kotlin has a feature called Type Inference, which means it is smart enough to guess the data type based on the value you provide. You can drop the : Int or : String part entirely, and Kotlin will just figure it out.
var ammo = 30 // Kotlin knows this is an Int
var shipX = 320.0f // Kotlin knows this is a Float because of the 'f'
var isGameOver = false // Kotlin knows this is a Boolean
val playerName = "Alex" // Kotlin knows this is a String
We will use Type Inference for almost everything in this book. It makes our code much cleaner and faster to read. You only need to explicitly state the type if you are creating a variable but not giving it a value right away.
Mathematical Operators
Storing numbers is useful. Doing math with them is where the fun starts. Kotlin gives us all the standard arithmetic operations you learned in school.
var a = 10
var b = 3
var sum = a + b // 13
var difference = a - b // 7
var product = a * b // 30
var quotient = a / b // 3 (Wait, what?)
var remainder = a % b // 1
Let's look at quotient. 10 / 3 gave us 3, not 3.333. That is because when you divide two Int values, Kotlin gives you an Int result. It throws away the fractional part completely. This is called integer division, and it catches beginners out constantly.
If you want a decimal answer, the numbers need to be Float values:
var exactQuotient = 10.0f / 3.0f // 3.3333333
The last operator, %, is called the modulo operator. It gives you the remainder after division. 10 % 3 is 1 because 10 divided by 3 is 3, with 1 left over. Modulo is surprisingly useful in games for things like wrapping things around the screen or checking if a number is even or odd.
Compound Assignment Operators
We often want to change a variable based on its current value. For example, the player loses a life:
lives = lives - 1
The computer reads the right side first (lives - 1), figures out the answer, and stores it back in the lives box. This pattern is so common that Kotlin provides shortcuts for it:
var score = 100
score += 10 // Same as: score = score + 10 (now 110)
score -= 5 // Same as: score = score - 5 (now 105)
score *= 2 // Same as: score = score * 2 (now 210)
These are called compound assignment operators. They do exactly the same thing as the longer version; they just save you some keystrokes.
Adding or subtracting exactly 1 is so common in games — counting down lives, stepping through a list, advancing a frame of animation — that Kotlin gives it an even shorter shortcut:
var lives = 3
lives++ // Same as lives += 1 (now 4)
lives-- // Same as lives -= 1 (now 3)
The ++ operator is called increment and -- is called decrement. You will see them constantly once we reach loops in Chapter 6.
Converting Between Types
Every now and then we have a value of one type and need it as another. This happens more than you might think. Android stores a View's width and height as whole numbers (Int), but our drawing math works in Float. We will hit exactly this situation in the very next chapter.
Kotlin will not quietly convert numbers from one type to another for you. It wants you to ask explicitly, which prevents a whole category of sloppy mistakes. You ask using one of the built-in conversion functions: .toFloat(), .toInt(), .toDouble(), and so on.
val screenWidth: Int = 1080
val widthAsFloat = screenWidth.toFloat() // 1080.0f
val exactPosition = 342.7f
val pixel = exactPosition.toInt() // 342 — the decimal part is dropped
In the preceding code, screenWidth.toFloat() produces a Float version of our whole number so it can be used in Float math. Going the other way, exactPosition.toInt() chops off everything after the decimal point — 342.7 becomes 342, not 343. It does not round; it simply throws the fraction away. That is worth remembering, because it occasionally produces results that surprise you.
You will see .toFloat() in our very next project, so it is worth getting comfortable with the idea now: when two types need to meet, you convert one to match the other.
Summary
We have covered the basic building blocks of Kotlin. You now know how to lock values in place with val and keep them flexible with var. You know how to store whole numbers (Int), decimals (Float), true/false logic (Boolean), and text (String). You have seen how Type Inference keeps your code clean, and you know how to perform basic math on your variables.
This might not feel like much yet, because we haven't done anything visual with any of it. That changes in the next chapter. We will put these variables to work in our very first project — a small app where the variables you create directly control a shape moving and bouncing around the screen.
Variables are not just boxes for numbers; they are the levers and dials that drive everything your game does.
AI Exercise (Optional)
If you would like to explore variables a little further on your own with an AI assistant, here is an exercise to try. If you don't want to use AI at all, just skip this section!
Open your AI chatbot of choice (like ChatGPT or Google Gemini). Paste in the following prompt:
"I'm a complete beginner learning Kotlin. Please explain the difference between a
Floatand aDoublein more depth. Cover how much memory each uses, how many digits of precision each gives, and give me one example from game programming where using aDoubleinstead of aFloatwould actually matter. Keep it conversational, and assume I just learned what a variable is."
Notice that the prompt does several things at once. It tells the AI who you are (a beginner), what you want (a deeper explanation), what to cover specifically, and what to avoid (assumed knowledge).
When you get the answer back, read it carefully. Does the game example make sense to you? If it falls short, don't be afraid to push back. "That example was too advanced — give me a simpler one" is a perfectly good follow-up prompt. Make the AI work to your level!