Home > Software design >  Multiple keydown rotations cause unwanted angles
Multiple keydown rotations cause unwanted angles

Time:10-30

I'm fairly new to unity, and after scraping through threads about rotations through frames, I managed to find code to help me get the look that I wanted for my game, here's the code for the movement:

public class Movement : MonoBehaviour
 {
 
     IEnumerator RotateOctagon(Vector3 byAngles, float inTime)
     {
         var fromAngle = transform.rotation;
         var toAngle = Quaternion.Euler(transform.eulerAngles   byAngles);
         for(var t = 0f; t <= 1; t = Time.deltaTime / inTime)
         {
             transform.rotation = Quaternion.Lerp(fromAngle, toAngle, t);
             yield return null;
         }
         transform.rotation = toAngle;
     }
 
     // Update is called once per frame
     void Update()
     {
         if (Input.GetKeyDown("left"))
         {
                 StartCoroutine(RotateOctagon(Vector3.forward * 45, 0.1f));
         }
         if (Input.GetKeyDown("right"))
         {
             StartCoroutine(RotateOctagon(Vector3.forward * -45, 0.1f));
         }
         if (Input.GetKeyDown("up"))
         {
             StartCoroutine(RotateOctagon(Vector3.forward * 180, 0.1f));
         }
     }
 }

And it generates desired results - putting the octagon to its next side, assuming you wait until the rotation is finished...

enter image description here

But The problem arises when you press left or right in the middle of the animation, in which the octagon incorrectly ends up on a point or weird angle (not a multiple of 45 degrees)...

enter image description here

My guess is that if the octagon is in the middle of the coroutine of rotating to the right say, at 30 degrees, any immediate key press will now rotate from that angle, ending up being 30 - 45 an unwanted angle.

I considered calculating the offset of the angle to the lower/higher 45 degree angle multiple, but I feel like there's a more correct approach and something I'm missing about coroutines that would help this.

Any help on how to get the octagon to rotate properly under the condition that multiple keys are pressed would be greatly appreciated.

Desired behavior: Say the current z axis is 0. After right is pressed and in the middle of rotating, left is pressed at 30 degrees. The octagon should rotate back to z=0

CodePudding user response:

In order to simply not allow a new routine until the previous one has finished simply introduce a bool flag

private bool isRotating;

IEnumerator RotateOctagon(Vector3 byAngles, float inTime)
 {
     if(isRotating) yield break;
     isRotating = true;

     var fromAngle = transform.rotation;
     var toAngle = Quaternion.Euler(transform.eulerAngles   byAngles);
     for(var t = 0f; t <= 1; t = Time.deltaTime / inTime)
     {
         transform.rotation = Quaternion.Lerp(fromAngle, toAngle, t);
         yield return null;
     }
     transform.rotation = toAngle;

     isRotating = false;
 }

This way your input is basically ignored until the previous routine finished.

In order to be more efficient you can then also add

void Update()
{
    if(isRotating) return;

    ...
}

Or the "fancy" alternative without additional flag: Make Start itself a Coroutine and do

IEnumerator Start ()
{
    while(true)
    {
        if (Input.GetKeyDown("left"))
        {
             // This now rotates and waits at the same time until the rotation is finished
             yield return RotateOctagon(Vector3.forward * 45, 0.1f));
        }
        else if (Input.GetKeyDown("right"))
        {
            yield return RotateOctagon(Vector3.forward * -45, 0.1f));
        }
        else if (Input.GetKeyDown("up"))
        {
            yield return RotateOctagon(Vector3.forward * 180, 0.1f));
        }
        else
        {
            // If none of the previous is true wait at least one frame
            yield return null;
        }
    }
}

Alternatively since now we know you want to rather interrupt the current rotation and start a new one store it in a field like

private float rotation;

void Awake()
{
    rotation = transform.localEulerAngles.z;
}

void Update()
 {
     if (Input.GetKeyDown("left"))
     {
         RotateAbout (45);
     }

     if (Input.GetKeyDown("right"))
     {
         RotateAbout (-45);
     }

     if (Input.GetKeyDown("up"))
     {
         RotateAbout (180);
     }
 }

private void RotateAbout(float angle)
{
    StopAllCoroutines ();
    rotation  = angle;
    StartCoroutine(RotateOctagon(rotation, 0.1f));
}

and then rather do

IEnumerator RotateOctagon(float toAngle, float inTime)
 {
     var fromRotation = transform.localRotation;
     var toRotation =  Quaternion.Euler(Vector3.forward * toAngle);
     for(var t = 0f; t <= 1; t = Time.deltaTime / inTime)
     {
         transform.localRotation = Quaternion.Lerp(fromRotation, toRotation, t);
         yield return null;
     }
     transform.localRotation = toRotation;
 }
  • Related