In unity 2D c# how to rotate an object like geometry dash?

Viewed 569

In my game, my main character is a cube and if press space it's jumping depending on how much you press the button but I want to rotate my cube 90 degrees when pressing the space button. I am unsure of how to achieve this.

Here is what I am currently trying

if (Input.GetKey(KeyCode.Space)) 
{ 
    timeCount=Time.time 
} 

if (Input.GetKeyUp(KeyCode.Space)) 
{ 
    transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(0, 0, 90), timeCount*.1f); 
    timeCount = 0; 
}

Edit: Here is my updated code with your code

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerEverything : MonoBehaviour{
    public Transform GroundCheck; // Put the prefab of the ground here
    
    public LayerMask groundLayerBlue; // Insert the layer here.
    public LayerMask groundLayerPurple;
    public bool isGroundedBlue;   
    public bool isGroundedPurple;
    private Rigidbody2D rb2D;
    private float jumpTimer;
    private float timeCount = 0.0f;
    private bool isGrounded;
    public Transform groundCheck;
    public LayerMask groundLayer;
    // we are going to store our coroutine to assure that it is only going to run once
    private Coroutine RotationCoroutine = null;
    
    [SerializeField] private float degreesToRotateBy;   // assign this variable to 90.0f in the inspector
    [SerializeField] private float timeToRotate;        // assign this variable to however much time you want the rotation to occur
    
    // axis of rotation that will occur
    private Vector3 rotationAxis = Vector3.forward;
    
    // Start is called before the first frame update
    public void Start()
    {
        rb2D = GetComponent<Rigidbody2D>();
    }
    
    // Update is called once per frame
    public void Update()
    {
        isGroundedBlue = Physics2D.OverlapCircle(GroundCheck.position, 0.15f, groundLayerBlue);
        if (isGroundedBlue)
        {
            transform.position += Vector3.right/60;
        }
        
        isGroundedPurple = Physics2D.OverlapCircle(GroundCheck.position, 0.15f, groundLayerPurple);
        if (isGroundedPurple)
        {
            transform.position += Vector3.left / 60;
        }
    
        if (isGrounded)
        {
            if (Input.GetKey(KeyCode.Space))
            {
                jumpTimer += Time.deltaTime;
                timeCount = Time.time;
            }
            
            if (Input.GetKeyUp(KeyCode.Space))
            {
                DoJump(250f * jumpTimer);
                
                jumpTimer = 0;
            }
            
            // I changed your input to GetKeyDown instead of GetKey
            // as GetKeyDown is triggered the single frame the user presses space instead of every frame
            if (Input.GetKeyDown(KeyCode.Space))
            {
                if (RotationCoroutine == null)
                    RotationCoroutine = StartCoroutine(RotateAxisByDegrees(degreesToRotateBy, timeToRotate, rotationAxis));
            }
    
            if (Input.GetKeyUp(KeyCode.Space))
            {
                // assuming you want the rotation to stop 
                if (RotationCoroutine != null)
                    StopCoroutine(RotationCoroutine);
    
                RotationCoroutine = null;
            }
        }
    
    }
    
    /// <summary>
    /// Rotate an object by a set angle in the rotationAxis over a set time
    /// </summary>
    /// <param name="angleToRotateTo">Angle we are rotating by</param>
    /// <param name="timeToRotate">Amount of time the rotation takes</param>
    /// <param name="rotationAxis">Axis to rotate around</param>
    /// <returns></returns>
    private IEnumerator RotateAxisByDegrees(float angleToRotateTo, float timeToRotate, Vector3 rotationAxis)
    {
        // store our initial rotation and keep a timer for the rotation progress
        float currentTimeElapsed = 0.0f;
        Quaternion startRotation = transform.rotation;
        Quaternion endRotation = startRotation * Quaternion.AngleAxis(angleToRotateTo, rotationAxis);
    
        while (currentTimeElapsed < timeToRotate)
        {
            // set our new rotation
            transform.rotation = Quaternion.Slerp(startRotation, endRotation, currentTimeElapsed / timeToRotate);
    
            // increment our timer with how long it has been since the last frame
            currentTimeElapsed += Time.deltaTime;
    
            // this line will tell the coroutine to end here and continue work in the next frame
            yield return null;
        }
    
        // assign our rotation in case there are floating point errors
        transform.rotation = endRotation;
    
        // set our coroutine to null as we are done
        RotationCoroutine = null;
    }
    
    private void FixedUpdate()
    {
        isGrounded = Physics2D.OverlapCircle(groundCheck.position, 0.2f, groundLayer);
    }
    
    public void DoJump(float JumpForce)
    {
        float jumpForceMagnitutde = Mathf.Clamp(JumpForce/3, 0, 50);
        rb2D.AddForce(Vector2.up * jumpForceMagnitutde/3, ForceMode2D.Impulse);
        rb2D.AddForce(Vector2.right*jumpForceMagnitutde/10,ForceMode2D.Impulse);
    
    }
}
1 Answers

As I mentioned, the example code you posted has one main issue

if (Input.GetKey(KeyCode.Space)) 
{ 
    timeCount=Time.time 
} 

if (Input.GetKeyUp(KeyCode.Space)) 
{ 
    transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(0, 0, 90), timeCount*.1f); 
    timeCount = 0; 
}

A Lerp is a process that gradually completes over multiple frames. I am assuming that this input code is placed inside of the Update function, which normally would allow the Lerp to work. However, as it is inside of a conditional checking for the lifting of space, which only happens the single frame the user lifts a key, it will not continually run.

In situations after some sort of event (a jump in your case) occurs and I need a task gradually completed, I usually turn to Coroutines. A Coroutine is a function that is able to handle processes in small chunks every frame and return to where it left off in the following frame.

// we are going to store our coroutine to assure that it is only going to run once
private Coroutine RotationCoroutine = null;

[SerializeField] private float degreesToRotateBy;   // assign this variable to 90.0f in the inspector
[SerializeField] private float timeToRotate;        // assign this variable to however much time you want the rotation to occur

// axis of rotation that will occur
private Vector3 zRotationAxis = Vector3.forward;

private void Update()
{
    // I changed your input to GetKeyDown instead of GetKey
    // as GetKeyDown is triggered the single frame the user presses space instead of every frame
    if (Input.GetKeyDown(KeyCode.Space))
    {
        if (RotationCoroutine == null)
            RotationCoroutine = StartCoroutine(RotateAxisByDegrees(degreesToRotateBy, timeToRotate, zRotationAxis));
    }

    if (Input.GetKeyUp(KeyCode.Space))
    {
        // assuming you want the rotation to stop 
        if (RotationCoroutine != null)
            StopCoroutine(RotationCoroutine);

        RotationCoroutine = null;
    }
}

/// <summary>
/// Rotate an object by a set angle in the rotationAxis over a set time
/// </summary>
/// <param name="angleToRotateTo">Angle we are rotating by</param>
/// <param name="timeToRotate">Amount of time the rotation takes</param>
/// <param name="rotationAxis">Axis to rotate around</param>
/// <returns></returns>
private IEnumerator RotateAxisByDegrees(float angleToRotateTo, float timeToRotate, Vector3 rotationAxis)
{
    // store our initial rotation and keep a timer for the rotation progress
    float currentTimeElapsed = 0.0f;
    Quaternion startRotation = transform.rotation;
    Quaternion endRotation = startRotation * Quaternion.AngleAxis(angleToRotateTo, rotationAxis);

    while (currentTimeElapsed < timeToRotate)
    {
        // set our new rotation
        transform.rotation = Quaternion.Slerp(startRotation, endRotation, currentTimeElapsed / timeToRotate);

        // increment our timer with how long it has been since the last frame
        currentTimeElapsed += Time.deltaTime;

        // this line will tell the coroutine to end here and continue work in the next frame
        yield return null;
    }

    // assign our rotation in case there are floating point errors
    transform.rotation = endRotation;

    // set our coroutine to null as we are done
    RotationCoroutine = null;
}

I currently implemented a rotation that is done in a specific amount of time. It is possible to rotate an object given a speed instead. You can also extend the function to take in a specific axis to create a function that allows rotation on any axis by any angle in any amount of time. Just add the new parameter and do not make the axis global.

If you want to learn more about Lerp, there is a useful article that goes over some general practices when dealing with them.

Edit: To clarify why the current code is not working, it is because you are putting the if condition check for GetKeyUp inside of your isGrounded check.

if (isGrounded)
{
    if (Input.GetKey(KeyCode.Space))
    {
        jumpTimer += Time.deltaTime;
        timeCount = Time.time;
    }
    
    if (Input.GetKeyUp(KeyCode.Space))
    {
        DoJump(250f * jumpTimer);
        
        jumpTimer = 0;
    }
    
    // I changed your input to GetKeyDown instead of GetKey
    // as GetKeyDown is triggered the single frame the user presses space instead of every frame
    if (Input.GetKeyDown(KeyCode.Space))
    {
        if (RotationCoroutine == null)
            RotationCoroutine = StartCoroutine(RotateAxisByDegrees(degreesToRotateBy, timeToRotate, rotationAxis));
    }
}

// this needs to be outside of the isGrounded
if (Input.GetKeyUp(KeyCode.Space))
{
    // assuming you want the rotation to stop 
    if (RotationCoroutine != null)
        StopCoroutine(RotationCoroutine);

    RotationCoroutine = null;
}
Related