Search code examples
c#unity-game-enginesmoothingrigid-bodies

How to move rigidbody object up or down smoothly in unity3d


I have start a project and I attached a rigidbody to my player for apply some force on it.So when I run my project then in FixedUpdate() function I apply a force on player to moving it in forward direction.So when I pressed leftarrow or rightarrow it perform 'tilt' means rotate it's wings. But now I want to move my player up or down smoothly by pressing Uparrow or Downarrow,and when I press both uparrow and downarrow then it must be affect on player. Here is my code.Please help me. :(

void FixedUpdate ()
{
  rb.AddForce(transform.forward *1000f);
  float movedir = Input.GetAxis ("Horizontal");

  Vector3 movement = new Vector3 (movedir, 0.0f, 0.0f);
  rb.velocity = movement * movingspeed;
  rb.rotation = Quaternion.Euler (0.0f, 0.0f, rb.velocity.x * -3f);//perform tilt


 if (Input.GetKeyDown (KeyCode.UpArrow)) 
      { 
       //code for smoothly move up from current position to = current position + 2f 
      }

  if (Input.GetKeyDown (KeyCode.DownArrow)) 
      { 
        //code for smoothly move down from current position to = current position - 2f 
      }


}

Solution

  • Your question is not really clear. Move smoothly could mean many things.

    In general you should use RigidBody.MovePosition and Rigidbody.MoveRotation to set a Rigidbody's transforms instead of rb.rotation and rb.position in order to get "smooth" movements:

    Use Rigidbody.MovePosition to move a Rigidbody, complying with the Rigidbody's interpolation setting.

    If Rigidbody interpolation is enabled on the Rigidbody, calling Rigidbody.MovePosition results in a smooth transition between the two positions in any intermediate frames rendered. This should be used if you want to continuously move a rigidbody in each FixedUpdate.

    Set Rigidbody.position instead, if you want to teleport a rigidbody from one position to another, with no intermediate positions being rendered.

    So as you can see depending on your settings using Rigidbody.MovePosition might already result in a "smooth" movement.

    rb.MovePosition(transform.position + Vector3.up * 2.0f);
    

    Also you use Input.GetKeyDown so it works like a trigger ... it is not called continously like Input.GetKey

    If you want to move continously while the key stays pressed use e.g.

    // set e.g. in the inspector
    public float verticalMoveSpeed;
    
    // ...
    
    if (Input.GetKey(KeyCode.UpArrow)) 
    { 
        rb.MovePosition(transform.position + Vector3.up * verticalMoveSpeed * Time.deltaTime);
    }
    
    if (Input.GetKey(KeyCode.DownArrow)) 
    { 
        rb.MovePosition(transform.position - Vector3.up * verticalMoveSpeed * Time.deltaTime);
    }
    

    If you want to trigger the movement only with GetKeyDown instead you could also do something like e.g.

    // set e.g. in the inspector
    public float verticalMoveSpeed;
    
    // ...
    
    if (Input.GetKeyDown(KeyCode.UpArrow)) 
    { 
        StartCoroutine(MoveVertical(2.0f, verticalMoveSpeed));
    } 
    else if (Input.GetKeyDown(KeyCode.DownArrow)) 
    { 
        StartCoroutine(MoveVertical(-2.0f, verticalMoveSpeed));
    }
    
    // ...
    
    private IEnumerator MoveVertical(float distance, float speed)
    {
        var originalY = transform.position.y;
        var targetY = originalY + distance;
    
        var currentY = originalY; 
        do
        {     
            rb.MovePosition(new Vector 3(transform.position.x, currentY, transform.positiom.z);
    
            // Update currentY to the next Y position
            currentY = Mathf.Clamp(currentY + speed * Time.deltaTime, originalY, targetY);
            yield return null;
        }
        while(currentY < originalY);
    
        // make sure you didn't move to much on Y
        rb.MovePosition(new Vector3(transform.position.x, targetY, transform.position,z));
    }
    

    Than there are two options to prevent concurrent routines:

    1. use a flag. This also prevents the routine from beeing interrupted/called twice/called concurrent

      privtae bool isMovingVertical;
      
      // ...
      
      if (Input.GetKeyDown(KeyCode.UpArrow) && !isMovingVertical ) 
      { 
          StartCoroutine(MoveVertical(2.0f, verticalMoveSpeed));
      } 
      else if (Input.GetKeyDown(KeyCode.DownArrow) && !isMovingVertical ) 
      { 
          StartCoroutine(MoveVertical(-2.0f, verticalMoveSpeed));
      }
      
      // ...
      
      private IEnumerator MoveVertical(float distance, float speed)
      {
          isMovingVertical = true;
      
          // ...
      
          isMovingVertical = false;
      }
      
    2. use StopAllCoroutines to interrupt a running routine (attention this might lead to "infinite" moves in one direction - at least without you preventing it with additional checks)

      if (Input.GetKeyDown(KeyCode.UpArrow)) 
      { 
          StopAllCoroutines();
          StartCoroutine(MoveVertical(2.0f, verticalMoveSpeed));
      }
      
      if (Input.GetKeyDown(KeyCode.DownArrow)) 
      { 
          StopAllCoroutines();
          StartCoroutine(MoveVertical(-2.0f, verticalMoveSpeed));
      }