How To Do 2D Top-Down Movement – Unity C#

So you want to make a top-down game, but you’re not sure how to handle character movement. This tutorial assumes you have some familiarity with programming and Unity, but are needing a bit more help. With that in mind, I’ll explain how to do this in detail.

I used a variation of this code for my game Puzzledorf, demonstrated below.

The Code

First, attach a Rigidbody2D component to your 2D character, and turn the gravity scale down to 0. Next, make a new script and attach it to the character and add the following code inside your class (note there is an improved version at the end of this tutorial so if you’re just copy and pasting, scroll down first – although I strongly suggest you read the article to understand what it does):

Rigidbody2D body;

float horizontal;
float vertical;

public float runSpeed = 20.0f;

void Start ()
{
   body = GetComponent<Rigidbody2D>(); 
}

void Update ()
{
   horizontal = Input.GetAxisRaw("Horizontal");
   vertical = Input.GetAxisRaw("Vertical"); 
}

private void FixedUpdate()
{  
   body.velocity = new Vector2(horizontal * runSpeed, vertical * runSpeed);
}

Rigidbody

The Rigidbody2D is what handles collisions and physics for 2D characters. We get the component in Start to access the velocity later.

Taking Input

horizontal and vertical will track which keys are being pressed on the keyboard, or what direction you’re pressing the control stick in. For keyboard controls, Input.GetAxisRaw(“Horizontal”) will give us a value of either -1 or 1. Left arrow is -1, right arrow is 1. Vertical will be 1 for up and -1 for down. This should also work for the WASD keys by default without changing any code.

If you’re using a control stick, instead of being either -1 or 1, you will get decimal values between that range depending on the direction of the stick.

Update And Fixed Update

We check the input in Update because Update runs constantly, every frame, so that way we make sure that Unity registers the key was pressed.

Physics tasks, like changing velocity, should be done in Fixed Update, because it’s designed to calculate physics – but it also doesn’t run every frame, so if you put key presses in Fixed Update, sometimes it won’t register that you pressed a key.

Velocity

Velocity is both a speed and a direction (this web page explains that in detail, but I give the simple version below). It is also a property of Rigidbody2D. When we set it to something other than 0, our character will start moving in a direction.

horizontal and vertical determine our direction, being either -1 or 1. Then when we multiply it by the runSpeed to determine how fast we move in the given direction.

When you multiply horizontal with runSpeed, the characters X velocity will be will either be -20 or 20, in other words, they will be moving 20 pixels per second, which would move us right, or -20 pixels per second, which would move us left.

This is how we determine if the character is moving left or right, because negative values will move you left – or down. (You don’t necessarily move in pixels per second but that idea should help you understand).

Using Velocity

Let’s look again at FixedUpdate.

private void FixedUpdate()
{  
   body.velocity = new Vector2(horizontal * runSpeed, vertical * runSpeed);
}

Here we change the velocity value of our Rigidbody2D. A 2D velocity looks like this:

velocity( X , Y )

Whatever value we put as X, is how fast our character will travel along the X axis; how fast he will travel left or right. If the value is 1, he will travel 1 pixel per second to the right. If the value is 10, he will travel 10 pixels to the right. If the value is -10, he will travel 10 pixels to the left.

The value we put as Y will determine how fast our character will move up and down along the Y axis. 1 would move 1 pixel per second up, -10 would move 10 pixels per second down.

If we rewrote our line of code as the following:

private void FixedUpdate()
{ 
   body.velocity = new Vector2(20, -20);
}

What that would do is make our character move at 20 pixels per second to the right and 20 pixels per second down, every time physics get updated. But we don’t want our character to constantly move in one direction. We want the direction to change when we press keys. So that’s why we have horizontal and vertical to work out which direction we want to go.

Take the following maths:

horizontal = 1;
runSpeed = 20;

newSpeed = horizontal * runSpeed;

If you look at the value of horizontal and runSpeed, newSpeed is clearly 20. And if we changed horizontal to -1, newSpeed would end up being -20.

So look at our code again:

private void FixedUpdate()
{ 
   body.velocity = new Vector2(horizontal * runSpeed, vertical * runSpeed);
}

body.velocity is a Vector2 because it holds an X and a Y value, the speed and direction we want to move on the X and Y axis. To change it, we have to declare that we want to make a new Vector2, and inside the ( ) parentheses, or brackets, we put the values we want for our X and Y speed, in other words:

private void FixedUpdate()
{ 
   body.velocity = new Vector2( X , Y );
}

A Vector2 quite simply is just a variable that stores 2 numbers, separated by a comma. This is why it is useful to use them to describe 2D velocity, because our velocity has both an X and a Y value.

So if horizontal is 1, we’ll move right, or -1, we’ll move left. Then we multiply it by runSpeed to work out how fast we are moving to the right.

We do the same with vertical – vertical determines up or down, then we multiply it by runSpeed to work out how fast we should move up or down.

You can also do 3D velocity. It looks like this:

velocity ( X , Y, Z )

It works the same way, except that we can now move in 3 dimensions, with the Z direction and speed worked out in the third value.

Too Fast Diagonally

If you haven’t already, test your code. You should see that your character moves, but feels like he moves too fast diagonally if you are using a keyboard. There’s a nice, simple way to fix this.

Create a new variable called moveLimiter at the top of your script with the other variables.

Rigidbody2D body;

float horizontal;
float vertical;
float moveLimiter = 0.7f;

public float runSpeed = 20.0f;

The part in blue is the new code.

Forgetting all of the mathematical reasons why, for diagonal movement to feel right, you want to move 70% slower than we currently do when we move diagonally. That’s what moveLimiter is for, because mutliplying a number by 0.7 gives us 70% of that number.

Let’s add some new code to FixedUpdate – add the parts in blue:

private void FixedUpdate()
{
   if (horizontal != 0 && vertical != 0) // Check for diagonal movement
   {
      // limit movement speed diagonally, so you move at 70% speed
      horizontal *= moveLimiter;
      vertical *= moveLimiter;
   }

   body.velocity = new Vector2(horizontal * runSpeed, vertical * runSpeed);
}

So we added a new if statement. Our if statement checks if you’re moving diagonally – if horizontal and vertical are both not equal to 0, you’re moving diagonally.

moveLimiter has the value of 0.7. So when we multiply any number by 0.7, that will give us 70% of that number.

Ironically in this case, multiplying horizontal by 0.7 will make horizontal equal to 0.7 or -0.7, but if we just said:

horizontal = 0.7f;

It would always be positive, so you could only ever move right. But if horizontal is -1, and we multiply that by 0.7, we’ll end up with -0.7, so we can move at 70% speed to the left. This is why we use a combination of horizontal and moveLimiter.

Then, when we modify our velocity, the new velocity will be 70% slower because we have modified the values of horizontal and vertical to be 70% of their value, so we will be multiplying our runSpeed value by either 0.7 or -0.7, again, setting out speed to 70% of runSpeed.

Keyboard Or Controller

This scheme is intended for keyboard. For controllers, you don’t need to limit the movement speed because when you press diagonal on a controller it does that automatically.

So if you plan to make your game work with both keyboards and controllers, check which input is being used and modify your code accordingly – don’t check for diagonal movement with controllers.

The final code is below.

Final Code

As promised, here is the final version of the code:

Rigidbody2D body;

float horizontal;
float vertical;
float moveLimiter = 0.7f;

public float runSpeed = 20.0f;

void Start ()
{
   body = GetComponent<Rigidbody2D>();
}

void Update()
{
   // Gives a value between -1 and 1
   horizontal = Input.GetAxisRaw("Horizontal"); // -1 is left
   vertical = Input.GetAxisRaw("Vertical"); // -1 is down
}

void FixedUpdate()
{
   if (horizontal != 0 && vertical != 0) // Check for diagonal movement
   {
      // limit movement speed diagonally, so you move at 70% speed
      horizontal *= moveLimiter;
      vertical *= moveLimiter;
   } 

   body.velocity = new Vector2(horizontal * runSpeed, vertical * runSpeed);
}

75 thoughts on “How To Do 2D Top-Down Movement – Unity C#

Add yours

  1. This is by far the best tutorial I’ve ever encountered. You give a part of the code, then explain the reasoning behind it and how it works. This way of doing it works amazing because it gives me the knowledge to and confidence to do code like this again on my own in another game without having to search up another tutorial for the same subject I tried learning about 2 days before. Thank you so much and from now on I will always be going to this website for tutorials before I go anywhere else. Congrats you mastered the art of explaining code.

    Like

    1. Thank you so much! That means a lot. Inspires me to write more tutorials 😀 But yes, from my time with teaching, and my own time learning, that is how I found learning is best. People need context of why they are doing things to really have understanding, not just copy and pasting code snippets. I’m very glad you appreciate it!

      Like

  2. Well, this code is amazing and it set me on the correct path to make my own. All I really needed from this was the info on the velocity component of the rigidbody, and then I made a really good controller.

    Like

  3. insted of hardcode a movelimiter you can just take the input as a vector 2 and normalize it.
    So it would be rb.velocity = input.normalized * movespeed * Time.deltaTime.

    Like

    1. Your code is very nicely written and you are correct that is a very clean way to do it. However, this tutorial is aimed at absolute programming beginners, and I want to make sure all of the concepts are understood clearly. Normalizing variables is a fairly difficult maths concept for some people to grasp, and I feel I would need to dedicate a significant portion of the article into explaining it, and I just didn’t want to add that much complexity, or extra length, to this article.

      I wanted to write simple code that anyone can understand. And yes while your code would work, the way I have written it helps teach the intricacies of what’s going on behind the scenes and the potential issues that can arise, ie, moving too fast when moving diagonally if you don’t cap it. So there’s also a lesson in the code itself, which, as people get better at programming, they can change to something similar to what you’ve written.

      Thanks for sharing your input.

      Like

  4. Nice and straight forward.
    I am wondering one thing tho. How come you didnt do the velocity like this:
    body.velocity = new Vector2(horizontal, vertical).normalized * runSpeed;

    This way you can completely avoid the “diagonal check” since it will always be normalized. I feel like the 70% fix is kind of a quickfix 🙂

    Liked by 1 person

    1. Your code is very nicely written and you are correct that is a very clean way to do it. However, this tutorial is aimed at absolute programming beginners, and I want to make sure all of the concepts are understood clearly. Normalizing variables is a fairly difficult maths concept for some people to grasp, and I feel I would need to dedicate a significant portion of the article into explaining it, and I just didn’t want to add that much complexity, or extra length, to this article.

      I wanted to write simple code that anyone can understand. And yes while your code would work, the way I have written it helps teach the intricacies of what’s going on behind the scenes and the potential issues that can arise, ie, moving too fast when moving diagonally if you don’t cap it. So there’s also a lesson in the code itself, which, as people get better at programming, they can change to something similar to what you’ve written.

      Thanks for sharing your input.

      Like

  5. Hi! Thanks for the tutorial I liked the format and worked like a charm.

    I’m just curious how you’ve gone about the size of the game area and the size of the 2D objects in the game area. I’m trying to size my game up right now. How did you select the resolution and size of the sprites etc?

    thanks!

    Like

    1. You ask a good question. Short answer: I use other games as a reference, and work it around HD. So I start at full HD screen size, then work backwards, to either half HD or 1/4 HD, and see how the sprites look at that size – in terms of thinking how many pixels to fit on the screen at once. You may even want to go 1/8 HD, I dunno.

      I think I’m gonna write an article about it that will explain better, so keep an eye out for it. It’s something I have to work out again every time I start a new project.

      Like

  6. I’ve read through it all, gave it a go, it said compiler error, redid it, still didn’t work, so I tried to copy and paste your finished script in and it still has the exact same compiler error, am I doing something wrong?? I’ve not had a single script work from anywhere since I started trying to give unity a go

    Like

    1. Just going by what you said, it sounds like your problem may be less understanding my script, and perhaps more how to use Unity.

      In which case, I would suggest starting here as an absolute beginner: https://learn.unity.com/

      And if you get really stuck, I suggest starting by asking in the ‘General’ section of the ‘Unity Forum’, they are usually very helpful and can help with a wide range of questions: https://forum.unity.com/

      Like

    2. Just to add to my previous message, it’s hard for me to say exactly why your script isn’t working with the information I’ve got, but since you say no scripts have worked for you, I am assuming that you may actually not have set up Unity and your coding software correctly (I use Visual Studio Community Edition, but you also have to link them), or there are some more fundamental issues about setting the script up you might need to understand, which is where the ‘Unity Learn’ tutorials are probably a better starting place for you.

      Like

  7. Thank you so much! Just started my first project and this was incredibly helpful! I appreciate how you explained the journey from the simple to the more elegant and functional version.

    Like

    1. Yes you could, but unless you plan to use the arrow keys specifically for something else, I’m not sure why you would limit the player in that way?

      Regardless, there are many ways you could do it. The simplest way to make it work with this code would be to change this line of code:

      body.velocity = new Vector2(horizontal * runSpeed, vertical * runSpeed);

      Use unity’s Input method to check for Key presses, and check if W, A, S, or D are being pressed – if they are, then change the velocity. If not, ignore it.

      https://docs.unity3d.com/ScriptReference/KeyCode.html

      Like

  8. when i try to click play it says that all the compiler errors have to be fixed before i play and the error is “namespace cannot directly contain members such as fields or methods” and i do not know what that means and how to fix it do you think you might know how to help
    thanks

    Like

    1. Hard to know without seeing the code. Generally when you get stuck, there’s a couple of things you can do:

      Copy and paste the error message into google and see what comes up: “namespace cannot directly contain members such as fields or methods”

      There’s quite a few results that might help, if you understand what they’re talking about. Sometimes, though, the results are too specific to someone elses code, especially if you are a beginner. In that case, the next best thing is to go to “Unity Answers” if it is Unity related, post your error message and a copy of part of your code. The people there are normally quite helpful and get back pretty soon.

      Like

  9. im just starting to learn how to code and this really helped but i cant figure out a way to make it move slower is there anything i can do about it?

    Like

    1. The simplest way is to animate based on which key was pressed. Otherwise you would have to likely check the speed and direction of the character. If X is positive, you’re moving right, if it’s negative, you’re moving left. Same with up and down and Y negative is down, positive is up (or the other way around). And then when you know which direction you’re moving, animate that way.

      So you’ll need some separate if statements to check for key presses or direction. And do the animations in Update, since they are not physics based

      Like

  10. I keep getting an error message “NullReferenceException: Object reference not set to an instance of an object”

    Like

      1. No worries, glad you solved it. What was the problem? Sounds like you forget to set a variable and left it blank / null, like you forget to reference a Game Object somewhere. Often I find if I get stuck, walking away for a bit and coming back, it’s easier to find the solution

        Like

  11. Hey! just noticed in the code that you declared the body as “RigidBody2D” than you called it in the start function as “RigidBody” which caused a problem in the code when I typed it in? is this a typo or does it automatically convert?

    Like

    1. A good question, but no. When applying forces with RigidBodies in Unity, it automatically calculates delta time for you. My understanding is when manipulating the velocity of the rigidbody directly, the same applies.

      However, if you were creating your own physics system from scratch, yes you would have to factor in delta time manually, to make sure that the physics behaved the same on every PC

      Like

    1. Not sure which part of the code you’re referring to?

      In any case, the article was long enough and I don’t want to introduce too many new concepts, otherwise it’s something else I would have to explain. Better to get people understanding how to write code, and the basic maths and principles behind it, and then worry about optimizations later.

      Like

  12. Love it!

    But is there anyway to move like that but with blocky movement? Like, no slowing down or speeding up, the player just… moved?

    Like

    1. There are lots of different ways to do movement. Are you asking about grid based movement, where you move one grid space at a time, or like the old Pokemon games on Gameboy, or simply movement without acceleration?

      Like

    1. If you double click the error, it should highlight the section of code. It would be helpful if you could attach that.

      It sounds like you have incorrectly declared the ‘Rigidbody2D’ variable, put some code in the wrong place or mistyped something. Read my code sample again very closely and make sure.

      A useful tip when you get an error is to copy and paste the error message into google – it’s quite common for someone else to have had the error before. I did that and got this, which suggests a mistype: https://answers.unity.com/questions/766896/unexpected-symbol-rigidbody2d.html

      It’s also important to note all of that code as to go inside the scripts “class”. If you’re not sure what that is, and you are a complete beginner programmer, this might not be the best place for you to start. I suggest going through Unity’s official tutorials, starting with the rolling ball tutorial. I will have a beginner programmer course in the coming future, however this is intended for people who are somewhat familiar with programming and some small familiarity with Unity, but have not made a top down game before.

      Like

    1. In future when you get an error, you should try copy and pasting the error message into google and see if someone else has had it before – they usually have. If not, someone on Unity answers will often be able to get back to you fairly quickly if you can provide them with a sample of code. Without a sample of code it is hard to know where you have gone wrong. It sounds like, however, you have mistyped something and not too familiar with programming, in which case you should be looking at the Unity ‘Learn’ tutorials to get a bit of an idea how to learn Unity, although that won’t properly teach you programming. Look at Udemy also for a course on programming, or find a highly rated Youtube tutorial series.

      Unity official tutorials: https://unity3d.com/learn

      Eventually I will have beginner programming courses but that’s not what this article is.

      Like

    1. Did you try using WASD? If you used the method I mentioned, it should work already. Unity’s in-built Input.GetRawAxis / “horizontal” / “vertical” accounts for both arrows and WASD.

      Like

Leave a comment

Blog at WordPress.com.

Up ↑