Foreword
Now, in order for Unity to call our OnTriggerEnter2D method, we have to make sure both the LeftWall and RightWall have the Is Trigger checkbox selected on their Box Colliders in the Inspector pane. This means that Unity won’t treat these walls as physical walls, but rather they “trigger” something in the game (in this case, they give a.
Snake is an arcade game that was created back in the 1970's. It's been ported to almost all systems out there, even on classic old Nokia phones! Like most arcade games it's still easy and lots of fun to develop your own Snake game. In this tutorial, we will be explaining how to make a simple yet functional clone of Snake.
Requirements
Knowledge
- Unity is the ultimate game development platform. Use Unity to build high-quality 3D and 2D games, deploy them across mobile, desktop, VR/AR, consoles or the Web, and connect with loyal and enthusiastic players and customers.
- Feel free to read our easier Unity Tutorials like Unity 2D Pong Game to get used to this amazing game engine. Our Snake Tutorial will use Unity 2018.4 LTS. Newer versions such as 2019.2 should work as well, however to prevent any confusion it's always recommended to keep to the version of Unity the tutorial was written for.
This tutorial does not require any special skills except some knowledge about the Unity basics like GameObjects and Transforms. Even if you don't know those concepts yet, the Tutorial should still be doable, however we recommend you brush up on your knowledge of the mentioned subjects before attempting this tutorial.
Feel free to read our easier Unity Tutorials like Unity 2D Pong Game to get used to this amazing game engine.
Unity Trigger 2d
Unity Version
Our Snake Tutorial will use Unity 2018.4 LTS. Newer versions such as 2019.2 should work as well, however to prevent any confusion it's always recommended to keep to the version of Unity the tutorial was written for.
Protip: Never update to any version of Unity that has an 'a' or 'b' suffix. These versions are alpha and beta respectively, and more often than not things will break! Troubleshooting broken things isn't fun and causes frustration which you want to avoid when starting out in game development.
Project Setup
Let's get started! Assuming you are using a recent version of the Unity Hub, we'll boot the Hub interface and select New, then fill out the dialogue accordingly:
We will name it Snake, select any location like C:GameDev, select 2D in the Template section, and click Create:
If we select the Main Camera in the Hierarchy then we can set the Background Color to black, adjust the Size and the Position like shown in the following image:
Note: Size is pretty much the zoom factor.
Adding Borders
We will use one horizontal and one vertical white line image for our borders:
- border_vertical.png
Note: right click each link, select Save As... and save the images in the project's Assets folder.
Once we have them in our Project's Assets folder, we can select them in Unity's Project Area:
Afterwards we can change their Import Settings in the Inspector to make them appear in the right size with the right looks:
Note: Pixels Per Unit is the ratio between one pixel in the image and one unit in the world. The Snake will have a size of 1x1 pixel, which should be 1 unit in the game world. This is why we will use a Pixels Per Unit value of 1 for all our textures.
Now we can drag each border image into the scene twice and position them so they form one rectangular border:
Note: the horizontal border image is used for the top and bottom borders. The vertical border image is used for the left and right borders.
Let's rename the borders to BorderTop, BorderBottom, BorderLeft and BorderRight. We will select one after another in the Hierarchy and then either press F2 or right click it and select Rename. Here is the result:
If you have issues positioning the borders, please use the following XYZ values for each one.
- BorderTop: X 0, Y 25, Z 0
- BorderBottom: X , Y -25, Z 0
- BorderLeft: X -34.5, Y 0, Z 0
- BorderRight: X 34.5, Y 0, Z 0
- Note: Make sure Z is always set to 0. Since we're working with 2D, we do not need to use the Z axis as this will cause unwanted behaviour.
Right now the borders are just images in the game. They may look like borders, but they are not part of the physical world just yet. If we want the Snake to collide with the borders, then we have to add Colliders to them. We can do so by first selecting all the borders in the Hierarchy:
Right now the borders are just images, they aren't really borders. The snake could walk right through them because they are not part of the physics world yet. Let's take a look at the Inspector and select Add Component -> Physics 2D -> Box Collider 2D. And since we have all borders selected right now, this will add a Box Collider 2D to each of them:
And that's all there is to it. We just created the borders for our game without writing a single line of code, thanks to this powerful game engine.
Creating the Food Prefab
We don't want our snake to get hungry, so let's randomly spawn some food in the game. As usual we will start with a food image, in our case a colored pixel:
- food.png
Note: right click on the link, select Save As... and save it in the project's Assets folder.
We will use the following Import Settings for the food image:
Alright, let's drag the food image into the Scene to create a GameObject:
The Snake should receive some kind of information whenever it collides with food. This means that the food has to be part of the physics world, which can be done with a Collider.
A GameObject without a Collider is just a visual thing, its not part of the physics world. A GameObject with a Collider is part of the physics world, just like a wall. It will cause other things to collide with it, and it will trigger the OnCollisionEnter2D event. A GameObject with a Collider that has Is Trigger checked will not cause other things to collide with it, but it will still trigger the OnTriggerEnter2D event.
The Snake should get notified when it walks through food, but it's not supposed to collide with it like if the food was a wall. So let's select the food in the Hierarchy and then choose Add Component -> Physics 2D -> Box Collider 2D in the Inspector and enable Is Trigger:
Okay, now we don't want the food to be in the Scene from the beginning. Instead we want a Prefab so that we can Instantiate it whenever we need it. In order to create a Prefab, all we have to do is rename the food to FoodPrefab and then drag it from the Scene into the Project Area:
Now we can delete the FoodPrefab GameObject from the Hierarchy, because we don't want it to be in the game world just yet.
Spawning Food
Let's spawn new food at some random position every few seconds. This kind of behavior can be implemented with a Script, so let's create a SpawnFood Script. The Script should be in the Scene all the time, so we will keep it simple and add it to the Main Camera(because it will be in the Scene all the time, too). Let's select the Main Camera in the Hierarchy and then click on Add Component -> New Script, name it SpawnFood and select CSharp for the language:
Afterwards we will double click it in the Project Area to open it in the script editor (usually Visual Studio):
We won't need the Update function, so let's remove it:
Our Script needs to know where the food Prefab is. We will add a public variable of type GameObject:
The food should be spawned within the borders, not outside. So we will also need a variable for each of the borders so that our Script knows their positions:
Note: they are already of type Transform so we don't have to write borderTop.transform.position all the time. Instead we will be able to access their positions like borderTop.position.
Let's create the Spawn function that spawns one piece of food within the borders. At first we will choose the X axis position somewhere randomly between the left and right border. Then we will choose the y position randomly between the top and bottom border. Afterwards we will instantiate the food Prefab at that position:
Note: x and y are rounded via (int) to make sure that the food is always spawned at a position like (1, 2) but never at something like (1.234, 2.74565).
Now let's make sure that our Script calls the Spawn function every few seconds. We can do so by using InvokeRepeating:
Note: There are other ways of doing a repeating function, but this is the easiest way of doing so.
Here is our full script:
If we save the Script and select the Main Camera then we can see that all our public variables are now shown in the Inspector. They are still None, so let's drag the FoodPrefab from the Project Area into the Food Prefab variable and the Borders from the Hierarchy into their corresponding slots:
Note: we can drag something into a Script variable slot by literally dragging it with the mouse from the Hierarchy or Project Area into those slot things that can be seen in the above picture.
Alright, now it's time to press Play and wait a few seconds. We should be able to see some new food spawn in between the borders:
Unity Ontriggerenter2d Not Working
Creating the Snake
Let's finish the main part of the our game: the Snake. As usual we will start by drawing the snake image, which is just a 1 x 1 pixel texture:
- snake.png
Note: right click on the link, select Save As... and save it in the project's Assets folder.
We will use the following Import Settings for it:
Now we can drag the snake image into the middle of the Scene:
So far it's just the Snake's head, so let's rename it to Head to keep things clean:
The snake should be part of the physics world, which means that we need to add a Collider to it again, so let's select Add Component -> Physics 2D -> Box Collider 2D:
Note: the Collider has the size (0.7, 0.7) instead of (1, 1) so that it doesn't collide with other parts of the snake that are right next to it. We simply want to give Unity some space.
Now the snake is also supposed to move around. As a rule of thumb, everything in the physics world that is supposed to move, needs a Rigidbody. A Rigidbody takes care of things like gravity, velocity and movement forces. We can add one by selecting Add Component -> Physics 2D -> Rigidbody 2D. We will use the following settings for it:
Notes:
- The Rigidbody's Gravity Scale is 0 because we don't want the snake to fall towards the bottom of the screen all the time.
- The Is Kinematic option disables the physical behavior of the Rigidbody, so it doesn't react to gravity or collisions. We only need to know if the snake collided with something. We don't need Unity's physics to push things around in case of collisions. More info: Rigidbody2D Unity Docs.
The final snake will consist of many little elements. There will always be the Head at the front and then there will be several Tail elements like here:
The only difference between the Tail elements and the Head is that the head does all the thinking. We will add a Script to it later.
Let's drag the snake Head from the Hierarchy into the ProjectArea to create a Prefab and then name it TailPrefab so we can load it whenever the snake grows:
Note: some Unity versions will automatically rename GameObject in the Hierarchy too, so make sure that the one in the Hierarchy is still named Head.
Alright, let's select the snake Head in the Hierarchy again and click on Add Component -> New Script, name it Snake and select CSharp as the language:
We can open the Script by double clicking it in the Project Area:
Let's modify the top of the Script to include some List functionality that we will need later on:
The Snake should always move exactly one unit into whatever direction it wants to move. Now if we would allow it to move in every Update call, then it would be really fast. Instead we will only allow movement every 300 milliseconds by using Unity's InvokeRepeating function. It's like we create our own Update method that only gets called every 300 ms instead of every frame:
The Snake should always be moving into some direction, it should never stand still. So let's define a direction variable and use it to move the snake in the Move function:
Note: transform.Translate means 'add this vector to my position'.
The direction variable is of type Vector2, which means that it has an X axis and Y axis value. The following image shows different directions for a Vector2:
If we press play then we can already see the Snake move to the right:
The user should be able to change the movement direction by pressing one of the arrow keys. Now we could just check for key presses in our Move function, but that would make the game feel laggy because then we would only detect key presses every 300 ms. Instead we will use the Update function to detect key presses all the time:
Note: if you are not sure why we used Update and Move instead of just Update or just Move, feel free to put the code from Move into Update or the code from Update into Move, then you will see that the snake moves rapidly fast or that the key presses are detected only rarely.
If we press play then we can now move the Snake with the arrow keys:
The Snake's Tail
Unity 2d Ontriggerenter2d
Let's think about how the Snake's tail will work. First of all, let's assume we have a snake with one head and three tail elements:
Now as soon as the head moves to the right, the obvious thing would be to move every tail element to where its previous tail element was, like this:
This would work, but it would also be some complicated code. Let's use a neat little trick to make our lives easier. Instead of moving one tail element after another, we will simply move the last tail element into the gap like here:
Now that sounds like an easy algorithm. In every movement call, all we have to do is move the last tail element to where the head was before.
At first we will need some kind of data structure to keep track of all the tail elements that we will add later on:
Note: it's really important to add 'using System.Collections.Generic;' and 'using System.Linq;' to the top in order for lists to work.
Let's get to the code that takes the last tail element, removes it from the back and puts it into the gap mentioned above:
Note: Translate simply means 'add this vector to my position'. Afterwards we check if there is anything in the tail list, in which case we change the last tail element's position to the gap position (where the head was before). We also have to keep our list order, hence the Insert and RemoveAt calls at the end. They make sure that the last tail element is now the first element in the list, too.
And that was the only slightly complicated part of our Unity 2D Snake Tutorial. Now we are almost done.
Feeding the Snake
We will use the OnTriggerEnter2D function to receive collision information (which will happen whenever the snake walks into food or into a border).
Whenever it runs into food, we will use the exact same mechanics that we used for our movement above, except that this time instead of removing the last tail element and moving it into the gap, we will only Instantiate a new element into the gap:
It's important to understand that we will not make the Snake longer immediately after it eats something. Just like with our arrow key presses, we will wait until it actually moves. Therefore we will need a new variable that we will set to true whenever the Snake ate something:
We will also need a public variable that let's us assign the TailPrefab later on:
Note: the two variables are defined at the top of our Snake script.
Now let's get to the OnTriggerEnter2D function. This one will be straight forward again. We will find out if the Snake collided with food, in which case we set the ate variable to true and destroy the food. If it didn't collide with food, then it either collided with itself or with a border:
Note: we use coll.name.StartsWith because the food is called 'FoodPrefab(Clone)' after instantiating it. The more elegant way to find out if coll is food or not would be by using a Tag, but for the sake of simplicity we will use string comparison in this Tutorial.
Alright, let's modify our Move function so it makes the Snake longer whenever ate is true:
Note: all we did was check if ate is true, then Instantiate the tail prefab at the position v with the default rotation (Quaternion.identity). Afterwards we add it to the tail list and reset the ate flag. The rest of the code was already there.
Now we can select the snake Head in the Hierarchy, take a look at the Inspector and drag the TailPrefab from the Project Area into the Script:
If we press Play then we can now play a nice round of Snake:
Note: Since there is no game over condition for colliding with your own tail or hitting a wall, you will find you will pass through the said objects. This is not a bug, and is left to the reader to implement the said condition.
Summary
Snake is one awesome game for a Unity Tutorial. There is a lot of value in understanding how to do pixel exact games like this and how to add movement with InvokeRepeating. Yet again we saw how amazingly easy 2D games are with Unity's robust 2D features.
What's next?
Now it's up to the reader to make the game fun! There are tons of improvements to be made like a win/lose screen, a better looking snake texture, multiple levels, power-ups, increasing speed, high-scores, multiplayer and so on. Recreating classic games like Snake is just the start of your game development career.
Maybe you'd like to move onto something a little more challenging, like our Minesweeper clone or Pac-Man clone tutorials?
Download Source Code & Project Files
The Unity 2D Snake Tutorial source code & project files can be downloaded by Premium members.All Tutorials. All Source Codes & Project Files. One time Payment.
Get Premium today!