In some cases, you may want to constrain a Rigidbody2D to only move or rotate along some axes. You can use the bitwise OR operator to combine multiple constraints. You can use the bitwise OR operator to combine multiple constraints.
- Unity Rigidbody
- Unity Rigidbody 2d
- Rigidbody2d Unity Velocity
- Rigidbody2d Unity
- Unity Rigidbody2d Body Type
- A child GameObject with a RigidBody2D that can be moved around depending on the current location of the hook’s anchor point. This will essentially be the rope hinge / anchor point. A raycast for firing the hook and attaching to objects.
- 在unity中,经常遇到的一个问题就是物体移动的问题,Unity引擎给出了很多种解决方案,这里先给大家介绍一种——Transform.Translate函数。 在Unity中这是最基础的一种物体移动的方式之一,物体会按照你给的速度方程移动。 首先,我们先创建一个场景,加入.
Foreword
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
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 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:
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
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!
Update (14 August 2020): Looking for an article on the Interpolate property on Unity Rigidbodies? We’ve put one together recently, so have a look here.
It isn’t particularly difficult to set up physics-based movement for objects in Unity — simply add a Rigidbody component onto an object that has a Collider component, and you’ll have yourself an object that moves and collides realistically with other objects.
If you start having fast-moving objects however, you might start to see these objects tunnel through obstacles.
The illusion of motion and tunnelling
Before we delve into why tunnelling occurs, it is important to understand this: the motion that we see in animated media is an illusion. Anything that appears to move on a screen does so because the screen is showing us a rapid sequence of images. This tricks our minds into thinking that there is motion, even though what we saw was a series of static images.
Similarly, the motion of the ball that we saw above was made of many individual images, each one showing the ball at different positions. How much a ball moves from one image to the next depends on how fast it is moving. Hence, a slower-moving ball moves very little from image to image, while a fast-moving ball can move a distance equal to several times its diameter from one image to another.
Because fast-moving objects cover so much distance from one frame to the next, it may skip over thinner objects (as pictured in the image above right) without registering a collision, since there is such a large gap between its positions in any two frames. This is what causes the tunnelling phenomenon you see.
The solution: Continuous collision
To address this problem, physics engines utilise a collision algorithm that projects a shape across an object’s path of travel. This shape is then used to check for any collisions from the object’s displacement between frames.
Unity Rigidbody
This method of detecting collisions is called continuous collision detection, whereas the one that doesn’t prevent tunnelling is called discrete collision detection.
Discrete vs. Continuous collision
Although continuous collision detection is clearly the better choice, game engines generally do not enable continuous collision detection for all objects in their physics engines. This is for a simple reason: continuous collision detection is significantly more expensive, so having too many objects using it can unnecessarily use up computing power! Since only fast-moving objects suffer from the tunnelling problem, developers often enable continuous collision detection only for these objects.
In Unity, the option to turn on continuous collision detection can be found on the Rigidbody2D
and Rigidbody
components, which are used in 2D and 3D games respectively to give objects physics-based movement. Again, the idea here is to set the collision detection mode to Continuous for fast-moving objects, and leave the rest of the objects at the default value of Discrete.
Article continues after the advertisement:
Continuous Dynamic vs. Speculative
If you look at the Rigidbody
component, you will find that it has 2 additional values in Collision Detection compared to its Rigidbody2D
counterpart: Continuous Dynamic and Continuous Speculative. These additional collision detection modes are further optimisations, unique to Unity’s 3D physics system, and in place because 3D collision detection can be potentially much more expensive than its 2D counterpart.
Rigidbody
components that use the Continuous mode only use continuous collision detection on static objects (i.e. Collider
components without a Rigidbody
, which means the object does not move using Physics). This means that, in theory, tunnelling can still occur when you are colliding with any object using a Rigidbody
component.
Objects using the Continuous Dynamic mode will not have these issues, as they will use continuous collision against all objects, except against Rigidbody
objects using Discrete collision detection.
Continuous Speculative is even better. It collides against everything — static and dynamic objects in all modes of collision; is computationally faster than the other 2 modes of continuous collision; and detects certain kinds of collisions caused by spinning objects that are missed by other modes of continuous collision.
Unity Rigidbody 2d
However, because it speculates (i.e. predicts) collisions based on objects’ current motions, collisions that are detected can sometimes be inaccurate.
Making sense of everything
Rigidbody2d Unity Velocity
All of this can be pretty confusing, isn’t it? Here are some good rule of thumbs to help you decide which modes to use:
- If there are no fast-moving objects in your game, you can safely use Discrete collision detection for all your objects. An object can be considered to be fast-moving if it can travels a distance larger than its width or height within a frame.
- If you don’t care about collision accuracy in your game,Continuous Speculative will be the way to go with your fast-moving objects. The rest of the objects in the game can use Discrete.
- For dynamic objects (i.e. objects with
Rigidbody
) that don’t touch fast-moving objects at all, you can safely use Discrete collision. - For dynamic objects that are not fast-moving, use Continuous collision on them if you need them to always collide with fast-moving objects.
- For dynamic objects that are fast-moving, always use Continuous Dynamic collision.
Rigidbody2d Unity
As a summary to help make sense of all the information above, here is a table that outlines how each of the collision modes interact with one another:
Unity Rigidbody2d Body Type
Article continues after the advertisement:
Comments are closed.