- Home /
How do I automatically go diagonally when hitting a diagonal?
So, I'm starting a top-down 2D RPG sort of game, with free-form movement instead of, well, large-grid specific movement. I got a 'basic guy moving across the screen colliding with a tilemap. However, when I hit a diagonal, I slow way down.
I seem to be doing this:

When I want to do this:

This seems to be a problem that should have a simple solution. Ideas?
Total guessarouni without more context, but perhaps you should rotate your 2D character so that it is aligned with the normal of the surface and make sure the code is moving the character on its forward not world forward. That way your code won't be forcing the character into the object. Or you could raycast and take into account the slope when you apply force.
Are there any examples on how to do this?
Right now my movement code is very simple.
float moveHorizontal = Input.GetAxisRaw("Horizontal"); float moveVertical = Input.GetAxisRaw("Vertical");
$$anonymous$$ovement = new Vector2(moveHorizontal, moveVertical); rigidbody2D.velocity = $$anonymous$$ovement * Speed;
I've been working on this and scouring the internet all day, and getting frustrated.
Here's all the code that I have:
void FixedUpdate()
{
float moveHorizontal = Input.GetAxisRaw("Horizontal");
float moveVertical = Input.GetAxisRaw("Vertical");
$$anonymous$$ovement = new Vector2(moveHorizontal, moveVertical);
$$anonymous$$ovement.Normalize();
rigidbody2D.velocity = $$anonymous$$ovement * Speed;
}
void OnCollisionEnter2D(Collision2D other)
{
Normal = other.contacts[0].normal;
rigidbody2D.velocity -= Normal * Vector2.Dot(rigidbody2D.velocity, Normal);
}
It seems that no one has a good answer to this, and I imagine this is a relatively standard problem. What am I doing wrong?
Your OnCollisionEnter2D() code is making your character move at a new velocity parallel to the surface they hit. However, that new velocity is effectively projected from your old rigidbody2D.velocity, and onto the surface (I'll leave out the mathsy explanation for simplicity's sake). What's important is that this just means that for the new velocity, you're keeping only the portion of rigidbody2D.velocity that points in the same direction as the surface (see here for an explanation of vector projection).
Usually this is perfectly acceptable behaviour for movement - eg, walking into walls in an FPS or 3rd person game. However, if you want to maintain your ideal movement speed, in OnCollisionEnter2D() you'll need to take the new velocity, normalize it, and multiply it by your character's speed. This should do the trick.
If you're feeling tricky, you could also add code to OnCollisionEnter2D() that lets the player move until they're just touching the surface, then moves them in a direction parallel to the surface.
Actually, it seems the problem was that the velocity would be set once when the collision first happened, and then the FixedUpdate() loop would override it.
The original calculation of:
rigidbody2D.velocity -= Normal * Vector2.Dot(rigidbody2D.velocity, Normal);
Works; we don't need to multiply speed because speed is already calculated into the velocity, and we use the velocity in the equation.
Here's my new code:
void FixedUpdate()
{
float moveHorizontal = Input.GetAxisRaw("Horizontal");
float moveVertical = Input.GetAxisRaw("Vertical");
$$anonymous$$ovement = new Vector2(moveHorizontal, moveVertical);
$$anonymous$$ovement.Normalize();
rigidbody2D.velocity = $$anonymous$$ovement * Speed;
if (Colliding && $$anonymous$$ovement == Record$$anonymous$$ovement)
{
rigidbody2D.velocity -= Normal * Vector2.Dot(rigidbody2D.velocity, Normal);
}
else
{
Record$$anonymous$$ovement = $$anonymous$$ovement;
}
}
void OnCollisionEnter2D(Collision2D other)
{
Normal = other.contacts[other.contacts.Length - 1].normal;
Normal.Normalize();
OppositeNormal = Normal * -1;
if ((OppositeNormal != $$anonymous$$ovement) && (Colliding != true))
{
Colliding = true;
}
else
{
Colliding = false;
}
}
void OnCollisionExit2D(Collision2D other)
{
Colliding = false;
}
However, then I had another problem. Well, several other problems. I've resolved some, but one remains.
If I am up against a wall, and moving parellel to it so that my collider is 'just touching' the wall, and I hit a diagonal from there, then I move just as slow as if my new code didn't exist. After lots of debugging, I found that we are detecting a new collision, but if I try to get the normal of this new collision, I end up with the normal of the old collision (the wall I was sliding against) I don't know if this is a bug in Unity, or what.
I even tried changing my code to "Normal = other.contacts[other.contacts.Length - 1].normal;", assu$$anonymous$$g that would get the last element of the array (latest), but no dice, same incorrect normal.
So, any ideas?
Answer by zeteginara · Mar 03, 2014 at 04:11 PM
I finally resolved this! My gut feeling was right: I was overthinking it.
Step 1: Remove all of the code example above and just have a simple:
void FixedUpdate()
{
float moveHorizontal = Input.GetAxisRaw("Horizontal");
float moveVertical = Input.GetAxisRaw("Vertical");
movement = new Vector2(moveHorizontal, moveVertical);
movement.Normalize();
rigidbody2D.velocity = movement * Speed;
}
Step 2: In your assets folder, go to Create > Physics2DMaterial. Create one.
Step 3: Click your new material, and call it whatever you want. I called it "Frictionless". Set Friction to 0, and make sure "Bounce" is 0.
Step 4: Drag your new material to any of the "Physics Material (2D)" fields in any of the objects involved in the equation.
You're done!
Your answer
Follow this Question
Related Questions
Diagonal moving is faster .Help in solving a bug! 1 Answer
Drag object with mouse in diagonal direction 0 Answers
Swapping Horizontal Input for Mobile 0 Answers
Diagonal Wall Jump 0 Answers
UnityException: Input Axis Horizontal is not setup. 1 Answer