---
title: "Add a turn system"
content_type: tutorial
url: "https://learn.unity.com/tutorial/add-a-turn-system?language=en"
language: en
difficulty: beginner
duration_minutes: 20
unity_version:
  - "6.0"
  - "6.3"
supported_unity_versions:
  - name: "6.0"
    url: "https://learn.unity.com/tutorial/add-a-turn-system.md?language=en&version=6.0"
  - name: "6.3"
    url: "https://learn.unity.com/tutorial/add-a-turn-system.md?language=en&version=6.3"
last_updated: "2026-09-01T00:43:52.000Z"
---

# Add a turn system

Build a TurnManager script to track player turns and update your game initialization to support turn-based play.

This game is a turn based game, which means it needs a way to manage those turns.

By the end of this tutorial you'll have done the following:

- Created a TurnManager script that will track every time the player character moves and count that as one turn.
- Reworked the initialization code for the game to accommodate the new system.

## Overview

Now that you have a player character that you can move across the game board, the next step is to add the turn system to this turn based game. In this tutorial, you’ll add and code the system that will handle counting turns down each time the player character moves and that will notify your game that a turn has happened so you can create other game mechanics around this functionality.

## Turn manager

If you go back to your list of tasks, the next step after having a moving player is implementing a turn system when the player character makes a move.

To create a turn system, follow these instructions:

**1.**  Right-click the **Scripts** folder in the **Project** window and select  **Create** > **Scripting** > **Empty C# Script**. Name this script “TurnManager”

In this instance, you don’t need to use a MonoBehaviour script because, unlike the other scripts you’ve written so far, this one  is a pure data class that just handles data and is not linked to a GameObject, so it doesn’t need to inherit from MonoBehaviour.

## TurnManager

```csharp
using UnityEngine;

public class TurnManager
{
  
}
```

Because this is not a MonoBehaviour script and doesn’t exist in the scene, there is no **Start** or **Update** method, so you'll need to create/initialize it manually, like you would when you initialize a Vector3 or your CellData.

There are several ways you might do this:

- Inside your **BoardManager**’s **Start** method.
- Inside your **PlayerController**’s **Spawn** method.
- Inside a new system that takes care of initializing everything needed for a level at start.

That last solution seems to be the cleanest, as there will probably be other things you need to initialize when the game gets more complex.

Think about the startup of your game. Right now you rely only on the **Start** method of the **BoardManager** to initialize everything, but later on when you want to handle multiple levels, you might need to call the initialization code again.

Instead of this, let’s create a **GameManager** GameObject that will be the entry point for everything the game needs when starting. It will consist of a script whose **Start** method will do the following:

- Initialize the **TurnManager.**
- Trigger the **BoardGeneration**.
- Spawn the **Player**.

So you have some new code to write and some refactoring to do first!

**1.**  In the **Main** scene, create a new empty GameObject and name it “GameManager”.

**2.**  Right-click the **Scripts** folder in the **Project** window and select **Create** > **Scripting** > **MonoBehaviour Script**. Name this script “GameManager”.

**3.**  Add the **GameManager** script as a component to the **GameManager** GameObject.

**4.**  Inside the script create two public references:

- One for the **Board** of type **BoardManager**.
- One for the **Player** of type **PlayerController**.

**5.**  Create a private variable of type **TurnManager**.

**6.**  Inside **GameManager** script’s **Start** method do the following:

- Initialize the TurnManager (it’s just a normal class, so you can use “new”)
- Initialize the level (you’ll have to rename the **Start** method in the **BoardManager** to **Init** and make it **public** to be able to call it from this **Start** method)
- Spawn the player at (1,1), so you'll have to remove the call to spawn the player from the **BoardManager Init** method.

You should be able to do it all by yourself, and here is what the final scripts look like:

## BoardManager

```csharp
public class BoardManager : MonoBehaviour
{
   public class CellData
   {
       public bool Passable;
   }

   private CellData[,] m_BoardData;
   private Tilemap m_Tilemap;
   private Grid m_Grid;
  
   public int Width;
   public int Height;
   public Tile[] GroundTiles;
   public Tile[] WallTiles;
  
   public void Init()
   {
       m_Tilemap = GetComponentInChildren<Tilemap>();
       m_Grid = GetComponentInChildren<Grid>();
      
       m_BoardData = new CellData[Width, Height];

       for (int y = 0; y < Height; ++y)
       {
           for(int x = 0; x < Width; ++x)
           {
               Tile tile;
               m_BoardData[x, y] = new CellData();

               if(x == 0 || y == 0 || x == Width - 1 || y == Height - 1)
               {
                   tile = WallTiles[Random.Range(0, WallTiles.Length)];
                   m_BoardData[x, y].Passable = false;
               }
               else
               {
                   tile = GroundTiles[Random.Range(0, GroundTiles.Length)];
                   m_BoardData[x, y].Passable = true;
               }

               m_Tilemap.SetTile(new Vector3Int(x, y, 0), tile);
           }
       }
   }

   public Vector3 CellToWorld(Vector2Int cellIndex)
   {
       return m_Grid.GetCellCenterWorld((Vector3Int)cellIndex);
   }

   public CellData GetCellData(Vector2Int cellIndex)
   {
       if (cellIndex.x < 0 || cellIndex.x >= Width
           || cellIndex.y < 0 || cellIndex.y >= Height)
       {
           return null;
       }

       return m_BoardData[cellIndex.x, cellIndex.y];
   }
}
```

## GameManager

```csharp
public class GameManager : MonoBehaviour
{
   public BoardManager BoardManager;
   public PlayerController PlayerController;

   private TurnManager m_TurnManager;
  
   // Start is called once before the first execution of Update after the MonoBehaviour is created
   void Start()
   {
       m_TurnManager = new TurnManager();
      
       BoardManager.Init();
       PlayerController.Spawn(BoardManager, new Vector2Int(1,1));
   }
}
```

Don’t forget to assign the **BoardManager** and **PlayerCharacter** references in the **Inspector** window of the **GameManager**, and enter Play mode to check you can still move the player character properly across the board.

**Important**: If the game doesn’t work properly in Play mode, carefully read the errors that appear in the console and try to understand them:

- The **Null reference** error means something is not assigned properly, so you may have forgotten to assign the reference in the **Inspector** window of your **GameManager** GameObject.
- **Expected** or **does not exist** errors are usually typo errors: you have made a mistake in a variable name or are missing a bracket or semicolon somewhere in your code.

## Ticking turn

Now that you’ve reworked your initialization phase, let’s finally make the turn system tick.

To do this, you’ll need a private member variable that saves the current turn number, initialized at 1 in the constructor, and a **Tick** method that increases the count by 1 and writes the current turn count using the **Debug.Log** method, like the following:

```csharp
public class TurnManager
{
   private int m_TurnCount;

   public TurnManager()
   {
       m_TurnCount = 1;
   }

   public void Tick()
   {
       m_TurnCount += 1;
       Debug.Log("Current turn count : " + m_TurnCount);
   }
}
```

Then you’ll need to call **Tick** every time the player moves. But how to do this? The only place that has a reference to the **TurnManager** is inside the **GameManager**.

You could pass the **GameManager** to our **Player**, like you did for the **BoardManager**, but as a lot of other things in your game may require access to the **GameManager**, it’s a better idea to explore another method: the singleton pattern.

Using a singleton pattern means that there can only be one instance of a given class in your game/application and you can access it through a static member accessible from anywhere.

Like every decision you make in code architecture, singleton patterns have their drawbacks: they’re hard to use with multithreading and can make unit testing harder. Those are advanced use cases that are of no concern to you in this project, but because coding is about building good habits, , it’s often best to use them sparingly. However, in certain cases, they’re a powerful tool that makes code faster to write and simpler to understand. In the case of the **TurnManager** script, the drawbacks of a singleton pattern are worth it in exchange for not having to pass the **GameManager** all around your application code to every object that needs it.

This is our new singleton **GameManager**:

```csharp
public class GameManager : MonoBehaviour
{
   public static GameManager Instance { get; private set; }
  
   public BoardManager BoardManager;
   public PlayerController PlayerController;

   private TurnManager m_TurnManager;

   private void Awake()
   {
       if (Instance != null)
       {
           Destroy(gameObject);
           return;
       }
      
       Instance = this;
   }
  
   void Start()
   {
       m_TurnManager = new TurnManager();
      
       BoardManager.Init();
       PlayerController.Spawn(BoardManager, new Vector2Int(1,1));
   }
}
```

There are a couple of new things in this code, so let’s check them out:

- There is a static **GameManager** property member called **Instance**. This will store a reference to your GameManager. You can see that there are two keywords: get and set. **set** is preceded by the **private** keyword. What this means is that accessing that member variable (get) is available for all parts of the code, but writing the value of that member variable (set) can only be done from inside the class.
- A new method called **Awake** was added. This is called before **Start** when the GameObject is created. In this method, the following happens:
  - The code tests if **Instance** is null. If it’s not, that means there’s already a **GameManager**. This shouldn’t happen because a singleton pattern needs to be unique, so we destroy this GameManager and exit the method (as this is a void type method, “return;” doesn’t return anything).
  - If there is no **GameManager** stored yet (**Instance** is null) we store that **GameManager** in **Instance**.

Now you can use the following instance anywhere in your code to access the **GameManager**:

```csharp
GameManager.Instance
```

Now if you change the **TurnManager** variable of the **GameManager** to be **public**, other scripts will also be able to access it through **GameManager.Instance**. Note that in the code example below, we use the same trick used for the **Instance** variable, and define the **TurnManager** **set** property as **private** so only the **GameManager** script can change this variable, but leave the **get** property **public** so other scripts can access the **TurnManager**.

```csharp
public class GameManager : MonoBehaviour
{
   public static GameManager Instance { get; private set; }
  
   public BoardManager BoardManager;
   public PlayerController PlayerController;

   public TurnManager TurnManager { get; private set;}

   private void Awake()
   {
       if (Instance != null)
       {
           Destroy(gameObject);
           return;
       }
      
       Instance = this;
   }
  
   void Start()
   {
       TurnManager = new TurnManager();
      
       BoardManager.Init();
       PlayerController.Spawn(BoardManager, new Vector2Int(1,1));
   }
}
```

Then in the **Update** method of your **PlayerController**, you can “tick” the **TurnManager** just before calling **MoveTo**:

```csharp
if(hasMoved)
{
   //check if the new position is passable, then move there if it is.
   BoardManager.CellData cellData = m_Board.GetCellData(newCellTarget);

   if(cellData != null && cellData.Passable)
   {
       GameManager.Instance.TurnManager.Tick();
       MoveTo(newCellTarget);
   }
}
```

Now you can try to play the game and check that a new turn count is printed in the console every time you move.

![](https://connect-mediagw.unity.com/h1/20241010/learn/images/15cc0e43-50fc-44f3-a2bd-4f1d2fc72805_console-output.png)

**Tip**: Remember to check in all those changes in the Unity Version Control system!

## Next steps

Your game now has a turn system and is becoming a lot more game-like! In the next tutorial, you’ll take this even further and add a food resource to your game.
