How To Make A Car Traffic?

How to make a car traffic?

I want to make a car that follows a road automatically, it detects the streets, puts a path through the whole road. I want like the video shown: https://www.youtube.com/watch?v=Qqapt_AbPv4

This is my code:

local car = workspace.Cars.Car4.Item
local carPrimaryPart = car["primary part"]
local CollectionService = game:GetService("CollectionService")
local PathFindingService = game:GetService("PathfindingService")

local streets = CollectionService:GetTagged("street")

local streetsFinishedDriving = {}
local hasFinishedDriving = false

local function findCurrentStreet()
	local raycastOrigin = carPrimaryPart.Position
	local raycastDirection = Vector3.new(0, -10000, 0) -- Straight down

	local raycastParams = RaycastParams.new()
	raycastParams.FilterDescendantsInstances = {streets} -- Ensure streets are grouped
	raycastParams.FilterType = Enum.RaycastFilterType.Include

	local raycastResult = workspace:Raycast(raycastOrigin, raycastDirection, raycastParams)

	if raycastResult then
		local street = raycastResult.Instance
		if street then
			print("Street found:", street)
			table.insert(streetsFinishedDriving, street)
			return street
		else
			print("Hit something else:", raycastResult.Instance.Name)
		end
	else
		print("Raycast did not hit anything.")
	end

	return nil -- Explicitly return nil if no street is found
end

local function findNextStreet(currentStreet)
	local closestStreet = nil
	local shortestDistance = math.huge

	for _, street in streets do
		if street ~= currentStreet then
			print("current street: ", currentStreet,"street found: ", street)
			local distance = (street.Position - currentStreet.Position).Magnitude

			local displacement = street.Position - carPrimaryPart.Position
			local direction = carPrimaryPart.CFrame.LookVector
			print(direction)

			local dot = displacement:Dot(direction)
			if distance < shortestDistance and dot >= 0.2 then
				print("dot: ", dot, "closest street: ", closestStreet, "shortest distance: ", shortestDistance)
				for i, finishedDrivingstreet in streetsFinishedDriving do
					print(finishedDrivingstreet)
					if not (finishedDrivingstreet == street) then
						print("Already driving this street")
						closestStreet = street
						shortestDistance = distance
						
					end
				end


			end
		end

	end

	return closestStreet
end

local function smoothTurn(carPart, targetPoint)
	local currentCFrame = carPart.CFrame
	local targetDirection = (targetPoint - carPart.Position).Unit
	local targetCFrame = CFrame.lookAt(carPart.Position, carPart.Position + targetDirection)

	local deltaRotation = targetCFrame:ToObjectSpace(currentCFrame)
	return deltaRotation
end


local function driveTo(startingPos, endingPos)
	local path = PathFindingService:CreatePath()

	path:ComputeAsync(startingPos, endingPos)

	carPrimaryPart:SetNetworkOwner(nil)

	local wayPoints = path:GetWaypoints()
	local waypoint = 2
	local moveToConnection

	local linearVelocity = Instance.new("LinearVelocity")
	linearVelocity.MaxForce = math.huge
	linearVelocity.RelativeTo = Enum.ActuatorRelativeTo.World
	linearVelocity.Enabled = true


	local angularVelocity = Instance.new("AngularVelocity")
	angularVelocity.MaxTorque = math.huge

	local attachment = Instance.new("Attachment")
	attachment.Parent = carPrimaryPart

	linearVelocity.Attachment0 = attachment
	linearVelocity.Parent = carPrimaryPart

	angularVelocity.Attachment0 = attachment

	for i, waypoint in ipairs(wayPoints) do
		hasFinishedDriving = false

		--if not hasRun or (carPrimaryPart.Position - waypoint.Position).Magnitude < 1 and 2 < #wayPoints then
		
		local direction = (waypoint.Position - carPrimaryPart.Position).Unit
		linearVelocity.VectorVelocity = direction * carPrimaryPart.AssemblyMass * 0.06

		--angularVelocity.AngularVelocity = smoothTurn(carPrimaryPart, waypoint.Position).LookVector * 15

		--end

	end
	hasFinishedDriving = true
	return hasFinishedDriving
end

	while true do
	local currentStreet = findCurrentStreet()
	if currentStreet then
		print("Current street detected: ", currentStreet)

		local destination = findNextStreet(currentStreet)
		if destination then
			print("Destination detected: ", destination)
			local finishedDriving = driveTo(carPrimaryPart.Position, destination.Position)
			if finishedDriving then 
				driveTo(carPrimaryPart.Position, destination.Position)
			end
		else
			warn("No valid destination found!")
		end
	else
		warn("Failed to detect a current street.")
	end

	-- Optionally wait before starting again
	task.wait(1)
	end

Explanation: I detect the current street(raycasting down from the car) and the closest street to the current street, and then just create a path to that closest street, and attach a linear velocity to the car to go to the waypoints of the path to the street.

It can make the car move along the street but most of the time its just back and forth between current and closest street, it also can’t turn, and its extremely unreliable since it keeps creating pathes in real time while driving.

Any help is appreciated!

5 Likes

Ello!

Does this work?

local car = workspace.Cars.Car4.Item
local carPrimaryPart = car["primary part"]
local CollectionService = game:GetService("CollectionService")
local PathFindingService = game:GetService("PathfindingService")

local streets = CollectionService:GetTagged("street")
local hasFinishedDriving = false
local currentDestination = nil

local function findCurrentStreet()
    local raycastParams = RaycastParams.new()
    raycastParams.FilterDescendantsInstances = streets
    raycastParams.FilterType = Enum.RaycastFilterType.Include

    local raycastResult = workspace:Raycast(carPrimaryPart.Position, Vector3.new(0, -10000, 0), raycastParams)
    return raycastResult and raycastResult.Instance or nil
end

local function findNextStreet(currentStreet)
    local closestStreet = nil
    local shortestDistance = math.huge

    for _, street in ipairs(streets) do
        if street ~= currentStreet then
            local distance = (street.Position - carPrimaryPart.Position).Magnitude
            if distance < shortestDistance then
                closestStreet = street
                shortestDistance = distance
            end
        end
    end

    return closestStreet
end

local function smoothTurn(targetDirection)
    local currentCFrame = carPrimaryPart.CFrame
    local targetCFrame = CFrame.lookAt(carPrimaryPart.Position, carPrimaryPart.Position + targetDirection)
    local rotation = currentCFrame:ToObjectSpace(targetCFrame).Rotation
    carPrimaryPart.CFrame = carPrimaryPart.CFrame * rotation
end

local function driveTo(destination)
    hasFinishedDriving = false

    local path = PathFindingService:CreatePath()
    path:ComputeAsync(carPrimaryPart.Position, destination.Position)

    local waypoints = path:GetWaypoints()
    if #waypoints == 0 then
        warn("Pathfinding failed!")
        return
    end

    for _, waypoint in ipairs(waypoints) do
        local direction = (waypoint.Position - carPrimaryPart.Position).Unit
        smoothTurn(direction)

        local linearVelocity = Instance.new("LinearVelocity")
        linearVelocity.MaxForce = math.huge
        linearVelocity.RelativeTo = Enum.ActuatorRelativeTo.World
        linearVelocity.Velocity = direction * 10 -- Adjust speed as needed
        linearVelocity.Parent = carPrimaryPart

        while (carPrimaryPart.Position - waypoint.Position).Magnitude > 1 do
            task.wait()
        end

        linearVelocity:Destroy()
    end

    hasFinishedDriving = true
end

while true do
    if not hasFinishedDriving or not currentDestination then
        local currentStreet = findCurrentStreet()
        if currentStreet then
            local nextStreet = findNextStreet(currentStreet)
            if nextStreet then
                currentDestination = nextStreet
                driveTo(nextStreet)
            else
                warn("No next street found!")
            end
        else
            warn("Current street not detected!")
        end
    end

    task.wait(1) -- Prevent infinite loop lockup
end

2 Likes

Doesn’t really work and i don’t see any changes, the car just floats and i’m not sure why.

2 Likes

Have you tried using pathfinding links and attachments?
I think this other forum is more of what you should be looking at to help solve your issue

[Full Release] Introducing the Pathfinding Links for NPCs - Updates / Announcements - Developer Forum | Roblox

1 Like

how would i go about using pathfinding attachments in order to make the car drive on the road endlessly?

2 Likes

Hi there,

I’ve put together two scripts for you. They’re probably a bit over the top for what you needed, but I’ve tested them, and they work great—very smooth. There are a few optional error checks included to help ensure everything is set up correctly. If you prefer, you can remove those.
For example, these lines will alert you if the waypoints folder is missing or empty:

if not waypointsFolder then
	error("Waypoints folder 'selfDrivingWaypoints' not found!")
	return
end


if #waypoints == 0 then
	error("No waypoints found in 'selfDrivingWaypoints'!")
	return
end

Important Note: Make sure your waypoints are set up properly and that the folder name in the script matches your actual folder name:

local waypointsFolder = workspace:FindFirstChild("selfDrivingWaypoints")

Here’s an example of how the setup should look. You can use a car from the toolbox, delete all its original scripts, and paste in the two scripts I’ve provided below.

Script 1: Self-Driving Script

This script should be placed inside the car:

---THIS SCRIPT GOES IN THE CAR
local PathfindingService = game:GetService("PathfindingService")
local RunService = game:GetService("RunService")
local Effects = require(script.Effects) -- Import the Effects module

local car = script.Parent -- The car model
local waypointsFolder = workspace:FindFirstChild("selfDrivingWaypoints")
local waypoints = waypointsFolder:GetChildren()

-- Check if waypointsFolder exists
if not waypointsFolder then
	error("Waypoints folder 'selfDrivingWaypoints' not found!")
	return
end


if #waypoints == 0 then
	error("No waypoints found in 'selfDrivingWaypoints'!")
	return
end

-- Sort waypoints by name
table.sort(waypoints, function(a, b)
	return tonumber(a.Name) < tonumber(b.Name)
end)

local speed = 25 -- Adjust car speed
local wheelRotationSpeed = 140 -- Adjust for wheel speed
local steeringSpeed = 0.1 -- Adjust for smooth turning
local currentWaypoint = 1

-- Initialize the Effects module
local effectsHandler = Effects.new(car.Chassis, car.Effects, car)
effectsHandler:Enable()

-- Function to move the car towards a target waypoint
local function moveTowardsTarget(targetPosition)
	local reached = false
	effectsHandler:SetThrottleEnabled(true) -- Enable throttle (engine and sound effects)

	while not reached do
		local deltaTime = RunService.Heartbeat:Wait()

		-- Flatten the target position to ignore height differences
		local carPosition = car.PrimaryPart.Position
		local flatTargetPosition = Vector3.new(targetPosition.X, carPosition.Y, targetPosition.Z)

		-- Calculate direction and target orientation
		local direction = (flatTargetPosition - carPosition).Unit
		local targetCFrame = CFrame.lookAt(carPosition, carPosition + direction)

		-- Gradually turn the car towards the target
		car:SetPrimaryPartCFrame(car.PrimaryPart.CFrame:Lerp(targetCFrame, steeringSpeed))

		-- Move the car forward
		local forwardVelocity = direction * speed * deltaTime
		car:SetPrimaryPartCFrame(car.PrimaryPart.CFrame + forwardVelocity)

		-- Spin wheels (simulate rotation)
		for _, wheelData in ipairs(effectsHandler.wheels) do
			local wheel = wheelData.wheel
			local wheelRadius = wheel.Size.Y / 2 -- Assuming the wheel's height is the diameter
			local rotationSpeed = (wheelRotationSpeed / wheelRadius) * deltaTime -- Rotation based on speed
			wheel.CFrame = wheel.CFrame * CFrame.Angles(math.rad(rotationSpeed), 0, 0)
			--IF for some reason your wheels look like they are spining backwards make this -math.rad
		end

		-- Check if the car is close enough to the target
		if (car.PrimaryPart.Position - flatTargetPosition).Magnitude < 5 then
			reached = true
		end
	end

	effectsHandler:SetThrottleEnabled(false) -- Disable throttle when target is reached
end

-- Main loop to navigate between waypoints
while true do
	local startWaypoint = waypoints[currentWaypoint]
	local endWaypoint = waypoints[currentWaypoint % #waypoints + 1]

	-- Compute path between waypoints
	local path = PathfindingService:CreatePath({
		AgentRadius = 4.254,
		AgentHeight = 6.298,
		AgentCanJump = false,
		AgentMaxSlope = 45,
	})

	path:ComputeAsync(startWaypoint.Position, endWaypoint.Position)

	if path.Status == Enum.PathStatus.Success then
		-- Follow the path step by step
		for _, waypoint in ipairs(path:GetWaypoints()) do
			moveTowardsTarget(waypoint.Position)
		end
	else
		moveTowardsTarget(endWaypoint.Position) -- Fallback movement
		--Really this just makes it contiune on the path it was going to continue on
	end

	-- Update to the next waypoint
	currentWaypoint = currentWaypoint % #waypoints + 1
end

Script 2: Effects Module

This module should be a child of the self-driving script:

---Place this module Script as the child of the previous script
local RunService = game:GetService("RunService") -- Service used for running logic every frame
local Effects = {} -- Table that will act as the Effects module
Effects.__index = Effects -- Set the metatable for the Effects table

-- Constructor function for creating a new Effects instance
function Effects.new(chassis, effectsFolder, topModel)
	local self = setmetatable({}, Effects) -- Create a new table for the Effects instance
	self.ignore = topModel -- The main model of the vehicle, used to ignore certain interactions
	self.base = chassis:FindFirstChild("FloorPanel") -- The main floor part of the chassis
	self.attachmentContainer = self.base -- Container for attachments (e.g., tire trails)
	self.active = false -- Flag to track whether the effects system is active
	self.wheels = {} -- Table to store all the wheels for rotation effects

	-- Gather all wheels from the chassis and add them to the `wheels` table
	for _, suspension in ipairs(chassis:GetChildren()) do
		if suspension:FindFirstChild("Wheel") then
			local wheelPart = suspension.Wheel
			table.insert(self.wheels, { wheel = wheelPart }) -- Store wheel part for later effects
		end
	end

	-- Load engine sound from the effects folder and attach it to the main model
	self.engineSound = effectsFolder:FindFirstChild("Engine")
	if self.engineSound then
		self.engineSound = self.engineSound:Clone() -- Clone the engine sound to avoid modifying the original
		self.engineSound.Parent = topModel -- Attach the sound to the main model
	end

	return self -- Return the new Effects instance
end

-- Enable the effects system and connect the OnHeartbeat function
function Effects:Enable()
	self.active = true -- Mark the effects system as active
	if not self.heartbeatConn then
		-- Connect the OnHeartbeat function to RunService's Heartbeat event
		self.heartbeatConn = RunService.Heartbeat:Connect(function(dt)
			self:OnHeartbeat(dt) -- Call the OnHeartbeat function every frame
		end)
	end
end

-- Disable the effects system and disconnect the OnHeartbeat function
function Effects:Disable()
	self.active = false -- Mark the effects system as inactive
	if self.heartbeatConn then
		self.heartbeatConn:Disconnect() -- Disconnect the Heartbeat event
		self.heartbeatConn = nil -- Clear the connection reference
	end
end

-- Toggle the engine sound on or off based on the throttle state
function Effects:SetThrottleEnabled(toggle)
	if toggle and self.engineSound then
		self.engineSound.Playing = true -- Start playing the engine sound
	elseif not toggle and self.engineSound then
		self.engineSound.Playing = false -- Stop playing the engine sound
	end
end

-- Function called every frame while the effects system is active
function Effects:OnHeartbeat(dt)
	if not self.active then return end -- Exit if the system is not active

	-- Custom logic for effects (e.g., tire trails, wheel effects) can be added here
	-- This function is intended to update real-time effects based on the car's state
end

return Effects -- Return the Effects module for use in other scripts

Final Notes

  • You can adjust the speed and wheel rotation speed in the self-driving script:
local speed = 25 -- Adjust car speed
local wheelRotationSpeed = 140 -- Adjust for wheel speed

If your wheels appear to rotate backward: (This shouldn’t be an issue but if it is)

			wheel.CFrame = wheel.CFrame * CFrame.Angles(math.rad(rotationSpeed), 0, 0)
			--IF for some reason your wheels look like they are spining backwards make this -math.rad

To determine the Agent Height and Agent Radius, you can create a part and scale it to roughly match the size of your vehicle. In this example, the part’s size was 8.508, 6.298, 20.583.

  • The Agent Radius is calculated as half of the first number (8.508 ÷ 2 = 4.254).
  • The Agent Height is simply the second number (6.298).

For the slope:

  • A slope of 45° is usually too steep for most vehicles. Upon testing, a 45° slope caused the bumper to hit the incline, preventing the vehicle from climbing.
  • A slope closer to 30° (e.g., part size 22.802, 25.42, 41.702) requires the car’s speed to be increased to be around 90 to make it up the incline.
  • A more manageable slope, such as 13° (e.g., part size 22.802, 9.837, 41.702), works well without needing to adjust the current car’s speed or the wedge’s incline.
-- Compute path between waypoints
	local path = PathfindingService:CreatePath({
		AgentRadius = 4.254,
		AgentHeight = 6.298, 
		AgentCanJump = false,
		AgentMaxSlope = 45,
	})
  • I’ve ensured the script doesn’t calculate waypoints by height, so you won’t need to worry about perfect waypoint placement. (The car would potentially shake if you didn’t make them perfect otherwise)

If you need further assistance or modifications beyond making the car move as intended, feel free to DM me.

Good luck! :red_car::dash: