Recently, I decided to write a chess program to experiment with AI, inspired by the ongoing World Chess Cup tournament held in Tbilsi, Georgia. I thought I could take some time today to share the foundation for my game – the chessboard. We will be using Unity and writing in C#, and we will also need a few piece assets. As a side note, project files for this series on chess will be uploaded to GitHub as soon as I finish them. In the mean time, any asset or line of code you need will be provided in the lessons that introduce them. If you have any questions or seek clarification on anything at all in this tutorial, I’m always available to answer them here. Additionally, if you are coming to this tutorial solely because you are interested in chess, you’ll need a little more background to follow along with me in Unity. I’d recommend downloading the editor, experimenting with it, and then viewing some of the learning resources Unity provides to get accustomed to the workflow. Finally, I just finished a tutorial series on Pong, so feel free to read parts one and two if you want a little practice. Back to the main feature – Chess in Unity!

If you’re not familiar with chess, or just a little rusty, it’s an age old game (we’re talking single-digit centuries) played between two players by moving pieces on an 8×8 checkered board. Rows on the board are called “ranks” and are numbered 1 to 8, and columns are called “files” and denoted with letters, ‘a’ through ‘h’. The aforementioned six pieces are arranged on opposite sites of the board as opposing “teams”, white and black:

Each side starts with their pawns on the 2nd or 7th rank, with more powerful pieces guarded behind them on the 1st and 8th ranks. The Queens rest on the middle file squares that match their color (the white queen is on D1, a white or “light” square, while the black queen starts on D8, a black or “dark” square). The Kings rests beside the Queens on the E file. These royal pieces are protected by two bishops, then two knights, and finally, two rooks. Each of these pieces has its own ruleset for moving to new squares and capturing other pieces. White moves first, and the game continues until the opponent’s King is placed in irreconcilable danger, an action known as a “checkmate”.

Enough talk about chess, let’s make it! What we will need:

Sprites of each piece, all white (6 total)

Two materials that we can assign to the pieces (white or black)

A Piece.cs script that each piece can inherit from, with a boolean variable indicating white/black, a Move method, a Capture method, and two methods that return true or false depending on whether or not a potential move or capture is “legal”. These last two methods will be virtual, so that each pieces class (Pawn.cs, Bishop.cs, King.cs, etc.) can override them with their own unique rulesets.

Six unique piece scripts that hold their own rulesets and inherit from Piece.cs (named after the six pieces). We can go ahead and attach these to our piece sprites and save them all as prefabs.

A square-shaped game object that we can use to represent one square of the board. I will be using Unity’s generic quad, but it may be a better idea to use a lighter-weight sprite instead of the the quad’s mesh components. This square should be turned into a prefab so that we can refer to it like a blueprint when creating our board. It will also be nice to have a Tile.cs script attached to this prefab, so that we can reference the positions of pieces a little easier

Two materials that we can assign to the tiles (light or dark)

A Main Camera that is positioned well enough to see the board clearly (use your own judgement here. Mine is set to a position of (4, 3.5, -10) with a black background

An empty game object called “Game Manager” that has the following scripts: GameManager.cs (useful for setting up the game, relaying inputs, and monitoring time/score) Board.cs, the class responsible for setting up the tiles and pieces at the start of the game



I drew a mock-up of the six main pieces in Piskel, a relatively lightweight, low barrier-of-entry pixel art editor and animation tool. I’d highly recommend looking into the desktop app if you are in need of quick temp-art for your 2D projects. Anyway, the pieces shown in this image are, from right-left and top-bottom:

Pawn

Knight

Bishop

Rook

Queen

King

We can create empty game objects for each of these pieces, import these sprites with the Sprite Editor into our Assets folder, and then assign each sprite to the matching game object. Then, we can create prefabs out of these pieces and delete them from the hierarchy. Be sure to adjust the pixels per unit field in the Inspector when importing the sprites, so that their widths and heights are 1×1 Unity units. Mine are set to 256:

Note that I made twelve sprites, six in white and six in black. The black sprites are unnecessary, since we will color the sprites with materials a bit later. This also enables us to re-skin our pieces later, if we choose to do so.

Let’s check our Assets folder to make sure we’re on the same page:

We can also create four materials using the Sprites/Default shader, and choose the colors that we want to represent our squares and pieces:

We have everything set up now! Let’s open up Board.cs and get our chessboard up and running. We will need a reference to our square prefab, as well as references to all of our piece prefabs. Finally, we need to reference our four materials. Let’s take a look:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class Board : MonoBehaviour {

public GameObject tilePrefab;

public GameObject pawnPrefab, knightPrefab, bishopPrefab, rookPrefab, queenPrefab, kingPrefab;

public Material whiteMat, blackMat;

public Material whitePieceMat, blackPieceMat;

}



In the inspector, let’s attach our prefabs to the associated fields within the Game Manager:

Alright, back in the Board script, let’s think about how we want to arrange the tiles. We know that the chessboard is a grid, and that means our tiles will need to be arrange along 2D (x, y) coordinates. Since we’ve set the position of all of our game objects to (0, 0), we can let that point be our A1 square, and work up to H8 at (7, 7). We can use a 2D array of squares to represent this:

public GameObject[,] squares = new GameObject[8, 8];

Then, we can create a method called CreateBoard that instantiates these squares at every integer position of the grid:

public void CreateBoard() {

for (int i = 0; i < 8; i++) {

for (int j = 0; j < 8; j++) {

squares [i, j] = Instantiate (tilePrefab, new Vector3 (i, j, 0), Quaternion.identity);

}

}

}

In order to test our code, we need to call the CreateBoard() method somewhere. Let’s turn to the GameManager script quickly and give it a reference to the board:

public class GameManager : MonoBehaviour {

Board board;

void Start() {

board = gameObject.GetComponent<Board> ();

board.CreateBoard ();

}

}

Now when we run the game, we see an 8×8 grid of squares, but… the squares are all the same color! Back in Board.cs, we can add some logic to this method to assign light or dark materials to the squares, depending on their location. For a checkerboard pattern, dark squares appear at coordinates that are either both even (4,2), or both odd (3,7). White squares appear at mismatched, even-odd coordinates like (1, 2). Let’s look at this logic in action:

public void CreateBoard() {

for (int i = 0; i < 8; i++) {

for (int j = 0; j < 8; j++) {

…

// if the square is double odd or double even, it‘s black

if (i % 2 != 0 && j % 2 != 0 || i % 2 == 0 && j % 2 == 0) {

squares [i, j].GetComponent<Renderer> ().material = blackMat;

} else {

squares [i, j].GetComponent<Renderer> ().material = whiteMat;

}

}

}

}

Wonderful! It’s starting to look a lot like a chess board. I’d like to clean up the hierarchy though. We can assign all of the newly created squares as children of the Game Manager, and give them all proper names. Since chess squares are named after their file and rank, we can make a static array of letters ‘a’ through ‘h’ to help us:

[HideInInspector]

public static string[] alphabet = new string[] {“a“, “b“, “c“, “d“, “e“, “f“, “g“, “h“};

public void CreateBoard() {

for (int i = 0; i < 8; i++) {

for (int j = 0; j < 8; j++) {

…

squares [i, j].transform.SetParent (gameObject.transform);

squares [i, j].name = alphabet [i] + (j + 1);

}

}

}

Great, the board itself is good-to-go. Onto the pieces… let’s make an array of Game Objects called “pieceArrangement”, as well as a new method called SetupPieces() that fill out the array with the pieces in the correct order:

GameObject[] pieceArrangement;

…

public void SetupPieces() {

…

pieceArrangement = new GameObject[8] {

rookPrefab, // R = 0

knightPrefab, // N = 1

bishopPrefab, // B = 2

queenPrefab, // Q = 3

kingPrefab, // K = 4

bishopPrefab, // B = 5

knightPrefab, // N = 6

rookPrefab // R = 7

};

}

Since pieces are arranged by rank, we can use a single for loop to contain our logic, and iterate by file along the ranks we choose. Since our game is using (0,0) as the position of the A1 square, our ranks will be arranged from 0 to 7, not the tradition 1 to 8. Don’t worry, fellow chess enthusiasts – this is only represented in the code itself!

Knowing this, the white pieces will be arranged on the 0th rank, followed by the white pawns on the 1st rank. On the other end of the board, the black piece get placed along the 7th rank, behind the black pawns on the 6th. Let’s see what this looks like in our SetupPieces() method:

public void SetupPieces() {

…

for (int i = 0; i < 8; i++) {

// SETUP 1st RANK WHITE PIECES

GameObject newWhitePiece = Instantiate (pawnPrefab, squares [i, 1].transform);

newWhitePiece.gameObject.GetComponent<Piece> ().white = true;

newWhitePiece.GetComponent<Renderer> ().material = whitePieceMat;

// SETUP 2nd RANK WHITE PAWNS

GameObject newWhitePawn = Instantiate (pieceArrangement [i], squares [i, 0].transform);

newWhitePawn.gameObject.GetComponent<Piece> ().white = true;

newWhitePawn.GetComponent<Renderer> ().material = whitePieceMat;

// SETUP 7th RANK WHITE PAWNS

GameObject newBlackPawn = Instantiate (pawnPrefab, squares [i, 6].transform);

newBlackPawn.gameObject.GetComponent<Piece> ().white = false;

newBlackPawn.GetComponent<Renderer> ().material = blackPieceMat;

// SETUP 8th RANK WHITE PIECES

GameObject newBlackPiece = Instantiate (pieceArrangement [i], squares [i, 7].transform);

newBlackPiece.gameObject.GetComponent<Piece> ().white = false;

newBlackPiece.GetComponent<Renderer> ().material = blackPieceMat;

}

}

Let’s quickly call this method in the Start() method of GameManager.cs with:

void Start() {

…

board.SetupPieces ();

}

Alright, we have classically arranged pieces decorating our chessboard now! Perhaps the player with the black pieces feels a little left out, and would like it if they could rotate the board. If they are reading this tutorial, then they are in luck! Let’s add a Rotate() method to Board.cs that looks like this:

public void Rotate() {

for (int i = 0; i < 8; i++) {

for (int j = 0; j < 8; j++) {

squares [i, j].gameObject.transform.localPosition = new Vector3 (

7 – squares [i, j].gameObject.transform.localPosition.x,

7 – squares [i, j].gameObject.transform.localPosition.y,

0);

}

}

print (“Board rotated“);

}

We can call this method in Update() whenever we press a button, but let’s do that in the GameManager:

void Update() {

if (Input.GetKeyDown (KeyCode.Space)) {

board.Rotate ();

}

}

Finally, it would fun if we could enjoy the game from starting arrangements other than the classic arrangement. Bobby Fischer, famed chess player and quasi- Cold War superstar, felt similarly when he came up with FischerRandom. FischerRandom, or Fischer960, is a chess variant that was made to eschew reliance on studying opening theories, and instead put a focus on finding moves and tactics through over-the-board analysis and intuition. FischerRandom chess is played identically to regular chess, with the exception that the positions of the starting pieces are randomized. If we can find a way to randomize the indexes of our array elements, we can just shuffle our pieceArrangement array before we place the pieces in SetupPieces(). Thankfully, another Fisher (no ‘c’ this time) partnered up with someone named Yates to create a rather efficient means of shuffling arrays, aptly named the Fisher-Yates Method, sometimes known as the Fisher-Yates / Knuth Shuffle. Let’s add this method into our Board script:

public void RandomizeArrangement(GameObject[] pieces) {

for (int i = pieces.Length–1; i > 0; i–) {

int randomIndex = Random.Range(0,i);

GameObject temp = pieces[i];

pieces[i] = pieces[randomIndex];

pieces[randomIndex] = temp;

}

}

In SetupPieces(), we can send our pieceArrangement array out to be shuffled by this method, just after we initialize it and just before we instantiate the pieces:

public void SetupPieces() {



pieceArrangement = new GameObject[8] {

…

};

if (fischer) {

RandomizeArrangement (pieceArrangement);

}



for (int i = 0; i < 8; i++) {

…

}

}

Don’t worry, let’s add in a boolean switch to the top of the script called “fischer” that controls the randomization of the pieces:

[SerializeField]

bool fischer = false;

We can now select whether or not we want to use FischerRandom or Classical arrangements in the editor before we start our game!

Thus ends the first part of this tutorial on chess and array manipulation, and I hope to see you here again next time, when we cover the Piece.cs script and virtual methods. Thanks for reading!