Unity 8-Directional Locked Movement (Super Mario 3D World Style)

Hey everyone. I’ve been struggling for the past few days on something I didn’t think would be too difficult but has turned out to be anyway. I know exactly what I want to make and I have a lot of experience in Unity, but I cannot figure out the solution to this problem at all. Here are the details:

  • I am trying to create a top-down character controller where the player can only move in 8 directions using WASD in the input manager to control.
  • The player always faces the direction they are going, like Diablo or Super Mario.
  • However, I still want to lerp, or blend, between the 8 different directions to make everything look smooth.

The main problem I keep having is if I slightly tap a key to move diagonally, the player doesn’t complete the rotation towards the diagonal angle and stops between 2 of the 8 directions. Instead of being at a 90 or 45 degrees angle, if you slightly touch the key, the player doesn’t finish rotating into that new position and will get stuck in obscure angles like 72 or whatever.

I guess what I am trying to say is I need some sort of way to move the player smoothly in 8 directions, but still is certain to end up on a solid 45, 90, 135, etc. angle. I have tried everything I can think of but I still have no idea how to go about doing this.

TL;DR: Here is a video that shows the game Super Mario 3D World’s movement, which is EXACTLY what I’m looking for. Notice how Mario always is locked to facing 8 directions but also smoothly rotates between those directions. Thanks a lot for reading this! I hope someone knows a way to do this.

Well, it would be interesting how you actually implemented the rotation. If you create a Vector3 out of the two key axis the vector you just have to clamp it to one of those 8 directions.

Vector3 targetDir;

void Update()
    Vector3 inputDir = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    Vector3 v = inputDir.normalized;
    v.x = Mathf.Round(v.x);
    v.z = Mathf.Round(v.z);
    if (v.sqrMagnitude > 0.1f)
        targetDir = v.normalized;
    transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(targetDir), Time.time*5f);
    // your movement code

The trick here is we first normalize the direction so it’s length is 1.0. That means the components of “v” are in the range [-1, 1] Simply rounding each component seperately will ensure their values are either -1, 0 or 1. This represents exactly the 8 direction thing you want. If the input vector is too small (i.e. the user doesn’t press any keys) we have to prevent that the “targetDir” is set to Vector3.zero, that’s why there’s the magnitude check

So at any time targetDir represents a vector that always points in one of those 8 directions. Independent from that we do the rotation smoothing each frame in Update.

Vector3 input = new Vector3 (horizontal, 0f, vertical);
Vector3 inputRaw = new Vector3 (horizontalRaw, 0f, verticalRaw);
if (input.sqrMagnitude > 1f)
input.Normalize ();
if (inputRaw.sqrMagnitude > 1f)
inputRaw.Normalize ();

if (inputRaw != Vector3.zero)
	targetRotation = Quaternion.LookRotation (input).eulerAngles;

rigidbod.rotation = Quaternion.Slerp (transform.rotation, Quaternion.Euler (targetRotation.x, Mathf.Round (targetRotation.y / 45) * 45, targetRotation.z), Time.deltaTime * rotationSpeed);

//Movement Code Goes Here...

I had been working on this and it works really well! No problems with it at all whatsoever. It works identical to how it does in Super Mario 3D World. I hope this helps out anyone else trying to achieve this!

Hello @Colinmbo,

I have the exact same problem : “However, when you release those two keys, unless the player releases those keys on the same exact frame (which isn’t likely), the player turns to the direction of the last pressed key”.

Have you found a solution for this? I’ve looked around the forums and anwsers for over 3 hours now. Nothing so far. Doing this in 2D actually.

Thanks in advance.

Hey, guys! I could use some help! @colinmbo @Bunny83 @Kaitocs

I’ve put each code to test, but the rotation doesn’t work properly.
When i type left, my character faces left, like in the Super Mario video posted here, but while facing that new direction, my character moves towards the camera (that is, sideways).
Same happens when pressing right.

Also, when pressing down arrow, he turns until he’s looking to the camera and moves to the direction his back is facing.

Same diagonals problem too!

On the console, it says:
“Look rotation viewing vector is zero

I hope what i just wrote seems clear to you!
Thanks, in advance, for the help given!