Stop Skidding By Cutting Movement On One Axis

So I’m making a car but I don’t want it to skid. I’m thinking about achieving this by cutting movement from the left and right of the car. I managed to get the car’s velocity relative to the orientation.

https://i.gyazo.com/d351cc125d3f0edb153a945f4f06f943.mp4

My question is: How do I know how much force to apply in the opposite direction using the relative velocity to balance the forces so the car cannot move left and right?

1 Like

Are you trying to disable left and right skidding while driving forward and turning or are you trying to disable the ability for left and right arrow keys to move the car left and right?

I am confused, because the video appears to be a car being driven to the right presumably by pressing the right arrow key/D key.

1 Like

I’m making it go left and right by hitting A and D to simulate skidding.

I want to know how I can calculate a counter force (friction) to cancel this effect out, for scenarios where the skidding is natural (on a ramp)

1 Like

You can use Part.Velocity of the main part and find how much of this velocity is in the horizontal direction by calling Part.CFrame:ToObjectSpace(Part.Velocity). The x component will tell you how great this force is. The force needed to stop this movement is precisely Part.CFrame:ToWorldSpace(Vector3.new(vehicleMass * -x, 0, 0)). However, this may be very slightly off due to the imprecise nature of physics systems. You can find the vehicleMass by adding up the Part:GetMass() of all the parts on the vehicle. The force will need to be applied at the center of mass of the vehicle. If you use a VectorForce object, it has a ApplyAtCenterOfMass property that will do this for you.

It should be noted that dealing with the physics system like this is difficult, because if this force is applied for too long then it will actually push the vehicle even faster in the opposite direction. Then, once you update the force it will be even stronger in the opposite direction. The result is a vehicle that has a spasm and flies off the map. You may want to apply a dampening scalar constant to the force, depending on how often you update the force and how fast Roblox’s physics system is currently running. (yuck, ikr)

2 Likes

Keep in mind that my vehicles are floating and nothing is really in contact with the floor.

Given that, cant I just apply a counter friction (ideally in each wheel) using the normal of the surface that is on (through raycast) and the weight of the vehicle (i already calculate this).

This is static friction btw.

image

I simply calculate m * g * sin(theta) and apply it the other way.

2 Likes

Yeah, should work. Have you tried it already and had issues with it? If you are calculating forces per a wheel, you may want to consider the center of gravity of the vehicle to find how much weight is being put on each wheel, especially if the vehicles could carry heavy cargo. Also if the force isn’t updated frequently enough it could cause the vehicle to rotate.

The picture you posted is to calculate the components of the weight to find out how much force is being applied horizontal to the surface. As it turns out, to find the angle between the surface normal and the weight (which is the same theta as shown in the image), it is easiest to use their dot product. Using a unit surface normal and the weight vector, this is the projection of the weight vector on the normal (the mg cos theta seen in the image). To find the projection on the surface, simply subtract out the projection on the normal from the original weight vector.

Static friction keeps and object from moving, but once the horizontal force becomes too great it starts moving and kinetic or sliding friction takes over. Kinetic force simply subtracts a the normal force (how hard the two surfaces are touching) scaled by the materials’ friction constants from the horizontal force. Once this kinetic friction force becomes greater than the horizontal force, the object stops and static friction takes over again (electrons have time to form stronger bonds).

Yeah I’m using dot product. What I’m struggling is in what direction to apply the force.

local weight = Vector3.new(0, mass * gravity, 0) --pointing upwards

		local ray = Ray.new(carModel.PrimaryPart.CFrame.p, carModel.PrimaryPart.CFrame:VectorToWorldSpace(Vector3.new(0, -1, 0)) * 10)
		local hit, pos, normal = workspace:FindPartOnRayWithIgnoreList(ray, {carModel, workspace.Ignore})
		local theta = math.acos(Vector3.new(0, 1, 0):Dot(normal))
		
		local normalForce = weight * math.cos(theta)
		local pull = weight * math.sin(theta)
		local counterForce = (Vector3.new(0, 1, 0) - normal) * pull.Magnitude
		frictionForce.Force = counterForce
--friction force is relative to world and ApplyAtCenterMass = true	

https://i.gyazo.com/5432212beb5acbe2e94b92ad5983f0be.mp4

It slides slower but it doesn’t completely stop like I was expecting it to.

A couple things. First, that code looks right but can be simplified a bit:

local weight = Vector3.new(0, -mass * gravity, 0)

local ray = Ray.new(carModel.PrimaryPart.CFrame.p, carModel.PrimaryPart.CFrame:VectorToWorldSpace(Vector3.new(0, -1, 0)) * 10)
local hit, pos, normal = workspace:FindPartOnRayWithIgnoreList(ray, {carModel, workspace.Ignore})
		
local normalForce = weight:Dot(normal)
local pullForce = weight - normalForce
frictionForce.Force = -pullForce

Now, as to what may be causing the sliding… This only counters any new force being applied but if the vehicle was in motion before the force was applied then it should remain in motion but without accelerating. This seems like it may be the case because it is moving at a constant velocity.

This might be explained if the computations were calculated after the car started moving, a slight dampening was applied to the force, or the force was applied later after a physics step or two.

It might be solved by either applying the counter force before the slide starts, or if it is a physics system quark like explained above then applying a slight force to stop this movement.

Can you do something with velocity? I feel liek that would work tbh

EDIT: I think theres something wrong with the code tbh…

Yeah, you apply a force in the opposite direction just long enough to consume the velocity. velocity is studs/s and force is studs/s^2 so to apply a force for 1 second would change the velocity by the magnitude of the force (roughly).

Does my code perform the same as yours? I don’t see any problems with it unless the problem lies in code that hasn’t been shown yet.

There’s an error;


local normalForce = weight:Dot(normal)

doesn’t produce a vector

Ah, should be this:

local normalForce = normal * weight:Dot(normal)

Checking it again, the rest looks good. The math behind it is solid and the Lua code for it seems bug free.

Work’s like a charm,

had the correct idea but the numbers got to me

Not only did you have the correct idea, but I still believe your code is correct too. Does this behave the same? It may have been a rounding error.

local weight = Vector3.new(0, mass * gravity, 0) --pointing upwards

local ray = Ray.new(carModel.PrimaryPart.CFrame.p, carModel.PrimaryPart.CFrame:VectorToWorldSpace(Vector3.new(0, -1, 0)) * 10)
local hit, pos, normal = workspace:FindPartOnRayWithIgnoreList(ray, {carModel, workspace.Ignore})
local theta = math.acos(Vector3.new(0, 1, 0):Dot(normal))
frictionForce.Force = -weight.Magnitude * math.sin(theta)

image

-.- You caught me again, I keep doing that:

frictionForce.Force = weight * -math.sin(theta)

Yeah I tried it, it slides now.

Tbh I don’t know why mine isn’t working

EDIT: I think I know why, hold up.

local ray = Ray.new(carModel.PrimaryPart.CFrame.p, carModel.PrimaryPart.CFrame:VectorToWorldSpace(Vector3.new(0, -1, 0)) * 10)
local hit, pos, normal = workspace:FindPartOnRayWithIgnoreList(ray, {carModel, workspace.Ignore})
local theta = math.acos(Vector3.new(0, 1, 0):Dot(normal))
local normalForce = normal * math.cos(theta) * weight.Magnitude
local pullForce = weight - normalForce
frictionForce.Force = pullForce

This worked

I found out why as well, and was about to post it xD

From your code:

local normalForce = weight * math.cos(theta)
local pull = weight * math.sin(theta)

for the normalForce that should be -normal instead of weight. For the pull it should be Vector3.new(0, 1, 0) - normal. Also, the counterForce direction needs to be a unit vector otherwise it skews the strength of the force. The code ends up looking like this:

local weight = Vector3.new(0, mass * gravity, 0) --pointing upwards

local ray = Ray.new(carModel.PrimaryPart.CFrame.p, carModel.PrimaryPart.CFrame:VectorToWorldSpace(Vector3.new(0, -1, 0)) * 10)
local hit, pos, normal = workspace:FindPartOnRayWithIgnoreList(ray, {carModel, workspace.Ignore})
local theta = math.acos(Vector3.new(0, 1, 0):Dot(normal))

local normalForce = normal * math.cos(theta)
local counterForce = (Vector3.new(0, 1, 0) - normal).Unit * math.sin(theta)
frictionForce.Force = counterForce

By not using the (Vector3.new(0, 1, 0) - normal) calculation for the counter direction, I avoided the issue of having to normalize it. By substituting theta I avoided having to decide which vector the normal force was along.

1 Like

Hm, now I need to apply it to all the wheels lol

EDIT: And restrict movement only for left and right of the car (wheels don’t turn yet)