I would like to show you a version of Game of Life build with Flutter. My intention was to get some insights on the following topics:
- Custom drawing — create your own UI-elements
- Very basic “game” approach — game loop
About the game
Let’s first start with a brief intro explaining what “Game of Life” is actually about. In case you know the board game called “Game of Life” I have to disappoint you — the game we are building here is a bit different. Below is the summary in my words, feel free to skip this and check the great Wikipedia article directly.
The game shows a board of cells which can either be alive or dead. Every turn, each cell gets checked and a new state (dead, alive) is calculated based on the current game board. The rules are quite simple — a cell will only change the state based on the following rules:
- Any living cell with < 2 live neighbors will be changed to dead in the next round
- If a dead cell has 3 living neighbors it will get alive in the next round
- Any living cell with either two or three live neighbors (in all directions) will not change the state in the next round
- Any living cell with more than three neighbors will die in the next round
The game will run infinitely and every round will introduce a new generation of cells until all cells are dead or spread across the board in a way that they can't interact with other cells according to the described rules above.
Code, just show me the code
The complete project is hosted on GitHub. Please note, I’m new to Flutter so please excuse any code smells you might spot:
You can't perform that action at this time. You signed in with another tab or window. You signed out in another tab or…
To start, either create a new flutter project or simply clone my repository. I will skip the project creation, in case you need a helping hand check out the great Flutter starting guide.
We can divide the code into three main areas of tasks:
- The main widget — gets all parts of the game together
- The game logic class — all game rules are applied in here
- The drawing department — drawing of the cells is handled in here
If you’re wondering why the code is split up like this, at first I like to separate code by its actual job and secondly I might want to reuse the game logic code (which is pure dart code) for other projects.
For example, we could set up a Game of Life web version using dart and the logic class from the Flutter project…
The main widget
To keep it short, the job of the main widget is to bring everything together and provide sizing information to our drawing department. Most of the code inside the main.dart file is nothing special, nevertheless I would like to point out the more interesting places to get you started:
The first line creates our logic class, it will be used to interact with our game. To get a new board and ensure the app is running in fullscreen during startup we use the initState method.
Thanks to the _game object we can simply call resetWorld() and get a fresh new start. The last method in the code allows the app to toggle the game between running or paused mode, the call to setState ensures that Flutter will update the UI accordingly.
To have something we can start/pause we need a so called game loop. This loop is handled by our logic object (_game) but somehow we have to update our UI after each iteration to make sure our App shows changes. I decided to use a StreamBuilder for this job — with every loop the StreamBuilder gets a notification to update the UI.
You might spot a bit more in the above code. It includes the callback to our toggle methods to start/pause the game and also some size calculation for the cells. I will explain most of it in the following parts.
To get the game started we have to fill the world, this job is done by the code below:
The board itself is a simple List of boolean values. The resetWorld function performs the following steps:
- Fill the world with dead cells, from zero to worldSize (right now 1024 cells)
- Getting a random number of cells up to 50% of the world size, see documentation for nextInt
- Mark cells as living until we reached the limit generated in our second step
You might have noticed that the above logic could end up with less active cells as our second step decided. This is due to the fact that the third step doesn’t check if the randomly selected cell is already active, it could happen that we set the same cell active twice.
Ready to go
Our game is ready to start, now we need to take care of the game loop that will update the cells based according to the game rules.
Game updates are handled by the _runTheGame Method. To ensure the loop is running “slow” enough it starts with sleep, our app will wait 350ms before it calculates the next round.
This part might look complicated but it solves a problem that all Game of life implementation face:
How do you handle cells at the edge of the board?
In my app the board has no real edges, if you are on the far right side and go one step to the right you will end up on the first left position. The same for the top or bottom end of the board, if you step over the edges you will end up on the opposite side.
It might remind you of the old Pacman game.
The complete calculation to apply the game rules is done by changing the index inside the array to check all surrounding cells, left, right, top, bottom and diagonal.
The drawing department
Getting the logic on the screen is the job of our drawing department, in this game we don’t require a lot of elements. Everything we need for custom drawing can be found in the code below.
Flutter contains the great and easy to use CustomPainter, the main idea is quite easy, it has two jobs:
- Draw the element with the provided size on the given canvas
- Decide if you need to draw your element again
Our cells will be represented by a rectangle with rounded edges, the color depends on the cell state. Living cells will be green, dead cells will be orange.
Drawing the rectangle is done by the build in function drawRRect you need to pass a rectangle with size, position and the radius for the edges.
A cell has to be painted again only if:
- The position or size of the cell has changed, for example, because the user rotated the phone
- The cell state changed, a living cell died or the other way around
Last puzzle piece is to arrange all cells in a way that our app renders the board, this is done by the code below:
The big idea behind the code is to draw the board row by row based on the calculated sizes.
Since this post I worked quite a bit with Flutter and Flame, right now I’m developing a “real” game from zero to credit screen ;) using Flame and Box2D check out my post below.
Games in Flutter — Flame & Box2D Part 1
I really like video games and I like to create and develop software. So I set down and checkout Flutter for this. 2D &…
First, Flutter is awesome, I tried a lot of cross-platform development tools but I have to admit that Dart and Flutter are easy to start and the provided tooling just works ( I’m using Linux, tested it also on Windows).
Creating custom UI elements is made in a logical two-step implementation, decide what you want to draw and when to redraw.
Game of life itself can be implemented in many ways, for myself it’s always a good way to test out a new platform/language. Besides the technical point of view, I like to watch and see how a randomly created board develops into different patterns or simple fades out or gets stuck in a repeating motion.
If you are curious about all the different shapes/pattern Game of life might generate check out the following resources:
If you want to see an epic version of Game of life:
Thanks for reading