3D Rigidbody Turning Inconsistencies while moving on a surface

Hey folks,

I posted a question (and a sort of answer) to Unity Answers. I didn’t get any bites there, but I did find out more information regarding the behavior I observed after additional testing. I have included the original and follow up post below.

However, while I have found a solution that should work for my purposes, the underlying question still lingers.

TLDR

I have a 3D rigidbody cube being driven on top of either terrain or much larger cubes. When turning the cube, using AddTorque, there is a noticeable difference in the sharpness of a left turn versus a right turn (

← before some refactoring, when the issue was more severe).

Setting the friction values to 0 and minimum combine seem to remove the variance.

Preferring to keep the friction values, I rewrote the script. The issue is still present, but much slighter than before. Based on my current setup, the variance between a left turn and a right turn is ~15%. I would like to understand why.

Original Post

Good morning,

I am observing a scenario where a 3D rigidbody cube behaves inconsistently between turning left & turning right. I am attempting to move the cube like a vehicle and stay compliant with the physics system (I’m using addforce/addtorque instead of adjusting some rigidbody values directly). Across several sessions, I have tweaked numerous values hoping to discover what causes this behavior. So far, I have had no luck isolating the issue.

I have ran low on ideas, but this behavior is really bothering me. I have circumvented the issue in the past by upping the torque value, but I was intending to improve finer control when this behavior reared its ugly head again. I wanted to post the question and see if the community had any experience or feedback regarding the issue.

I have attached a video displaying the behavior:

Additionally, here’s a code sample of the most relevant area. I believe I am applying the torque forces in the same manner both positively and negatively, but I could certainly have overlooked something.

            forwardInput = Input.GetAxis("Vertical");
            lateralInput = Input.GetAxis("Q/E");
            pivotInput = Input.GetAxis("Horizontal");
 
            if (Math.Abs(pivotInput) > 0f)
            {
                pivotCurrent = 0.16f * Math.Sign(pivotInput); ;
            }
            else if (Math.Abs(pivotInput) == 0f)
            {
                float subtractAmount = 1f;
                if (Math.Abs(pivotCurrent) <= 1f)
                {
                    subtractAmount = Math.Abs(pivotCurrent);
                }
                pivotCurrent = (Math.Abs(pivotCurrent) - subtractAmount) * Math.Sign(pivotCurrent);
            }
            float maxPivot = 60f;
            pivotCurrent = Mathf.Clamp(pivotCurrent, -maxPivot, maxPivot);
 
            pivotProduct = Vector3.up * pivotCurrent;
 
            if (BoxCaseGroundCheck())
            {
                myRigidbody.AddRelativeTorque(pivotProduct, ForceMode.Impulse);
                myRigidbody.AddRelativeForce(Vector3.forward * forwardInput * forwardMultiplier, ForceMode.Force);
                myRigidbody.AddRelativeForce(Vector3.right * lateralInput * lateralMultiplier, ForceMode.Force);
                if (myRigidbody.velocity.sqrMagnitude > squareMaxVelocity)
                {
                    RegulateVelocity();
                }
            }

Thanks for your time and attention regarding this behavior.

Follow Up Post

After investigating this issue further, it appears to be related to the friction component of the Player’s physic material. Setting dynamic friction/static friction to 0.0 seems to remove irregularities.

However, I have established a workable solution that seems to minimize the magnitude of the issue.

            // Ang Vel of .4-.5 feels good for turning
            // with Vel Change forcemode
  
            // Forcemode.VelocityChange directly adjusts the angular velocity. Give or take some
            // However, physic material Friction also seems to impact this. Add an additional offset for this
  
            // Left and right turning is not precisely the same, but it's withing spitting distance.
            // seems to be something related to friction physics-- setting to zero will ignore it
            // Will be investigating "friction 2" functionality I discovered
  
            forwardInput = Input.GetAxis("Vertical");
            steeringInput = Input.GetAxis("Horizontal");
  
            float deltaTargetAngularVelocity = 0f;
            float localVelocityZ = transform.InverseTransformVector(myRigidbody.velocity).z;
            float baseTarget = .75f;
            // Checking it against zero didn't seem to function. checking it against a small value (.025 arbitrarily)
            // seems to achieve the behavior
            if (Math.Abs(localVelocityZ) >= 0.025f)
            {
                deltaTargetAngularVelocity = Steering2(baseTarget);
            }
            else
            {
                deltaTargetAngularVelocity = Steering2(baseTarget * 2.25f);
            }
  
            pivotProduct = Vector3.up * deltaTargetAngularVelocity;
  
            if (BoxCaseGroundCheck())
            {
                myRigidbody.AddRelativeTorque(pivotProduct, ForceMode.VelocityChange);
                myRigidbody.AddRelativeForce(Vector3.forward * forwardInput * forwardMultiplier, ForceMode.Force);
  
                RegulateVelocity();
                RegulateLateralVelocity();
            }

        float Steering2(float targetValue)
        {
            /*
             * For some reason, I'm still seeing a variance on left/right turns. It seems to be something
             * concerning friction values. Removing friction removes the variances.
             * Until I can isolate it further, I've customized the steering function to degrade negative/
             * left turn by 15%-- this seems to get the values in range at the setting angVel goal of .75
             */
            Debug.Log(targetValue);
            float deltaTargetAngularVelocity = 0f;
            if (steeringInput != 0f)
            {
                float frictionValue = GetComponent<BoxCollider>().material.dynamicFriction;
                float localAngularVelocityY = transform.InverseTransformVector(myRigidbody.angularVelocity).y;
                float targetedAngularVelocity = targetValue * Math.Sign(steeringInput);
                deltaTargetAngularVelocity = targetedAngularVelocity - localAngularVelocityY;
                if(steeringInput > 0)
                {
                    deltaTargetAngularVelocity += frictionValue * Math.Sign(steeringInput);
                }else if (steeringInput < 0 )
                {
                    deltaTargetAngularVelocity += frictionValue * Math.Sign(steeringInput) * .85f;
                }
            }
  
            return deltaTargetAngularVelocity;
        }

Beyond refactoring the code, the major changes were:

  1. Changed Forcemode to Velocity Change. This takes some of the guesswork out of a torque to angular velocity conversion. It’s not precisely there, but it’s close enough to work with.
  2. In the same vein, it seems like dynamic/static friction component impacts velocity change nearly 1 to 1. adding the friction value to addTorque’s force seems to do a respectable job.
  3. Within the Steering2() functon, I wrote out a bit of code to scale back Left-turning by about 15%. This puts it roughly in line with RIght-turning with my specific setup. Additionally, even ignoring the 15% offset, this bit of code seemed to perform respectably, and probably wouldn’t have noticed the differences between left and right turning were it not for tracking the value onscreen.

Ultimately, I am pleased with the solution, and wanted to share it here in case anyone else experiences this issue.

The root of the question still remains though:
Why is there a ~15% turning variance between a left turn and a right turn?

1 Like

Man Ive had that same issue. Never got anywhere with it though! I decided to use wheels instead :wink: