# (Help) Custom Wheel Collider Friction Being Applied Incorrectly

So I have this custom wheel collider that I’m working on (still very early) and I have a very simple friction model (basically I get the local velocity of the vehicle and multiply it by a set friction coefficient and apply that to the opposite direction the vehicle is moving). The problem is, it only works in the negative X and Y world coordinates, meaning, if the vehicle turns more than 180 degrees in the Y axis (the “up” axis), friction is ADDING to my overall speed rather than subtracting. I’ve tried multiple approaches of fixing this like multiplying the coefficient of friction by -1, or getting the absolute value of the localWheelVelocity, and I’ve checked that the direction of force being applied is the object’s transform (transform.forward / right) and not the world vector (Vector3.forward / right). Help would be greatly appreciated!

Here’s the code (you can ignore the commented-out code):

``````using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Wheels : MonoBehaviour
{
private Rigidbody rb;
public float restLength;
public float springTravel;
public float springStiffness;
public float damperStiffness;
private float minLength;
private float maxLength;
private float lastLength;
public float springLength;
private float springForce;
private float damperForce;
private float springVelocity;
private Vector3 suspensionForce;
private Vector3 localWheelVelocity;
public Vector3 localWheelVelocityMagnitude;
public float forceX;
public float forceY;
public Vector2 wheelVelocity;
public Vector2 idealAngularVelocity;
private float vehicleWeight;
private Vector3 cosOfIncline;
private Vector3 normalForce;
private float staticCoefficientOfFriction;
private float kineticCoefficientOfFriction;
public Vector3 friction;
public Vector3 frictionForce;
public float enginePower;
public float totalVehicleVelocity;
public PhysicMaterial frictonOfObject;
public Vector2 torqueNM;
public float airDensity;
public float dragCoefficient;
public Vector2 airDrag;
public float rollingResistanceCoefficient;
public Vector2 rollingResistance;
[HideInInspector] public RaycastHit hit;
// Start is called before the first frame update
void Awake()
{
rb = transform.root.GetComponent<Rigidbody>();
minLength = restLength - springTravel;
maxLength = restLength + springTravel;
vehicleWeight = rb.mass * -Physics.gravity.y;
}
// Update is called once per frame
void FixedUpdate()
{
totalVehicleVelocity = Mathf.Abs(localWheelVelocity.x + localWheelVelocity.y + localWheelVelocity.z);
//airDrag = new Vector2((dragCoefficient * airDensity * rb.velocity.x * totalVehicleVelocity) / 2, (dragCoefficient * airDensity * rb.velocity.z * totalVehicleVelocity) / 2);
//rollingResistance = new Vector2(velocityX * rollingResistanceCoefficient, velocityY * rollingResistanceCoefficient);
if (Physics.Raycast(transform.position, -transform.up, out hit, (maxLength + wheelRadius)))
{
localWheelVelocity = transform.InverseTransformDirection(rb.GetPointVelocity(hit.point));
localWheelVelocityMagnitude = new Vector3(Mathf.Abs(localWheelVelocity.x), Mathf.Abs(localWheelVelocity.y), Mathf.Abs(localWheelVelocity.z));
frictonOfObject = hit.transform.gameObject.GetComponent<Collider>().material;
if (frictonOfObject != null)
{
staticCoefficientOfFriction = frictonOfObject.staticFriction * 100;
kineticCoefficientOfFriction = frictonOfObject.dynamicFriction * 100;
}
else
{
staticCoefficientOfFriction = 0.6f * 100;
kineticCoefficientOfFriction = 0.55f * 100;
}
//cosOfIncline = new Vector3(Mathf.Cos(hit.normal.x), Mathf.Cos(hit.normal.y), Mathf.Cos(hit.normal.z));
//normalForce = new Vector3((cosOfIncline.x * (vehicleWeight / 4)), (cosOfIncline.y * (vehicleWeight / 4)), (cosOfIncline.z * (vehicleWeight / 4)));
//if (totalVehicleVelocity <= 0.1f)
//{
//    friction = normalForce * staticCoefficientOfFriction;
//}
//else
//{
//    friction = normalForce * kineticCoefficientOfFriction;
//}
lastLength = springLength;
springLength = Mathf.Clamp(springLength, minLength, maxLength);
springVelocity = (lastLength - springLength) / Time.fixedDeltaTime;
springForce = springStiffness * (restLength - springLength);
damperForce = damperStiffness * springVelocity;
suspensionForce = (springForce + damperForce) * transform.up;
forceX = Input.GetAxis("Vertical") * enginePower;
forceY = localWheelVelocity.x * enginePower;
if (totalVehicleVelocity >= 0.1f)
{
frictionForce = localWheelVelocityMagnitude * kineticCoefficientOfFriction;
}
else
{
frictionForce = localWheelVelocityMagnitude * staticCoefficientOfFriction;
}
rb.AddForceAtPosition((forceX * -transform.forward) + (forceY * -transform.right), hit.point);
}

else
{
springLength = maxLength;
}
}
}
``````

I’ll be honest with you, I don’t know what all is going on in there, that is a metric TON of code to do what should be very simple. First of all, I recommend disentangling your input from your processing and breaking it into clearly-separated stages of processing:

1. gather ALL input into temp variables

2. apply scaling to that input, as appropriate

3. apply the input forces to the rigidbodies

4. study the speeds and decide how you want to drag

5. apply the drag forces.

Breaking stuff up into stages lets you turn off certain stages as well as use Debug.Log() to output intermediate values at each stage, so you can immediately see when a computation is wrong.

I’m also not sure why you are doing suspension forces; I thought that was the point of the wheel collider?

Well more than half of the variables that I have listed have been from previous attempts (just haven’t commented them out yet since I’ll elaborate on the friction forces with air drag, rolling resistance, slip, etc once I get the basics working), so really, all I’m getting is the localwheelvelocity from a raycast downwards and then applying suspension and friction forces accordingly. Also, since this wheel collider is from scratch, I’ll have to make my own suspension forces (I’m not adding features like improved friction to the existing wheel collider, I’m starting from the ground up to make a more realistic one). Just want to know why the friction forces are being applied as if I were applying them using world position rather than local position (when I’m clearly using local position as seen by my use of transform.forward and -transform.right rather than their vector3 counterparts).

That’s as may be but realize this might be covering up genuine issues.

For instance:

Line 102 uses ForceX and ForceY, which I would intuit are cartesian orthogonal control inputs. You appear to be correctly multiplying them by local vectors.

However there is this conflict:

• line 90 sets ForceX according to vertical axis input and engine power
• line 91 sets ForceY according to localWheelVelocity.x times engine power

Without even going any deeper into localWheelVelocity, I’m already baffled by the above two lines.

If I go into localWheelVelocity I see you “de-transforming” the direction of some velocity vector (which I assume brings it back into world coordinates)… yet the value is stored in something called localWheelVelocity… what?!

That’s what I mean by …

Well first off, although forceX is supposed to be for directly moving the vehicle forwards, forceY is used to help with steering (since I have another script that’s responsible for steering) the reason I multiplied it by engine power is because that value was conincidentally the exact value I needed, but I will definitely put in a proper value for forceY so there’s no confusion. The reason it looks like I’m converting localwheelvelocity to world space is because I need the x and z variables from that vector3, so I can’t set it as a vector2, where I wouldn’t need to convert it. Only way that I know of to use the vector2 in this way would be to make a vector2 called actualWheelVelocity, set it’s x to localwheelvelocity.x, set it’s y to local wheel velocity.z, and then extract forceX and forceY from that. Hope this clears some info up!

It seems like I didn’t understand exactly what you meant when you said “de-transforming” (excuse my stupidity), because I fixed the issue by transforming friction force back to local space before I apply the force using:

``````transform.TransformDirection(localWheelVelocity)
``````

Thanks for helping me out! Sorry my bird brain couldn’t understand at first!