Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Resolved How to tilt a rigidbody when it's turning based on its own local rotation

Discussion in 'Physics' started by Phasmatodea, Jan 30, 2021.

  1. Phasmatodea

    Phasmatodea

    Joined:
    Jan 30, 2021
    Posts:
    4
    Hi there!

    I'm new to Unity (and to game dev also actually). I'm creating my first video game and until now I've found answers to my questions and issues on this forum or others but for this one, I've been searching for days and I can't find anything.

    So I've got a wheel-shaped rigidbody (third person view) that moves forwards and backwards based on the vertical inputs, and that turns left and right based on both the horizontal inputs AND/OR the camera (cam.eulerAngles.y). And here is my issue :

    I can't find the way to change the wheel's rotation on the Z axis so it basically tilts/leans when it's turning (based on the inputs and/or the camera). I've read a lot about tilting/leaning but every time it's only based on the inputs, not the camera. So it doesn't work for me because for example, when I press D the wheel changes its direction in the world, but locally it's still going forward, and it's the same for each direction.

    I hope I'm clear enough and that someone can help !

    Here is my move function :

    Code (CSharp):
    1.  
    2.         // Declared in Update()
    3.         float xAxis = Input.GetAxisRaw("Horizontal");
    4.         float zAxis = Input.GetAxisRaw("Vertical");
    5.         direction = new Vector3(xAxis, 0f, zAxis).normalized;
    6.  
    7. private void PlayerMove()
    8.     {
    9.         // Slows the player down when jumping
    10.         if (!isGrounded)
    11.         {
    12.             currentSpeed /= 5;
    13.         }
    14.  
    15.         // Sets target angle according to inputs + camera angle
    16.         float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg + cam.eulerAngles.y;
    17.         // Smooths the target angle
    18.         float angle = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref turnSmoothVelocity, turnSmoothTime);
    19.  
    20.         transform.rotation = Quaternion.Euler(0f, angle, 0f);
    21.  
    22.         Vector3 moveDir = Quaternion.Euler(0f, targetAngle, 0f) * Vector3.forward;
    23.         movement = moveDir.normalized * currentSpeed * Time.deltaTime;
    24.         rb.AddForce(movement,ForceMode.VelocityChange);
    25.     }
     
    Last edited: Feb 8, 2021
  2. Phasmatodea

    Phasmatodea

    Joined:
    Jan 30, 2021
    Posts:
    4
  3. UniteFriend

    UniteFriend

    Joined:
    Feb 5, 2021
    Posts:
    3
    Use Rigidbody.MoveRotation to rotate a Rigidbody, complying with the Rigidbody's interpolation setting.


    If Rigidbody interpolation is enabled on the Rigidbody, calling Rigidbody.MoveRotation will resulting in a smooth transition between the two rotations in any intermediate frames rendered. This should be used if you want to continuously rotate a rigidbody in each FixedUpdate.


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

    //Attach this script and a Rigidbody to your GameObject. To do this click the GameObject, click the Add Component button in its Inspector, and go to Physics>Rigidbody.


    using UnityEngine;

    using System.Collections;


    public class Example : MonoBehaviour

    {

    //Make sure you attach a Rigidbody in the Inspector of this GameObject

    Rigidbody m_Rigidbody;

    Vector3 m_EulerAngleVelocity;


    void Start()

    {

    //Set the axis the Rigidbody rotates in (100 in the y axis)

    m_EulerAngleVelocity = new Vector3(0, 100, 0);


    //Fetch the Rigidbody from the GameObject with this script attached

    m_Rigidbody = GetComponent<Rigidbody>();

    }


    void FixedUpdate()

    {

    Quaternion deltaRotation = Quaternion.Euler(m_EulerAngleVelocity * Time.deltaTime);

    m_Rigidbody.MoveRotation(m_Rigidbody.rotation * deltaRotation);

    }

    }
     
  4. Phasmatodea

    Phasmatodea

    Joined:
    Jan 30, 2021
    Posts:
    4
    Thank you for your answer! Actually, your script rotates the axis every fixedUpdate. But what I'm looking for is to rotate (on the Z axis, because it's only to lean/tilt the wheel) only when I turn (with the horizontal inputs or the mouse/camera). So it looks more like the wheel leans purposely the way it's going. It should behave like a motorbike wheel, but without the bike and all.
     
    Last edited: Feb 5, 2021
  5. Phasmatodea

    Phasmatodea

    Joined:
    Jan 30, 2021
    Posts:
    4
    Yay! So I finally made it with the help of a lot of forums and lots of head scratchings! Here is my solution, I hope it will be useful to some. (The code might not be super optimized, corrections are welcome.)

    So I had to proceed in two steps, first step is to identify when the rigidbody turn right, left or doesn't turn. To do so, I used the velocity and checked whether it's below or above 1. Here is the code for this first step, in the Update function :

    Code (CSharp):
    1.         // Sets the delta of the rotation since the last frame (see at the bottom)
    2.         Quaternion deltaRot = transform.rotation * Quaternion.Inverse(prevRot);
    3.  
    4.         // If the delta is negative it means the rigidbody is turning left relatively to itself
    5.         if (deltaRot.y * 100 < -1.0f) // Had to multiply by 100, as the value is very small
    6.         {
    7.             noTurn = false;
    8.             turnRight = false;
    9.             turnLeft = true;
    10.  
    11.             // Sets the intensity of the turn and scale it between 1 and 10 to be used later as a multiplier for the tilting
    12.             leftIntensity = deltaRot.y * 100;
    13.             leftIntensity = Mathf.Clamp(leftIntensity, -20f, 0f); // Clamps the value between -20 and 0
    14.             leftIntensity = Mathf.Lerp(10f, 1f, Mathf.InverseLerp(-20f, 0f, leftIntensity));
    15.         }
    16.         // Else if the delta is positive, the rigidbody is turning right
    17.         else if(deltaRot.y * 100 > 1.0f)
    18.         {
    19.             noTurn = false;
    20.             turnLeft = false;
    21.             turnRight = true;
    22.          
    23.             // Same as leftIntensity
    24.             rightIntensity = deltaRot.y * 100;
    25.             rightIntensity = Mathf.Clamp(rightIntensity, 0f, 20f); // Clamps the value between 0 and 20
    26.             rightIntensity = Mathf.Lerp(1f, 10f, Mathf.InverseLerp(0f, 20f, rightIntensity));
    27.  
    28.         }
    29.         // Else, the rigidbody is not turning
    30.         else
    31.         {
    32.             turnLeft = false;
    33.             turnRight = false;
    34.             noTurn = true;
    35.         }
    36.  
    37.         // Saves the rotation at the end of Update function in order to compare it in the next frame (see at the top)
    38.         prevRot = transform.rotation;
    Once this is done, we know when the rigidbody is turning and have its turning intensity stored. Now the second step is to actually tilt the rigidbody left or right accordingly. Here is the code for the second step, in the FixedUpdate function :

    Code (CSharp):
    1.  
    2.         // If the rigidbody is turning left/right, sets the maximum/target rotation (30f) + rotates from the current rotation to the target rotation according to a delta multiplied by the left/rightIntensity (so in my example, 3f * 1-10 depending on the intensity of the turn in order to get to 30f)
    3.         // If the rigidbody isn't turning, it substracts its own current rotation to itself in order to return to 0
    4.          if (turnLeft)
    5.         {
    6.             targetRotation = Quaternion.AngleAxis(30f, rb.transform.forward) * rb.transform.rotation;
    7.             rb.transform.rotation = Quaternion.RotateTowards(rb.transform.rotation, targetRotation, leftIntensity * 3f);
    8.         }
    9.         else if (turnRight)
    10.         {
    11.             targetRotation = Quaternion.AngleAxis(-30f, rb.transform.forward) * rb.transform.rotation;
    12.             rb.transform.rotation = Quaternion.RotateTowards(rb.transform.rotation, targetRotation, rightIntensity * 3f);
    13.         }
    14.         else
    15.         {
    16.             resetAngle = rb.transform.rotation.eulerAngles.z;
    17.             targetRotation = Quaternion.AngleAxis(-resetAngle, rb.transform.forward) * rb.transform.rotation;
    18.             rb.transform.rotation = Quaternion.RotateTowards(rb.transform.rotation, targetRotation, 15f);
    19.         }
    Hope it's clear and it will help! Don't hesitate to make corrections and optimisations. The issue I'm facing with it now is some kind of blinking of the rigidbody, from time to time it tilts the opposite direction for a split second, so I still have to tweak it a bit.