New Input System Top Down Movement / Rotation

Hi all, i’ve recently moved from Game Maker to Unity. as support for GMS 1.4 stopped, and GMS 2 is too costly for my needs at the moment. c# is a new playground for me, and i’m still getting to grips with it from GML.

I’m creating a top down shooter and thought i’d get stuck in with some of Unity’s newest features.

Below is the code I’ve got to move and rotate my ship. It works great, moves and rotates in direction of left thumbstick fine. Except when you release the thumbstick, it snaps the ship to pointing to the right (or rotation of 0 I think), whereas I want it to keep it it’s last rotation angle. I believe this is because I reset Vector2 back to zero when left thumbstick movement is cancelled.

I am unsure how to cancel out left thumbstick input and stop ship from moving, but at the same not reset it’s rotation…

If anybody can help, or suggest a technique to look at that would be great. Or even am I going about the new input system massively wrong??

Thanks all in advance, any response, advice is much apprecaited!

public class shipcontrols : MonoBehaviour
{
    public GameObject LaserProjectile;
    public GameObject ship;
    public Transform FirePoint;

    Vector2 move;
    PlayerControls controls;

    void Awake()
    {
        controls = new PlayerControls();

        controls.Gameplay.Fire.performed += ctx => Fire();

        controls.Gameplay.Move.performed += ctx => move = ctx.ReadValue<Vector2>();
        controls.Gameplay.Move.canceled += ctx => move = Vector2.zero;

    }

    void Update()
    {
        FirePoint.rotation = transform.rotation;

        float deadzone = 0.3f;
        Vector2 stickInput = new Vector2(move.x, move.y);
        if (stickInput.magnitude < deadzone)
        {
            stickInput = Vector2.zero;
        }
        else
        {
            Vector2 m = new Vector2(move.x, move.y) * Time.deltaTime;
            transform.Translate(m, Space.World);
        }

        float angle = Mathf.Atan2(move.y, move.x) *Mathf.Rad2Deg;
        ship.transform.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
    }

    void OnEnable()
    {
        controls.Gameplay.Enable();
    }

    void OnDisable()
    {
        controls.Gameplay.Disable();
    }

    void Fire()
    {
        Instantiate(LaserProjectile, FirePoint.position, FirePoint.rotation);
    }
}
2 Likes

Welcome to C# land! It is a wild and fun place… I applaud you for stepping up, and this is the right place for questions like the above.

All that said, I believe what is happening above is that on line 37 above you are always calculating the new heading, and when your controls return to (0,0), that’s why you snap.

One way to control this is to only adjust the heading when the magnitude of move is greater than a certain small amount. If you guard both lines 37 and line 38 with an if statement like so, it should do it:

// insert before line 37 above:
if (move.magnitude > 0.1f)
{
// lines 37 and 38 above go here...
}

That will still snap though when you abruptly whip the controls from left to right, but it will not snap when you let the stick go, unless the input magnitude goes above 0.1f

To make it smoothly turn through angles is a bit trickier. Here are the steps, broadly speaking:

  • keep your own float variable that is your current angle
  • choose a rate of turn (say 200 degrees per second)
  • whenever you have a new calculated angle (the output of line 37 above), then:
    – - use Mathf.DeltaAngle() to find the difference
    – - based on the sign of the delta, either increase or decrease your own notion of heading by rate-of-turn times Time.deltaTime each frame.

Hi Kurt-Dekker,

Thank you for the reply! Much appreciated!

It makes sense when you point it out. I just didn’t see it…

It works, although I have been looking at possibly using a rigidbody now, as the idea of applying force, and having drag etc is more towards the type of movement I want.

But that’s for another day! At least now I can play without the horrible snapping to one angle!

Another thing then, if you don’t mind, as you seem to know your stuff.

I have an animation with multiple sprites, the animation speed is 0, and i’ve set it up to randomly choose a sprite based on a random number. I also have multiple 2D Polygon colliders all disabled, which match each animation frame. I’m then enabling the one to match the current sprite used in the animation.

There are 15 frames in the animation, so 15 colliders. At the moment I have each collider set to a variable like so:

public PolygonCollider2D p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15;

Then a switch statement to set which collider to enable, like so:

switch (randSet)
        {
            case 1: p1.enabled = true; break;
            case 2: p2.enabled = true; break;
            //etc... etc...
        }

I was wandering if there was a quicker to do so, as randSet will always be within 1 - 15. So is there a way to create something like the below?? A way to ammend the number in the variable name?? I know the below won’t work at all.

p(randSet).enabled = true;

I don’t mind switch statements, but when there more than say 10 cases, I think it just looks untidy and I think to myself there must be another shorter, cleaner way.

Thank you again!

Oh yes, oh yes there is! You need to learn about arrays and other collections… you’ll be AMAZED! :slight_smile:

Try this variable instead:

public PolygonCollider2D[] colliders;

Drag all 15 into there, then when you want a particular one, use an index:

int whichOne = 7; // however you determine this

var collider = colliders[whichOne];
collider.enabled = true;

To randomly choose, you use the .Length property:

int aRandomOne = Random.Range( 0, colliders.Length);

Thank you! I vaguely understood about Arrays and lists, but it’s a lot simpler to understand and implement than it reads, when someone types it like that. Thank you for all your help!