How do I make it so that the car's position on the road stays the same as before?

The difference between road parts is 2, there are 45 of them in total. It touched most of them(39), but skipped some here and there.

 02:23:59.630   ▶ 0, 2, 0 (x2)  -  Server - AIDrive:84
  02:24:00.245  0, 6, 0  -  Server - AIDrive:84
  02:24:00.563  0, 8, 0  -  Server - AIDrive:84
  02:24:00.880  0, 10, 0  -  Server - AIDrive:84
  02:24:01.180  0, 12, 0  -  Server - AIDrive:84
  02:24:01.496  0, 14, 0  -  Server - AIDrive:84
  02:24:01.812  0, 16, 0  -  Server - AIDrive:84
  02:24:02.129   ▶ 0, 18, 0 (x2)  -  Server - AIDrive:84
  02:24:02.763  0, 22, 0  -  Server - AIDrive:84
  02:24:03.079   ▶ 0, 24, 0 (x2)  -  Server - AIDrive:84
  02:24:03.646  0, 26, 0  -  Server - AIDrive:84
  02:24:04.012  0, 30, 0  -  Server - AIDrive:84
  02:24:04.330  0, 32, 0  -  Server - AIDrive:84
  02:24:04.646  0, 34, 0  -  Server - AIDrive:84
  02:24:04.963  0, 36, 0  -  Server - AIDrive:84
  02:24:05.279  0, 38, 0  -  Server - AIDrive:84
  02:24:05.597  0, 40, 0  -  Server - AIDrive:84
  02:24:05.912  0, 42, 0  -  Server - AIDrive:84
  02:24:06.196  0, 42, 0  -  Server - AIDrive:84
  02:24:06.546  0, 46, 0  -  Server - AIDrive:84
  02:24:06.862  0, 48, 0  -  Server - AIDrive:84
  02:24:07.178  0, 50, 0  -  Server - AIDrive:84
  02:24:07.496  0, 52, 0  -  Server - AIDrive:84
  02:24:07.812  0, 54, 0  -  Server - AIDrive:84
  02:24:08.129   ▶ 0, 56, 0 (x2)  -  Server - AIDrive:84
  02:24:08.762  0, 60, 0  -  Server - AIDrive:84
  02:24:09.079   ▶ 0, 62, 0 (x2)  -  Server - AIDrive:84
  02:24:09.712  0, 66, 0  -  Server - AIDrive:84
  02:24:10.029  0, 68, 0  -  Server - AIDrive:84
  02:24:10.329  0, 68, 0  -  Server - AIDrive:84
  02:24:10.612  0, 70, 0  -  Server - AIDrive:84
  02:24:10.995  0, 74, 0  -  Server - AIDrive:84
  02:24:11.311  0, 76, 0  -  Server - AIDrive:84
  02:24:11.630   ▶ 0, 78, 0 (x2)  -  Server - AIDrive:84
  02:24:12.195  0, 80, 0  -  Server - AIDrive:84
  02:24:12.579  0, 84, 0  -  Server - AIDrive:84
  02:24:12.912  0, 86, 0  -  Server - AIDrive:84
  02:24:13.230   ▶ 0, 88, 0 (x2)  -  Server - AIDrive:84
  02:24:13.796  0, 90, 0  -  Server - AIDrive:84

Maybe instead of get touching parts you can raycast to see if it hitting the floor just make a ray pointing downwards from a wheel or any where on the car (should be in the center for better checkings) and loop it like in the code you did above.

—Make sure to use raycast params to blacklist the car and anything else that you don’t want to be in the way
workspace:Raycast(origin,(origin-(origin-Vector3.new(0,25,0) ).Unit*length )

If you replay the video slowly you can see how it sometimes misses the parts that make the curve here and there which makes it offset from the position you want.

Just like with GetTouchingParts, the problem persisted.
(Video uploaded onto a hosting website because of uploading error)

game:GetService("RunService").Stepped:Connect(function()
local Origin = script.Parent.Parent.Wheels.FL.Position	
local Direction = Vector3.new(0,-5,0)
local Result = workspace:Raycast(Origin,Direction)  
local hit = Result.Instance
	if Result then
		print(Result)
	end	
	if hit.Material == Enum.Material.Concrete  and not Debounce then
		Debounce = true
		if hit.Orientation == Vector3.new(0,0,0) then
		else
			warn(hit.Orientation)
			local Dot = script.Parent.Parent.PrimaryPart.CFrame.RightVector:Dot(hit.CFrame.LookVector)
			script.Parent.Parent:SetPrimaryPartCFrame(CFrame.new(script.Parent.Parent.PrimaryPart.CFrame.Position)*CFrame.Angles(0,math.rad(hit.Orientation.Y),0))					
			if Dot < 0 then		
			  script.Parent.Parent:SetPrimaryPartCFrame(script.Parent.Parent.PrimaryPart.CFrame*CFrame.Angles(0,math.rad(-90),0)) --Left curve
			else
			  script.Parent.Parent:SetPrimaryPartCFrame(script.Parent.Parent.PrimaryPart.CFrame*CFrame.Angles(0,math.rad(90),0)) --Right curve
			end
		end
		wait(0.2)
		Debounce = false
	end		

end)

Your direction is off because the ray will go exactly to that Postion you need a vector unit instead of just a vector3 Direction-origin to get a vector unit with magnitude.

I get an “Unable to cast double to Vector3” error if I add ‘.Magnitude’ to the origin’s position.

All you need to do is subtract the two positions together without using .Magnitude or else it would error. Also you should put all the road parts in a folder or model if they aren’t already then after use the raycast params

local RayParams = RaycastParams.new()
RayParams.FilterType = Enum.RaycastFilterType.Whitelist
RayParams.FilterDescendantInstances = {Folder/Model}
—don’t know the names of the folder or model and the properties of the raycast Params so I might have misspelled them.

If I subtract them together, then I’d get 0, right? That would not work. And I tried that, and sure enough, it said “nil”.

I don’t think I need a whitelist.

Well your direction might be off because it is using the world’s vector3 so you need to offset the direction using the origin, and you do need params because rays only hit once which is why you are putting it on a loop.

And how would I offset the direction using the origin?

Also I get an “Unable to cast value to Objects” error.

game:GetService("RunService").Stepped:Connect(function()
local Params = RaycastParams.new()
Params.FilterType = Enum.RaycastFilterType.Whitelist
Params.FilterDescendantsInstances = workspace.Road
local Origin = script.Parent.Parent.Wheels.FL.Position	
local Direction = Vector3.new(0,-5,0)
local Result = workspace:Raycast(Origin,Direction,Params)  
local hit = Result.Instance
	if Result then
		print(Result)
	end	
	if hit.Material == Enum.Material.Concrete  and not Debounce then
		Debounce = true
		if hit.Orientation == Vector3.new(0,0,0) then
		else
			warn(hit.Orientation)
			local Dot = script.Parent.Parent.PrimaryPart.CFrame.RightVector:Dot(hit.CFrame.LookVector)
			script.Parent.Parent:SetPrimaryPartCFrame(CFrame.new(script.Parent.Parent.PrimaryPart.CFrame.Position)*CFrame.Angles(0,math.rad(hit.Orientation.Y),0))					
			if Dot < 0 then		
			  script.Parent.Parent:SetPrimaryPartCFrame(script.Parent.Parent.PrimaryPart.CFrame*CFrame.Angles(0,math.rad(-90),0)) --Left curve
			else
			  script.Parent.Parent:SetPrimaryPartCFrame(script.Parent.Parent.PrimaryPart.CFrame*CFrame.Angles(0,math.rad(90),0)) --Right curve
			end
		end
		wait(0.2)
		Debounce = false
	end		

end)

Yeah because you are supposed to put the objects in a table so it can cast the objects.

From one of my posts I gave you an example;

( (origin+Vector3.new(0,-25,0)-origin ).Unit*length 

I get an error:
Workspace.AICars.Car1.A-Chassis Tune.AIDriveNew:85: attempt to perform arithmetic (mul) on Vector3 and nil

local Direction = (Origin + Vector3.new(0,-5,0)-Origin).Unit*length

If I change “length” to “Origin.Magnitude”, then there’s no error, but the position issue persists.

The length depends on how far you want it to detect I believe the max is around 5000 so just set it to like 25 because the car won’t be that far from the ground. Also you forgot to add a parenthesis to get the offset the one between the vector3.new and the - origin like so;


local Direction = ( (Origin + Vector3.new(0,-5,0) )-Origin).Unit*length

I set the length to 25, but the position issue is still there.

I think you just need to delete the -origin part and leave the rest.

No effect on the position, just like before.

local Direction = ( (Vector3.new(0,-5,0) )).Unit*25

maybe try playing with the degrees of rotation sounds like a pretty simple fix just make them a little higher