Unity: Create Your Own Wordle in under 24 Hours

Wordle has become a phenomenal game. Many developers, me included, are trying to get that piece of hype launching clones, from a complete copy to something truly unique.

In this tutorial I will show you how to create your own version of a “classic Wordle” with 6 lines, 5 letter cells and a handpicked list of words. Considering the fierce competition, the goal here is to build a fully-functional game in under 24 hours.

Prerequisites

I’m using Unity 2021.2.13. This is already my third implementation of Wordle, if you want to check out the other two, here are the links:

  • Datele — a spin-off where you need to guess a date instead of a word
  • Categorle — a variant where you need to guess a word 4–7 letters long in a given category

As always, project sources are available at GitHub, find the link at the end of the page.

There are five parts in this tutorial: Planning, Scene setup, General implementation, Input validation and Statistics and state handling.

Part 1. Planning

The beauty of the game is in its simplicity, there are basically only four rules:

  • Only 1 word to guess per day
  • Each cell has to be filled with a letter
  • It must be an existing word from a predefined list
  • 6 tries to guess the correct word

You might be thinking that the first rule should include “Every day there should be a new word to guess”, but I actually omitted that part. There is only one original Wordle where this rule should be applied to. For mobile clones it doesn’t make much sense to keep it.

As per the game events there are only 2 of those:

  • After a user submits a word each cell and corresponding letter control should change its color to either gray, yellow or green.
  • Statistics of guesses are displayed after the game ends.

Looking at all the requirements listed above we can conclude that the following key parts should be implemented:

  • “timer” that will make sure only one word per day can be played
  • state to keep statistics between different sessions
  • “dictionary” with words of choice
  • a connection between the cells on the game field and the letter controls

Now we have a good overview of what to build, so let’s dive right in!

Part 2. Scene preparation

Before we start coding let’s have the Scene ready. To keep things simple, I’ll only show you how to set up a Game scene.

Note! In this tutorial I’m using TextMeshProUGUI component instead of the legacy UI.Text for all the texts. For the shortness sake I’ll refer to TextMeshProUGUI as TextMesh.

Let’s start off by creating the basics of the UI — the lines. We will keep them in a new game object called GameField inside a VerticalLayoutGroup to make it nicely aligned:

Each Line is a HorizontalLayoutGroup with an Image for the cell and a TextMeshes for the letter.

Moving on, let’s set up our first letter control. At this point we won’t build the entire “keyboard” just yet because first we want to figure out how the connection between cells and letter controls will be organized. Apart from the letter we will also add Enter and Backspace controls. The keyboard will reside in the Controls object.

The last piece of UI needed is the Statistic window. It should have a place to put the answer, general statistics about player’s performance and visual for guesses distribution.

I’ll add this component to the GameField object so that it is easier to handle game states later.

Scene setup is now nearly complete. The rest of the letter controls will be added in the next section.

Part 3. General implementation

Now it’s time to wire up different parts of the UI together!

We will start with the Line object. Looking at the original game, we can see that Line will perform 4 actions:

  • Receive a letter
  • Remove a letter
  • Validate the input
  • Check if the input is a correct answer

Additionally, the cell should react to the given action with a dedicated animation. To ease the animation part I’ll be using an amazing DOTween plugin.

Note! You can still proceed with the rest of the tutorial even if it is the first time you see that plugin. It’s API is very easy to read — every method starts with the prefix DO.

The GameField object will serve as an orchestrator, connecting Lines with letter controls. We can already define the set of methods that will be used later.

You can see that in order to keep the track of the current line a simple int field is used which is incremented every time the player gives a wrong answer.

In order to maintain a good separation of concerns the letter controls won’t be used directly in the GameField script but rather will be proxied through the Controls script.

This script will be responsible also for keeping the input within the limit of 5 characters.

The last missing piece here is the LetterControl script that will be attached to the letter controls.
There are various ways to implement this component. Since there is a predefine amount of letters I’ve decided to keep the actual letter in Unity, setting up each of them manually.
Then, the script will look like:

At this point we can already set up the rest of the letter controls in Unity. Each of them will have a Button, an Image and a TextMesh:

Single LetterControl object

The keyboard was organized into 3 separate game objects — UpperLetters, MiddleLetters, LowerLetters — each of those has a HorizontalLayoutGroup.

If you press the controls now nothing will happen as our Line script has no implementation yet.

Before we add a proper input handling let’s take a break and implement another crucial part of the game — words list handling.

The file with the words will be kept in the Resources folder. Each word will be on the new line and will be in lowercase.

In order to read the file and keep the data a new script — GameData — will be created:

Since we don’t store the index of the word that was played already, the GetWord method has hardcoded index of 0 (the first word from the list) for now. We will create a new GameData object in Unity and attach the script to it.

Now let’s get back to the input handling. As per the identified requirements both the cell and the letter control should change the background color depending on the user’s input. Once again, there are various ways to wire the components together. Keeping in mind the time constraints it will be implemented in the most straightforward way. As the colors of both objects are changed at the same time Line will get a reference of corresponding LetterControl so that the Line object can trigger a coloring of the LetterControl accordingly. In order to achieve that we will need to apply the following changes:

Also, we can already add a nice animation for the successful end of the game — the PlaySuccess method (Note the use of DOTween). Additionally, another instance variable _currentField has been added to keep track of the current cell. Next, we will apply the following changes to GameField:

Apart from extending the EnterLetter method with the LetterControl parameter an instance variable _gameEnd has been introduced.
Lastly, Controls should be changed:

Also here we will connect the CheckAnswer method with the Enter button and RemoveLast with Backspace.

Enter button
Backspace button

Now if you run the game the input from the LetterControls will be propagated to the Line. And if you hit Enter you can see the success animation in action:

Now that we have the main game loop in place it’s time to add validation of the input.

Part 4. Input validation

There are two methods left unimplemented from the previous part — IsValidInput and CheckAnswer.

Input is considered valid if all the letters are entered and the word is in the list of words from the Words.txt file.
At this moment we don’t have a dedicated UI element to show an error message, thus let’s create one — ErrorPopup. It will have an Image and a TextMesh. Color alpha will be set to 0 for both as we don’t want it to be visible:

The script to interact with the newly created component will look like:

Now it is time to add aforementioned validation logic to the Line script.

Note! Method signature has been changed — it now returns string instead of bool. I must mention that it is far from the best practices for handling text for your game. It was done this way due to the time constraints. Please, check out Unity: Localization Made Easy for best practices.

Now GameField should be changed accordingly with ErrorPopup and GameData added:

Validation results can be seen here:

The last part is to make the CheckAnswer method to actually check if the given input is a correct answer and color the Line and LetterControls accordingly.

Here it gets tricky as we need to check each input character against the corresponding character in the answer word.
There are 3 possible scenarios:

  • Given character is not found in the answer word — then the cell and the input control should turn grey.
  • Given character is found in the answer word and it is in the correct position — then the cell and the input control should turn green.
  • Given character is found in the answer word but on the wrong position — then the cell and the input control should turn yellow unless:
    – input control was previously marked green — then it shouldn’t change to yellow
    – given character is duplicated in input but only one character exists in the answer — then only the first character should be marked yellow and all the subsequent ones should be grey. The input control should be yellow. Example: input word is sweep, answer word is earth, then the first e will be yellow while the second e should be grey.
    – given character appears in the input more times than in the correct answer word AND all the correct spots are already taken — then the extra input character should be grey. The input control should be green.
    Example: input word is daddy, answer word is dawds, then the first and the third d should be green and the second d should be grey.

Additionally, Line animation should be performed cell by cell.

All things considered, I ended up with a rather complicated and not future proof algorithm that will be definitely reworked once unit tests are added. But given the time constraints, that’s the best one I could come up with.

ColorCollection is a simple static class with the 3 colors that are used for both cells and letter controls.

You have probably noticed that there is a new method — ChangeColorControl — that is called from Line::CheckAnswer. That’s the one responsible for proper coloring of LetterControls:

It is not safe to rely on an Image color for comparison. Thus, a new private enum to track the state of color has been introduced.

Lastly, GameField has been changed accordingly:

If you start the game now you can see the follow results:

Note! In this project I consciously violated various best practices widely accepted in the developer circles. It is okay when you want to develop something fast but always remember to come back and clean it up!

Part 5. Statistics and state handling

In the last part I’ll show you how to track the player’s statistics and make only one word available daily.

Let’s start off by creating the statistics model. Based on the statistics screen developed previously, we will need to keep track of the following: successes and failures, so that we can calculate the win rate, guess distribution and current and max streak. Additionally, we will add an index of the current word to be guessed from the Words.txt file.

I’d like to draw your attention to the int[] lineSuccessStats field. As we have a limited number of lines (6 in total) the easiest way to collect the distribution data is a simple array where the index corresponds to the line when the puzzle was solved.

Next, we will create a dedicated script to handle the stats — GameStatsSaver. It will read the previously saved stats and handle the saving of the new ones.

Before we implement the stats handling logic we will quickly get back to the GameData. Currently, we don’t progress in the words list after completion, so let’s fix this now.

First of all we will add the GameStatsSaver script to a new object on the scene. Next, we will access it in the GameData script and read the current word index

When it comes to the logic to be implemented for the Statistics window there are 2 possible scenarios:

  • player guessed the word:
    – successes count should be incremented
    – count for the line of the guess should be incremented
    – current streak should be incremented
    – max streak should be updated, if it is higher that the previous one
  • player failed to guess the word:
    – failures count should be incremented
    – current streak should be set to 0

Also, in both cases the current word index should be incremented.

Let’s go ahead and implement the logic accordingly in the GameStatsSaver script:

As you remember, there should be only one word available daily. In order to have this requirement covered PlayerPrefs will be used. If you have seen any of my other tutorials you will definitely recognize the following script:

The logic here is quite straightforward — when the game is over the current day is saved.
In order to prevent users from manipulating PlayerPrefs on their devices the property value is stored in base64 format.

Lastly, let’s wire it all together in the newly created Statistic script attached to the Statistic game object under the GameField game object:

Most of the code parts are self-explanatory. Although, I want to highlight one part — a distribution bar UI implemented in ExecuteOnShow method. Depending on the guess distribution data the bars should have a different length. And if the counts are the same for two different lines — the length should be the same.

The only missing piece now is to use the Statistic script in the GameField script, so that the statistic window is shown upon game completion:

Note! A simple “Come tomorrow” screen has been added as well. You can see it in action if you run the game sources that are available on Github.

Now the game is complete! See, the final results here:

Successful guess
Failed guess

Afterwards

Well done finishing the tutorial! It has been my longest one thus far.
Certain parts (such as current game state saving, StartMenu scene setup, tutorial window creation etc.), were intentionally not covered to keep the tutorial length reasonable.

Should you have any questions please leave them in the comments section below.

The project source files can be found at this GitHub repository.

You can check it all in action in the games:

  • Datele — a spin-off where you need to guess a date instead of a word
  • Categorle — a variant where you need to guess a word 4–7 letters long in a given category

In case you want your Wordle clone to have an endless gameplay, do check out the Bonus part of my next tutorial Unity: Building and Deploying WebGL game.

For the most comprehensive list of Wordle-like games and resources online be sure to check out this page.

Support

If you like the content you read and want to support the author — thank you very much!
Here is my Ethereum wallet for tips:
0xB34C2BcE674104a7ca1ECEbF76d21fE1099132F0

--

--

--

Serious software engineer with everlasting passion for GameDev. Dreaming of next big project. https://pudding.pro

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

How to Create Azure Virtual Machine?

What’s that? #whatsappdown?

Refactoring Code in Ruby

ChainWars Alpha: Progression Report 4

Python3: Mutable, Immutable… everything is object!

10 Best DevOps Tools 2021

January Recap

Meet Apache Airflow — #01

A pinwheel, the Apache Airflow symbol.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Pudding Entertainment

Pudding Entertainment

Serious software engineer with everlasting passion for GameDev. Dreaming of next big project. https://pudding.pro

More from Medium

Unity: Building and Deploying WebGL game

Use Better Inputs in Your Games

Week 24: How to add SFX to you game without going mad

Tiling a Procedurally Generated Unity TileMap