Finding appropriate value to turn/throttle a vehicle to reach a target position

Greetings!

I’m trying to create a pathfinding car that uses a VehicleSeat and the Throttle/Turn values on it to make its way to a position. I’ve hooked it up to Pathfinding, however I cannot seem to get this to work. The car just indefinitely does donuts around the first point!

local des = workspace.Part.Position
local pf = game:GetService('PathfindingService')


local path = pf:CreatePath()
path:ComputeAsync(script.Parent.Position, des)
local ways = path:GetWaypoints()

function calculateAngle(target, myPos)
	local rot = math.atan2(target.Z-myPos.Z,target.X-myPos.X)
	local desiredAngle = math.deg(rot)+script.Parent.Orientation.Y+90
	
	if desiredAngle > 180 then
		desiredAngle = -(360-desiredAngle)
	elseif desiredAngle <-180 then
		desiredAngle = 360+desiredAngle
	end
	return desiredAngle
end

script.Parent.Throttle = 25

for i, w in pairs(ways) do
	local part = Instance.new("Part")
	part.Shape = "Ball"
	part.Material = "Neon"
	part.Size = Vector3.new(0.6, 0.6, 0.6)
	part.Position = w.Position
	part.Anchored = true
	part.CanCollide = false
	part.Parent = game.Workspace
	spawn(function()
		
		local currentPos = script.Parent.Position
		local currentTarget = w.Position
		local angle = calculateAngle(currentTarget, currentPos)
		while calculateAngle(currentTarget, currentPos) >= 90 do
			script.Parent.Steer = 1
			wait()
		end
		while calculateAngle(currentTarget, currentPos) <= 90 do
			script.Parent.Steer = -1
			wait()
		end
		script.Parent.Steer = 0
	end)
end

image

1 Like

You are not waiting until you reached your waypoint.

I’m a little bit confused on this. Could you provide some code?

You should steer to the waypoint and then wait until the car gets there before you can go to the next waypoint. At the moment, you are immediately going through your for loop without reaching your waypoints. The car is randomly steering, because you multithreaded this:

I unmulti threaded and now my code looks like this.

local des = workspace.Part.Position
local pf = game:GetService('PathfindingService')


local path = pf:CreatePath()
path:ComputeAsync(script.Parent.Position, des)
local ways = path:GetWaypoints()

function calculateAngle(target, myPos)
	local rot = math.atan2(target.Z-myPos.Z,target.X-myPos.X)
	local desiredAngle = math.deg(rot)+script.Parent.Orientation.Y+90
	
	if desiredAngle > 180 then
		desiredAngle = -(360-desiredAngle)
	elseif desiredAngle <-180 then
		desiredAngle = 360+desiredAngle
	end
	return desiredAngle
end


for i, w in pairs(ways) do
	local part = Instance.new("Part")
	part.Shape = "Ball"
	part.Material = "Neon"
	part.Size = Vector3.new(0.6, 0.6, 0.6)
	part.Position = w.Position
	part.Anchored = true
	part.CanCollide = false
	part.Parent = game.Workspace
	script.Parent.Throttle = 25
		
	local currentPos = script.Parent.Position
	local currentTarget = w.Position
	local angle = calculateAngle(currentTarget, currentPos)
		while calculateAngle(currentTarget, currentPos) >= 90 do
			script.Parent.Steer = 1
			wait()
		end
		while calculateAngle(currentTarget, currentPos) <= 90 do
			script.Parent.Steer = -1
			wait()
		end
		script.Parent.Steer = 0
end

However, my car seems to jiggle back and forth from going straight to toward the waypoint, etc

Try to do something like this in your for loop:

repeat
    steerTowardsWaypoint(waypoint)
    RunService.Heartbeat:Wait()
until waypointIsReached(waypoint)

I only wrote the logic here. If you can properly define steerTowardsWaypoint and waypointIsReached, then it should work (fingers crossed :3).

local des = workspace.Part.Position
local pf = game:GetService('PathfindingService')


local path = pf:CreatePath()
path:ComputeAsync(script.Parent.Position, des)
local ways = path:GetWaypoints()

function calculateAngle(target, myPos)
	local rot = math.atan2(target.Z-myPos.Z,target.X-myPos.X)
	local desiredAngle = math.deg(rot)+script.Parent.Orientation.Y+90
	
	if desiredAngle > 180 then
		desiredAngle = -(360-desiredAngle)
	elseif desiredAngle <-180 then
		desiredAngle = 360+desiredAngle
	end
	return desiredAngle
end

function steer(currentTarget)
	local currentPos = script.Parent.Position
	local angle = calculateAngle(currentTarget, currentPos)
	while calculateAngle(currentTarget, currentPos) >= 90 do
		script.Parent.Steer = 1
		wait()
	end
	while calculateAngle(currentTarget, currentPos) <= 90 do
		script.Parent.Steer = -1
		wait()
	end
	script.Parent.Steer = 0
end

for i, w in pairs(ways) do
	local part = Instance.new("Part")
	part.Shape = "Ball"
	part.Material = "Neon"
	part.Size = Vector3.new(0.6, 0.6, 0.6)
	part.Position = w.Position
	part.Anchored = true
	part.CanCollide = false
	part.Parent = game.Workspace
	script.Parent.Throttle = 25
	local currentTarget = w.Position
	repeat
		steer(currentTarget)
		game:GetService('RunService').Heartbeat:Wait()
		print((script.Parent.Position - currentTarget).Magnitude)
	until (script.Parent.Position - currentTarget).Magnitude <= 10
end

This is what I did. It sort of works but the car reaches the waypoint it continues doing donuts around it

Maybe you can try to increase 10? I do not know how big your car is.

I did. I just increased it to 25 and it drove off the map

You do not need the while loop anymore here, since repeat keeps calling steer on each Heartbeat. Instead you can just set the Steer once in the function.

Can you try this? Also, it should steer more smoothly now.

I’m trying it now. It appears to reach the first waypoint and then stop steering all together until its off the map.

local des = workspace.Part.Position
local pf = game:GetService('PathfindingService')


local path = pf:CreatePath()
path:ComputeAsync(script.Parent.Position, des)
local ways = path:GetWaypoints()

function calculateAngle(target, myPos)
	local rot = math.atan2(target.Z-myPos.Z,target.X-myPos.X)
	local desiredAngle = math.deg(rot)+script.Parent.Orientation.Y+90
	
	if desiredAngle > 180 then
		desiredAngle = -(360-desiredAngle)
	elseif desiredAngle <-180 then
		desiredAngle = 360+desiredAngle
	end
	return desiredAngle
end

function steer(currentTarget)
	local currentPos = script.Parent.Position
	local angle = calculateAngle(currentTarget, currentPos) - 90
	script.Parent.Steer = math.clamp(angle, -90, 90) / 90
end

for i, w in pairs(ways) do
	local part = Instance.new("Part")
	part.Shape = "Ball"
	part.Material = "Neon"
	part.Size = Vector3.new(0.6, 0.6, 0.6)
	part.Position = w.Position
	part.Anchored = true
	part.CanCollide = false
	part.Parent = game.Workspace
	script.Parent.Throttle = 25
	local currentTarget = w.Position
	repeat
		steer(currentTarget)
		game:GetService('RunService').Heartbeat:Wait()
		print((script.Parent.Position - currentTarget).Magnitude)
	until (script.Parent.Position - currentTarget).Magnitude <= 25
	script.Parent.Steer = 0
end

Updated script

Can you share the vehicle in an empty project? I am getting really confused.

place.rbxl (27.9 KB)

I found out that the angle function did not behave correctly. This is what I have now:

function getAngle(cframe, vector)
	local projectedVector = cframe:PointToObjectSpace(vector)-- * Vector3.new(1, 0, 1)
	local angle = math.atan2(projectedVector.Z, projectedVector.X)
	return angle
end

function steer(currentTarget)
	local currentPos = script.Parent.CFrame * CFrame.Angles(0, math.pi / 2, 0)
	local angle = math.deg(getAngle(currentPos, currentTarget))
	script.Parent.Steer = math.sign(angle)
end

Now it will always aim for the waypoints.

It appeared to work at first. It takes a wild right and doesnt come back…

I actually just lowered the threshold I created from 60 to 25 and it worked. Thank you so much!

1 Like

Hey. I did some testing and the car is now aggressively jiggling. It seems it is trying to constantly point itself towards the goal. How would I implement something like a margin of error for the direction of the car to the waypoint?

1 Like

The problem is that it always steers completely. Steer is either -1, 0 or 1.

You should try to find a formula that can make it steer smoothly depending on the angle. It should steer less when the angle is tight and more when the angle is big.