Need help with grid based movement in C#

Hey guys, I’m fairly new to Unity. I’ve been playing around with the 2D editor and scripting with C#. I’ve run into a bit of a snag with my current project. I’m trying to create a top-down movement style similar to Pokemon or Final Fantasy.

Here’s what I have so far:

{
    public float speed = 2.5f;
    bool canMove;
    Vector3 pos;
    Transform tr;

    void Start ()
    {
        bool canMove = true;

    }

    void Movement ()
    {
        if (Input.GetKey (KeyCode.W))
        {
            transform.Translate(Vector3.up * speed * Time.deltaTime);
        }
        if (Input.GetKey (KeyCode.A))
        {
            transform.Translate(Vector3.left * speed * Time.deltaTime);
        }
        if (Input.GetKey (KeyCode.S))
        {
            transform.Translate(Vector3.down * speed * Time.deltaTime);
        }
        if (Input.GetKey (KeyCode.D))
        {
            transform.Translate(Vector3.right * speed * Time.deltaTime);
        }

    }
   
    void FixedUpdate ()
    {
        if (canMove = true)
        {
            Movement ();

        }

    }
}

I should also mention that I found a JavaScript code that worked exactly how i wanted it to somewhere amongst the interwebs, but I’m completely illiterate with JavaScript so I’ve been trying to recreate it in C#.

You’ll need to let us know what you want and what results you currently get… the script you posted looks fine to me.

Sorry.
What I’m going for is a tile by tile kind of movement where the player is bound to a kind of grid and stops centered on a tile and can only move horizontally and vertically. What I’m currently getting is more free, my player can stop and start in between tiles and move diagonally if two keys are held down.
I hope that cleared things up a bit.

This is the JavaScript code I found that does basically what I am looking for.

#pragma strict
private var speed = 2.0;
private var pos : Vector3;
private var tr : Transform;
function Start() {
     pos = transform.position;
     tr = transform;
}
function Update()
{
     if (Input.GetKey(KeyCode.D) && tr.position == pos)
     {
         pos += Vector3.right;
     }
     else if (Input.GetKey(KeyCode.A) && tr.position == pos)
     {
         pos += Vector3.left;
     }
     else if (Input.GetKey(KeyCode.W) && tr.position == pos)
     {
         pos += Vector3.up;
     }
     else if (Input.GetKey(KeyCode.S) && tr.position == pos)
     {
         pos += Vector3.down;
     }
  
     transform.position = Vector3.MoveTowards(transform.position, pos, Time.deltaTime * speed);
}

[/code]

I don’t see the difference in result between the two code samples you’ve posted. The second one isn’t a grid-style movement, either.

I didn’t actually try/test this code, but I translated the JavaScript to C# in Notepad real quick, so might have some minor syntax errors but I think should work for you. I think this will give you grid based movement, as long as your grid is 1x1 game units in size (I think this will move 1 game unit at a time). The MoveTowards part at the bottom does the actual movement, and it moves smoothly over time, so it will not “jump to each grid location” but I think that is what you want. The input is disabled until the character gets to each new spot on the grid because of the tr.position == pos in each if() statement.

float speed = 2.0f;
Vector3 pos;
Transform tr;
 
void Start() {
    pos = transform.position;
    tr = transform;
}
 
function Update() {
 
     if (Input.GetKey(KeyCode.D) && tr.position == pos)
     {
         pos += Vector3.right;
     }
     else if (Input.GetKey(KeyCode.A) && tr.position == pos)
     {
         pos += Vector3.left;
     }
     else if (Input.GetKey(KeyCode.W) && tr.position == pos)
     {
         pos += Vector3.up;
     }
     else if (Input.GetKey(KeyCode.S) && tr.position == pos)
     {
         pos += Vector3.down;
     }
 
     transform.position = Vector3.MoveTowards(transform.position, pos, Time.deltaTime * speed);
}
2 Likes

If your grid isn’t 1x1 game units, then you would need to change the lines like pos += Vector3.right to increase by a different amount. Maybe: pos.x += 2; …if your grid is 2 units along the X axis.

If you want the character to jump to each location, instead of smoothly move, just change the MoveTowards() line to:

transform.position = pos;

If you did that you would probably want to use Input.GetKeyDown() instead of Input.GetKey() so that it only moves once for each key press, instead of once per frame while key is held down.

1 Like

Hi, CalvBore!

I had some question earlier. Code below can resove your problem. Main idea is to move the player to destination point a little bit within each frame while it is not in the end of path.

!!! This code doesn’t solve collisions. If you solve collision, please write me.

if (!isMoving) {
            if (Input.GetAxis ("Vertical") > 0) {
                destinationPosition = tr.position + Vector3.forward;
            }
            if (Input.GetAxis ("Vertical") < 0) {
                destinationPosition = tr.position + Vector3.back;
            }
            if (Input.GetAxis ("Horizontal") > 0) {
                destinationPosition = tr.position + Vector3.right;
            }
            if (Input.GetAxis ("Horizontal") < 0) {
                destinationPosition = tr.position + Vector3.left;
            }
            if (Input.anyKey) {
                startTime = Time.time;
                isMoving = true;
            }
        } else {
            distanceLength = Vector3.Distance (tr.position, destinationPosition);
            if (distanceLength > 0){
                float distCovered = (Time.time - startTime) * speed;
                float fractDistance = distCovered / distanceLength;
                tr.position = Vector3.Lerp (tr.position, destinationPosition, fractDistance);
            } else {
                isMoving = false;
            }
        }

I worked on something like this quite a while back when I was first learning unity.

I remember struggling with letting there be continuous movement but always stopping on the grid space and things like that.

I’m on mobile, so I won’t be posting code for you right now, but I think some things to consider would be a little helpful at least.

I found it helpful then, and still do, to keep track of when I’m moving not only as a bool, but calculate my velocity so I know which direction I’m moving too.
There’s a few sources you can find to do this, it’s basically just setting a current position and a last position and subtracting those two multiplied by the time of the frame.

Essentially this could take place of your bool, so you know when and how your moving.

Then there’s an issue of locking your movement to some parameters without making it require a second key press to keep moving.

Basically, you have the information you need now to do that, you know when you’re moving, and in what direction, and you know if you press a key.

So if you press the right direction key and hold it down, you move right. If you’re moving right, you can’t move any other direction until you stop moving. And when you release the right direction key, you need to determine the next grid space you can stop moving at.

I hope all this is helping at least a little, but basically you check if a button is pressed. Here your if statment would likely check for input.get key instead of getkeydown so you don’t have to spam keys to move. At the same time, you’d likely check if you are already moving, and if you aren’t, or are already moving in the correct direction, continue.

Then you would apply or continue applying movement in the keypress direction. I remember that I centered my grid on the grid of the scene view, so every 0.5,0.5 I was in the center of a grid space, but it doesn’t really matter where your grid center is, as long as you’re consistent. But basically when you release the key, you need to get the next grid point, and keep moving the player until it reaches that point, and only allow input from the key for that direction until it finishes.

As for collisions, you can raycast the direction you pressed and bar movement before it even happens. If you’re already moving, raycast every time you reach the next grid space in the direction you’re moving, and stop movement if you get a hit.

Goodluck on your project!

1 Like

Hi guys! Here is what I did to fix the collision problem I was having with shopguy’s code…

void OnCollisionEnter2D()
{
float x = transform.position.x;
float y = transform.position.y;
float z = transform.position.z;
x = (int)x;
y = (int)y;
z = (int)z;
Vector3 away = new Vector3(x, y, z);
pos = away;
}

This should make your game object look like it bumped into the other 2D collider object and kinda bounces off. The file attached is the code I used for my program. I changed it to 0.5 increments instead of the usual 1.

Ps. function Update() should be void Update() in shopguy’s code.

2565866–178806–Movement.cs (1.63 KB)

1 Like

Hey, I’m having a problem with SpoonGuy/SashimiGirl’s code; I can collide with walls to my right just fine, and I can collide with walls down just fine, but, I can’t seem to collide left or up without getting my player stuck in the collision box. Help is greatly appreciated, here is my code:

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.Collections;

public class Movementy : MonoBehaviour
{

    public float speed = 2.0f;
    Vector3 pos;
    Vector3 old;
    Transform tr;

    void Start()
    {
        pos = transform.position;
        tr = transform;
    }

    void FixedUpdate()
    {
        if (Input.GetKey(KeyCode.UpArrow) && tr.position == pos)
        {
            pos += (Vector3.up) / 1;
        }
        else if (Input.GetKey(KeyCode.RightArrow) && tr.position == pos)
        {
            pos += (Vector3.right) / 1;
        }
        else if (Input.GetKey(KeyCode.DownArrow) && tr.position == pos)
        {
            pos += (Vector3.down) / 1;
        }
        else if (Input.GetKey(KeyCode.LeftArrow) && tr.position == pos)
        {
            pos += (Vector3.left) / 1;
        }

        transform.position = Vector3.MoveTowards(transform.position, pos, Time.deltaTime * speed);
    }

    void OnCollisionEnter2D()
    {
        float x = transform.position.x;
        float y = transform.position.y;
        float z = transform.position.z;

        if ((int)(x + 0.5) > (int)x && x > 0)
            x = (int)x + 0.5f;
        else if ((int)(x - 0.5) < (int)x && x < 0)
            x = (int)x - 0.5f;
        else
            x = (int)x;

        if ((int)(y + 0.5) > (int)y && y > 0)
            y = (int)y + 0.5f;
        else if ((int)(y - 0.5) < (int)y && y < 0)
            y = (int)y - 0.5f;
        else
            y = (int)y;

        z = 0f;

        Vector3 away = new Vector3(x, y, z);
        pos = away;

    }
}

Where the object is moved? this code makes player standstill

Hey Guys Regarding the collision problem. I think Sashimigirl’s code probably works well enough, but I personally(as a rookie) found it very complicated and convoluted to understand and cater to my needs. I found a simple solution for anyone that just wants to fix the basic collision stuck problem.

I found it easier to simply create a “Direction enum” and use it as a way for collision to check which way it needs to bounce the player back. Basically, depending on what direction you are facing is which direction the walls will bounce you back to your previous position when you collide.

here is my code just in case you are wondering (also, my grid is based off 16 by 16 sized sprites so thats why my bounce back value is “0.16” so you may need to change this around)

    private Vector3 pos;
    private Transform trans;


    Direction plyDirection;

    // Start is called before the first frame update
    void Start()
    {
      
        plyDirection = Direction.South;

        pos = gameObject.transform.position;
        trans = gameObject.transform;
    }
    // Update is called once per frame
    void Update()
    {

        Movement();
 
    }



    void Movement()
    {
        if (Input.GetKey(KeyCode.W) && trans.position == pos)
        {
            plyDirection = Direction.North;
            pos += new Vector3(0,0.16f,0);
        }
        if (Input.GetKey(KeyCode.A) && trans.position == pos)
        {
            plyDirection = Direction.East;
            pos += new Vector3(-0.16f, 0, 0);
        }
        if (Input.GetKey(KeyCode.D) && trans.position == pos)
        {
            plyDirection = Direction.West;
            pos += new Vector3(0.16f, 0, 0);
        }
        if (Input.GetKey(KeyCode.S) && trans.position == pos)
        {
            plyDirection = Direction.South;
            pos += new Vector3(0, -0.16f, 0);
        }
  
            transform.position = Vector3.MoveTowards(gameObject.transform.position, pos, Time.deltaTime * speed);
   


    }

    void OnCollisionEnter2D()
    {
        if (plyDirection == Direction.South)
        {
            pos += new Vector3(0, 0.16f, 0);
        }
        if (plyDirection == Direction.North)
        {
            pos += new Vector3(0, -0.16f, 0);
        }
        if (plyDirection == Direction.East)
        {
            pos += new Vector3(0.16f, 0, 0);
        }
        if (plyDirection == Direction.West)
        {
            pos += new Vector3(-0.16f, 0, 0);
        }
    }

Hey guys i’m ultra new to coding so forgive me if there is anything silly in this post but i am using Rixis’s code above and it works perfectly for me but i’m trying to add in a rotation of my player as well. specifically what i want is if my player is facing “North” and “East” input is detected i want the player to rotate that direction first instead of moving straight away. would i possibly be able to do this in an “else” statement or something? but Thanks guys having loads of fun learning unity and C# and all this was actually really helpful

Edit: i got the rotation easily enough by making a new set of Vector3 like this:

Vector3 up = Vector3.zero,
right = new Vector3(0, 90, 0),
down = new Vector3(0, 180, 0),
left = new Vector3(0, 270, 0),
playerROT = Vector3.zero;

void Start()
{
playerROT = up;

i wanted the rotation to occur first and instead if the movement if the input was different to the way the player was facing but i think i like this better anyways. solved my own problem :smile: thanks anyways guys.

Does anyone know how to make the player move horizontally and vertically?

Steps to success:

  1. First and foremost, STOP necro-posting to threads from 2014. Seriously. Just DON’T.

  2. try some tutorials. There are thousands to select from… try the first three you find:

7946905--1017157--Screen Shot 2022-03-07 at 8.42.31 AM.jpg

Tutorials and example code are great, but keep this in mind to maximize your success and minimize your frustration:

How to do tutorials properly, two (2) simple steps to success:

Tutorials are a GREAT idea. Tutorials should be used this way:

Step 1. Follow the tutorial and do every single step of the tutorial 100% precisely the way it is shown. Even the slightest deviation (even a single character!) generally ends in disaster. That’s how software engineering works. Every step must be taken, every single letter must be spelled, capitalized, punctuated and spaced (or not spaced) properly, literally NOTHING can be omitted or skipped.

Fortunately this is the easiest part to get right: Be a robot. Don’t make any mistakes.
BE PERFECT IN EVERYTHING YOU DO HERE!!

If you get any errors, learn how to read the error code and fix your error. Google is your friend here. Do NOT continue until you fix your error. Your error will probably be somewhere near the parenthesis numbers (line and character position) in the file. It is almost CERTAINLY your typo causing the error, so look again and fix it.

Step 2. Go back and work through every part of the tutorial again, and this time explain it to your doggie. See how I am doing that in my avatar picture? If you have no dog, explain it to your house plant. If you are unable to explain any part of it, STOP. DO NOT PROCEED. Now go learn how that part works. Read the documentation on the functions involved. Go back to the tutorial and try to figure out WHY they did that. This is the part that takes a LOT of time when you are new. It might take days or weeks to work through a single 5-minute tutorial. Stick with it. You will learn.

Step 2 is the part everybody seems to miss. Without Step 2 you are simply a code-typing monkey and outside of the specific tutorial you did, you will be completely lost. If you want to learn, you MUST do Step 2.

Of course, all this presupposes no errors in the tutorial. For certain tutorial makers (like Unity, Brackeys, Imphenzia, Sebastian Lague) this is usually the case. For some other less-well-known content creators, this is less true. Read the comments on the video: did anyone have issues like you did? If there’s an error, you will NEVER be the first guy to find it.

Beyond that, Step 3, 4, 5 and 6 become easy because you already understand!

1 Like