How can I make a vehicle ai chase me? (Like a cop car or just a normal car)

I’ve been stuck on this kind of thing for a while and still can’t figure out how some people are able to make this kind of thing, which is why I am asking for some advice and help where to look for this particular thing.

Basically I want to make an ai vehicle chase you and be able to add some codes to control what I want the ai to do, for example:

  • Ai chases you
    -Ai patrols area
    -Ai Makes sounds with car (beeping, Sirens, Voices like “Stop” or “PullOver”)
    -Ai can hit you or can stop right behind you with a certain distance given in a script.
    -Be able to add different codes on when or what the Ai does for testing.

I need some guidence for this thing cause I want to do some testing and also share my experience with others with testing the AI(s).

2 Likes

If you wanna use a normal car, just make it do some math to determine position.

However, if you wanna use a module such as A-Chassis, I actually worked on a script to make the car drive to a waypoint. You can take snips from the code if you want.

Should be setup like this:
image

SelfDriving script in Plugins:

--[[   _______  ________________________ _ _____
      / <  /  |/  / ___/_  __/ ____/ __ ( ) ___/
 __  / // / /|_/ /\__ \ / / / __/ / /_/ //\__ \ 
/ /_/ // / /  / /___/ // / / /___/ _, _/ ___/ / 
\____//_/_/  /_//____//_/ /_____/_/ |_| /____/
  __________________________________________________
 /____/____/____/____/____/____/____/____/____/____/
    ___   ___________
   /   | / ____/ ___/
  / /| |/ /   / __ \ 
 / ___ / /___/ /_/ / 
/_/  |_\____/\____/  
  _______________
 /____/____/____/
   _____      ______   ____       _       _            
  / ___/___  / / __/  / __ \_____(_)   __(_)___  ____ _
  \__ \/ _ \/ / /_   / / / / ___/ / | / / / __ \/ __ `/
 ___/ /  __/ / __/  / /_/ / /  / /| |/ / / / / / /_/ / 
/____/\___/_/_/    /_____/_/  /_/ |___/_/_/ /_/\__, /  
  ____________________________________________/____/   
 /____/____/____/____/____/____/____/____/____/____/
   _____            __               
  / ___/__  _______/ /____  ____ ___ 
  \__ \/ / / / ___/ __/ _ \/ __ `__ \
 ___/ / /_/ (__  ) /_/  __/ / / / / /
/____/\__, /____/\__/\___/_/ /_/ /_/ 
_____/____/__________________________
 /____/____/____/____/____/____/____/]]


local car = script.Parent.Car.Value
local values = car["A-Chassis Tune"]._SD.SelfDrivingEnabled
local tune = require(car["A-Chassis Tune"])
local car = script.Parent.Car.Value
local carValues = script.Parent.Values
function turn(v)
	values._Steering.Value = v 
end
function acc(v)
	values._Throttle.Value = v 
end
function brake(v)
	values._Brake.Value = v 
end
function emergencybrake(v)
	values.EmergencyStop.Value = v
end
function shiftup(v)
	values.MSU.Value = v 
end
function shiftdown(v)
	values.MSD.Value = v 
end

local target = Vector3.new(0, nil, 250)
local SupposedPosition = Vector3.new(0, nil, 500)
local OffsetFromTarget = Vector3.new(3, 0, 0)
local SupposedRotation = Vector3.new(0, 0, 0)
local applySpeed = 40
local stoppingForObject = true

local config = {
	brakeThreshold = 0.1,
	steerThreshold = 0.8,
	steerDeadband = 0.05  -- Adjust the deadband value as needed
}

repeat
	task.wait()
	if car.DriveSeat.Occupant then 
		carValues = game.Players:GetPlayerFromCharacter(car.DriveSeat.Occupant.Parent).PlayerGui["A-Chassis Interface"].Values

		if values.Value == true then 
			-- Steering
			if car.PrimaryPart then 
				local Kp = 0.1
				local Ki = 0.01
				local Kd = 0.01

				local integralSum = 0
				local lastError = 0

				local function calculateSteeringPosition(car, tr)
					local targetPosition = tr
					local currentRotation = car.PrimaryPart.CFrame.LookVector
					local carForwardVector = Vector3.new(1, 0, 0) 

					local dotProduct = currentRotation:Dot(carForwardVector)
					local rotationDifference = math.acos(dotProduct) * 180 / math.pi

					local crossProduct = carForwardVector:Cross(currentRotation)
					if crossProduct.Y < 0 then
						rotationDifference = -rotationDifference
					end

					local steeringValue = rotationDifference / 90
					steeringValue = math.clamp(steeringValue, -1, 1)

					local currentPosition = car.PrimaryPart.Position
					local localOffset = car.PrimaryPart.CFrame:VectorToObjectSpace(targetPosition - currentPosition)
					local lateralOffset = localOffset.X

					local error = lateralOffset
					integralSum = integralSum + error
					local derivative = error - lastError
					lastError = error

					-- Check if the lateral offset error is small enough to stop further steering adjustments
					if math.abs(error) < config.steerDeadband then
						steeringValue = 0
						integralSum = 0  -- Reset the integral sum to prevent accumulation
					else
						local steeringOffset = Kp * error + Ki * integralSum + Kd * derivative
						steeringValue = steeringValue + steeringOffset
						steeringValue = math.clamp(steeringValue, -1, 1)
					end

					turn(steeringValue)
				end

				calculateSteeringPosition(car, SupposedPosition)
			else 
				error(car.Name .. "Self Driving Error: \n\t No primary part found. Self-driving cannot continue.")
			end

			-- Accelerating and braking
			if carValues.Velocity.Value.Magnitude < applySpeed then 
				acc(math.clamp(1 - ((applySpeed - carValues.Velocity.Value.Magnitude) * -0.1), 0, 1))
				brake(0)
			end

			if carValues.Velocity.Value.Magnitude > applySpeed and carValues.Velocity.Value.Magnitude < applySpeed + 3 then 
				acc(0)
			elseif carValues.Velocity.Value.Magnitude >= applySpeed + 3 then 
				if carValues.Velocity.Value.Magnitude >= applySpeed + 15 and stoppingForObject == true then 
					acc(0)
					emergencybrake(math.clamp(((carValues.Velocity.Value.Magnitude - applySpeed) * config.brakeThreshold), 0, 1))
				else 
					acc(0)
					brake(math.clamp(((carValues.Velocity.Value.Magnitude - applySpeed) * config.brakeThreshold), 0, 1))
				end
			end
		end
	end
until false

Drive script inside A-Chassis Interface:

--[[
		___      _______                _     
	   / _ |____/ ___/ /  ___ ____ ___ (_)__ 
	  / __ /___/ /__/ _ \/ _ `(_-<(_-</ (_-<
	 /_/ |_|   \___/_//_/\_,_/___/___/_/___/
 						SecondLogic @ Inspare


*I assume you know what you're doing if you're gonna change something here.* ]]--

--[[START]]

	script.Parent:WaitForChild("Car")
	script.Parent:WaitForChild("IsOn")
	script.Parent:WaitForChild("ControlsOpen")
	script.Parent:WaitForChild("Values")

--[[Dependencies]]

	local player = game.Players.LocalPlayer
	local mouse = player:GetMouse()
	local UserInputService = game:GetService("UserInputService")
	local car = script.Parent.Car.Value
	local _Tune = require(car["A-Chassis Tune"])



--[[Output Scaling Factor]]

	local hpScaling = _Tune.WeightScaling*10
	local FBrakeForce = _Tune.FBrakeForce
	local RBrakeForce = _Tune.RBrakeForce
	local PBrakeForce = _Tune.PBrakeForce
	if not workspace:PGSIsEnabled() then
		hpScaling = _Tune.LegacyScaling*10
		FBrakeForce = _Tune.FLgcyBForce
		RBrakeForce = _Tune.RLgcyBForce
		PBrakeForce = _Tune.LgcyPBForce
	end

--[[Status Vars]]

	local _IsOn = _Tune.AutoStart
	if _Tune.AutoStart then script.Parent.IsOn.Value=true end
	
	local _GSteerT=0
	local _GSteerC=0
	local _GThrot=0
	local _GBrake=0
	
	local _ClutchOn = true
	local _ClPressing = false
	local _RPM = 0
	local _HP = 0
	local _OutTorque = 0
	local _CGear = 0
	
	local _TMode = _Tune.TransModes[1]
	
	local _MSteer = false
	local _SteerL = false
	local _SteerR = false
	local _PBrake = false
	local _TCS = _Tune.TCSEnabled
	local _TCSActive = false
	local _ABS = _Tune.ABSEnabled
	local _ABSActive = false
	
	local FlipWait=tick()
	local FlipDB=false
	
	local _InControls = false



--[[Shutdown]]

	car.DriveSeat.ChildRemoved:connect(function(child)
		if child.Name=="SeatWeld" and child:IsA("Weld") then
			script.Parent:Destroy()
		end
	end)



--[[Controls]]

	local _CTRL = _Tune.Controls
	local Controls = Instance.new("Folder",script.Parent)
	Controls.Name = "Controls"
	for i,v in pairs(_CTRL) do
		local a=Instance.new("StringValue",Controls)
		a.Name=i
		a.Value=v.Name
		a.Changed:connect(function()
			if i=="MouseThrottle" or i=="MouseBrake" then
				if a.Value == "MouseButton1" or a.Value == "MouseButton2" then
					_CTRL[i]=Enum.UserInputType[a.Value]
				else
					_CTRL[i]=Enum.KeyCode[a.Value]
				end
			else
				_CTRL[i]=Enum.KeyCode[a.Value]
			end
		end)
	end
	
	--Deadzone Adjust
	local _PPH = _Tune.Peripherals
		for i,v in pairs(_PPH) do
		local a = Instance.new("IntValue",Controls)
		a.Name = i
		a.Value = v
		a.Changed:connect(function() 
			a.Value=math.min(100,math.max(0,a.Value))
			_PPH[i] = a.Value
		end)
	end
	
	--Input Handler
	function DealWithInput(input,IsRobloxFunction)
		if (UserInputService:GetFocusedTextBox()==nil) and not _InControls then --Ignore when UI Focus
			--Shift Down [Manual Transmission]
			if _IsOn and  (input.KeyCode ==_CTRL["ContlrShiftDown"] or (_MSteer and input.KeyCode==_CTRL["MouseShiftDown"]) or ((not _MSteer) and input.KeyCode==_CTRL["ShiftDown"])) and (_TMode=="Semi" or (_TMode=="Manual" and (not _ClutchOn))) and input.UserInputState == Enum.UserInputState.Begin then
				if _CGear == 0 and (_TMode=="Auto" or not _ClPressing) then _ClutchOn = true end
				if selfDriving then 
					selfDriving.Folder.SelfDrivingEnabled.Value = false
				end
				_CGear = math.max(_CGear-1,-1)
				
			--Shift Up [Manual Transmission]
			elseif _IsOn and  (input.KeyCode ==_CTRL["ContlrShiftUp"] or (_MSteer and input.KeyCode==_CTRL["MouseShiftUp"]) or ((not _MSteer) and input.KeyCode==_CTRL["ShiftUp"])) and (_TMode=="Semi" or (_TMode=="Manual" and (not _ClutchOn))) and input.UserInputState == Enum.UserInputState.Begin then
				if _CGear == 0 and (_TMode=="Auto" or not _ClPressing) then _ClutchOn = true end
				_CGear = math.min(_CGear+1,#_Tune.Ratios-2)
				
			--Toggle Clutch
			elseif _IsOn and  (input.KeyCode ==_CTRL["ContlrClutch"] or (_MSteer and input.KeyCode==_CTRL["MouseClutch"]) or ((not _MSteer) and input.KeyCode==_CTRL["Clutch"])) and _TMode=="Manual" then
				if input.UserInputState == Enum.UserInputState.Begin then
					_ClutchOn = false
					_ClPressing = true
				elseif input.UserInputState == Enum.UserInputState.End then
					_ClutchOn = true
					_ClPressing = false
				end
				
			--Toggle PBrake
			elseif _IsOn and  input.KeyCode ==_CTRL["ContlrPBrake"] or (_MSteer and input.KeyCode==_CTRL["MousePBrake"]) or ((not _MSteer) and input.KeyCode==_CTRL["PBrake"]) then
				if input.UserInputState == Enum.UserInputState.Begin then
					_PBrake = not _PBrake
				elseif input.UserInputState == Enum.UserInputState.End then
					if car.DriveSeat.Velocity.Magnitude>5 then
						_PBrake = false
					end
				end
				
			--Toggle Transmission Mode
			elseif (input.KeyCode == _CTRL["ContlrToggleTMode"] or input.KeyCode==_CTRL["ToggleTransMode"]) and input.UserInputState == Enum.UserInputState.Begin then
				local n=1
				for i,v in pairs(_Tune.TransModes) do
					if v==_TMode then n=i break end
				end
				n=n+1
				if n>#_Tune.TransModes then n=1 end
				_TMode = _Tune.TransModes[n]
				
			--Throttle
			elseif _IsOn and ((not _MSteer) and (input.KeyCode==_CTRL["Throttle"] or input.KeyCode == _CTRL["Throttle2"])) or ((((_CTRL["MouseThrottle"]==Enum.UserInputType.MouseButton1 or _CTRL["MouseThrottle"]==Enum.UserInputType.MouseButton2) and input.UserInputType == _CTRL["MouseThrottle"]) or input.KeyCode == _CTRL["MouseThrottle"])and _MSteer) then
				if input.UserInputState == Enum.UserInputState.Begin then
					_GThrot = 1
				else
					_GThrot = _Tune.IdleThrottle/100
				end
				
			--Brake
			elseif ((not _MSteer) and (input.KeyCode==_CTRL["Brake"] or input.KeyCode == _CTRL["Brake2"])) or ((((_CTRL["MouseBrake"]==Enum.UserInputType.MouseButton1 or _CTRL["MouseBrake"]==Enum.UserInputType.MouseButton2) and input.UserInputType == _CTRL["MouseBrake"]) or input.KeyCode == _CTRL["MouseBrake"])and _MSteer) then
				if input.UserInputState == Enum.UserInputState.Begin then
					_GBrake = 1
				else
					_GBrake = 0
				end
				
			--Steer Left
			elseif (not _MSteer) and (input.KeyCode==_CTRL["SteerLeft"] or input.KeyCode == _CTRL["SteerLeft2"]) then
				if input.UserInputState == Enum.UserInputState.Begin then
					_GSteerT = -1
					_SteerL = true
				else
					if _SteerR then
						_GSteerT = 1
					else
						_GSteerT = 0
					end
					_SteerL = false
				end
				
			--Steer Right
			elseif (not _MSteer) and (input.KeyCode==_CTRL["SteerRight"] or input.KeyCode == _CTRL["SteerRight2"]) then
				if input.UserInputState == Enum.UserInputState.Begin then
					_GSteerT = 1
					_SteerR = true
				else
					if _SteerL then
						_GSteerT = -1
					else
						_GSteerT = 0
					end
					_SteerR = false
				end
				
			--Toggle Mouse Controls
			elseif input.KeyCode ==_CTRL["ToggleMouseDrive"] then
				if input.UserInputState == Enum.UserInputState.End then
					_MSteer = not _MSteer
					_GThrot = _Tune.IdleThrottle/100
					_GBrake = 0
					_GSteerT = 0
					_ClutchOn = true
				end
				
			--Toggle TCS
			elseif  _Tune.TCSEnabled and _IsOn and input.KeyCode == _CTRL["ToggleTCS"] or input.KeyCode == _CTRL["ContlrToggleTCS"] then
				if input.UserInputState == Enum.UserInputState.End then
					_TCS = not _TCS
				end
			
			--Toggle ABS
			elseif  _Tune. ABSEnabled and _IsOn and input.KeyCode == _CTRL["ToggleABS"] or input.KeyCode == _CTRL["ContlrToggleABS"] then
				if input.UserInputState == Enum.UserInputState.End then
					_ABS = not _ABS
				end
			end
			
			--Variable Controls
			if input.UserInputType.Name:find("Gamepad") then
				--Gamepad Steering
				if input.KeyCode == _CTRL["ContlrSteer"] then
					if input.Position.X>= 0 then
						local cDZone = math.min(.99,_Tune.Peripherals.ControlRDZone/100)
						if math.abs(input.Position.X)>cDZone then
							_GSteerT = (input.Position.X-cDZone)/(1-cDZone)
						else
							_GSteerT = 0
						end
					else
						local cDZone = math.min(.99,_Tune.Peripherals.ControlLDZone/100)
						if math.abs(input.Position.X)>cDZone then
							_GSteerT = (input.Position.X+cDZone)/(1-cDZone)
						else
							_GSteerT = 0
						end
					end
					
				--Gamepad Throttle
				elseif _IsOn and input.KeyCode == _CTRL["ContlrThrottle"] then
					_GThrot = math.max(_Tune.IdleThrottle/100,input.Position.Z)
					
				--Gamepad Brake
				elseif input.KeyCode == _CTRL["ContlrBrake"] then
					_GBrake = input.Position.Z
				end
			end
		else
			_GThrot = _Tune.IdleThrottle/100
			_GSteerT = 0
			_GBrake = 0
			if _CGear~=0 then _ClutchOn = true end
		end
	end
	UserInputService.InputBegan:connect(DealWithInput)
	UserInputService.InputChanged:connect(DealWithInput)
	UserInputService.InputEnded:connect(DealWithInput)



--[[Drivetrain Initialize]]

	local Drive={}
	
	--Power Front Wheels
	if _Tune.Config == "FWD" or _Tune.Config == "AWD" then
		for i,v in pairs(car.Wheels:GetChildren()) do
			if v.Name=="FL" or v.Name=="FR" or v.Name=="F" then
				table.insert(Drive,v)
			end
		end
	end
	
	--Power Rear Wheels
	if _Tune.Config == "RWD" or _Tune.Config == "AWD" then
		for i,v in pairs(car.Wheels:GetChildren()) do
			if v.Name=="RL" or v.Name=="RR" or v.Name=="R" then
				table.insert(Drive,v)
			end
		end
	end
	
	--Determine Wheel Size
	local wDia = 0
	for i,v in pairs(Drive) do
		if v.Size.x>wDia then wDia = v.Size.x end
	end
	
	--Pre-Toggled PBrake
	for i,v in pairs(car.Wheels:GetChildren()) do
		if math.abs(v["#AV"].maxTorque.Magnitude-PBrakeForce)<1 then
			_PBrake=true
		end
	end
	
	

--[[Steering]]

	function Steering()
		--Mouse Steer
		if _MSteer then
			local msWidth = math.max(1,mouse.ViewSizeX*_Tune.Peripherals.MSteerWidth/200)
			local mdZone = _Tune.Peripherals.MSteerDZone/100
			local mST = ((mouse.X-mouse.ViewSizeX/2)/msWidth)
			if math.abs(mST)<=mdZone then
				_GSteerT = 0
			else
				_GSteerT = (math.max(math.min((math.abs(mST)-mdZone),(1-mdZone)),0)/(1-mdZone))^_Tune.MSteerExp * (mST / math.abs(mST))
			end
		end
		
		--Interpolate Steering
		if _GSteerC < _GSteerT then
			if _GSteerC<0 then
				_GSteerC = math.min(_GSteerT,_GSteerC+_Tune.ReturnSpeed)
			else
				_GSteerC = math.min(_GSteerT,_GSteerC+_Tune.SteerSpeed)
			end
		else
			if _GSteerC>0 then
				_GSteerC = math.max(_GSteerT,_GSteerC-_Tune.ReturnSpeed)
			else
				_GSteerC = math.max(_GSteerT,_GSteerC-_Tune.SteerSpeed)
			end
		end
		
		--Steer Decay Multiplier
		local sDecay = (1-math.min(car.DriveSeat.Velocity.Magnitude/_Tune.SteerDecay,1-(_Tune.MinSteer/100)))
		
		--Apply Steering
		for i,v in pairs(car.Wheels:GetChildren()) do
			if v.Name=="F" then
				v.Arm.Steer.CFrame=car.Wheels.F.Base.CFrame*CFrame.Angles(0,-math.rad(_GSteerC*_Tune.SteerInner*sDecay),0)
			elseif v.Name=="FL" then
				if _GSteerC>= 0 then
					v.Arm.Steer.CFrame=car.Wheels.FL.Base.CFrame*CFrame.Angles(0,-math.rad(_GSteerC*_Tune.SteerOuter*sDecay),0)
				else
					v.Arm.Steer.CFrame=car.Wheels.FL.Base.CFrame*CFrame.Angles(0,-math.rad(_GSteerC*_Tune.SteerInner*sDecay),0)
				end
			elseif v.Name=="FR" then
				if _GSteerC>= 0 then
					v.Arm.Steer.CFrame=car.Wheels.FR.Base.CFrame*CFrame.Angles(0,-math.rad(_GSteerC*_Tune.SteerInner*sDecay),0)
				else
					v.Arm.Steer.CFrame=car.Wheels.FR.Base.CFrame*CFrame.Angles(0,-math.rad(_GSteerC*_Tune.SteerOuter*sDecay),0)
				end
			end
		end
	end



--[[Engine]]

	local fFD = _Tune.FinalDrive*_Tune.FDMult

	--Horsepower Curve
	local fgc_h=_Tune.Horsepower/100
	local fgc_n=_Tune.PeakRPM/1000
	local fgc_a=_Tune.PeakSharpness
	local fgc_c=_Tune.CurveMult
	function FGC(x)
		x=x/1000
		return (((-(x-fgc_n)^2)*math.min(fgc_h/(fgc_n^2),fgc_c^(fgc_n/fgc_h)))+fgc_h)*(x-((x^fgc_a)/((fgc_a*fgc_n)^(fgc_a-1))))
	end
	local PeakFGC = FGC(_Tune.PeakRPM)
	
	--Plot Current Horsepower
	local cGrav = workspace.Gravity/32.2
	function GetCurve(x)
		local hp=math.max((FGC(x)*_Tune.Horsepower)/PeakFGC,0)
		local iComp =(car.DriveSeat.CFrame.lookVector.y)*_Tune.InclineComp*cGrav
		if _CGear==-1 then iComp=-iComp end
		return hp,hp*(_Tune.EqPoint/x)*_Tune.Ratios[_CGear+2]*fFD*math.max(1,(1+iComp))*hpScaling
	end
	
	--Powertrain
	function Engine()
		--Neutral Gear
		if _CGear==0 then _ClutchOn = false end
	
		--Car Is Off
		local revMin = _Tune.IdleRPM
		if not _IsOn then 
			revMin = 0 
			_CGear = 0
			_ClutchOn = false
			_GThrot = _Tune.IdleThrottle/100
		end
		
		--Determine RPM
		local maxSpin=0
		for i,v in pairs(Drive) do
			if v.RotVelocity.Magnitude>maxSpin then maxSpin = v.RotVelocity.Magnitude end
		end
		if _ClutchOn then
			local aRPM = math.max(math.min(maxSpin*_Tune.Ratios[_CGear+2]*fFD*30/math.pi,_Tune.Redline+100),revMin)
			local clutchP = math.min(math.abs(aRPM-_RPM)/_Tune.ClutchTol,.9)
			_RPM = _RPM*clutchP  +  aRPM*(1-clutchP)
			_HP,_OutTorque = GetCurve(_RPM)
		else
			if _GThrot-(_Tune.IdleThrottle/100)>0 then
				_RPM = math.min(_RPM+_Tune.RevAccel*_GThrot,_Tune.Redline+100)
			else
				_RPM = math.max(_RPM-_Tune.RevDecay,revMin)
			end
			_OutTorque = 0
		end
		
		--Rev Limiter
		local spLimit = 0
		if _RPM>_Tune.Redline then 
			if _CGear<#_Tune.Ratios-2 then
				_RPM = _RPM-_Tune.RevBounce
				spLimit = 0
			else
				_RPM = _RPM-_Tune.RevBounce*.5
			end
		else
			spLimit = (_Tune.Redline+100)*math.pi/(30*_Tune.Ratios[_CGear+2]*fFD)
		end
		
		--Automatic Transmission
		if _TMode == "Auto" and _IsOn then
			_ClutchOn = true
			if _CGear == 0 then _CGear = 1 end
			if _CGear >= 1 then
				if _CGear==1 and _GBrake > 0  and car.DriveSeat.Velocity.Magnitude < 20 then
					_CGear = -1
				else
					if _Tune.AutoShiftMode == "RPM" then
						if _RPM>(_Tune.PeakRPM+_Tune.AutoUpThresh) then
							_CGear=math.min(_CGear+1,#_Tune.Ratios-2) 
						elseif math.max(math.min(maxSpin*_Tune.Ratios[_CGear+1]*fFD*30/math.pi,_Tune.Redline+100),_Tune.IdleRPM)<(_Tune.PeakRPM-_Tune.AutoDownThresh) then
							_CGear=math.max(_CGear-1,1)
						end
					else
						if car.DriveSeat.Velocity.Magnitude > math.ceil(wDia*math.pi*(_Tune.PeakRPM+_Tune.AutoUpThresh)/60/_Tune.Ratios[_CGear+2]/fFD) then
							_CGear=math.min(_CGear+1,#_Tune.Ratios-2)
						elseif car.DriveSeat.Velocity.Magnitude < math.ceil(wDia*math.pi*(_Tune.PeakRPM-_Tune.AutoDownThresh)/60/_Tune.Ratios[_CGear+1]/fFD) then
							_CGear=math.max(_CGear-1,1)
						end
					end
				end
			else
				if _GThrot-(_Tune.IdleThrottle/100) > 0 and car.DriveSeat.Velocity.Magnitude < 20 then
					_CGear = 1
				end
			end
		end 
		
		--Average Rotational Speed Calculation
		local fwspeed=0
		local fwcount=0
		local rwspeed=0
		local rwcount=0
		
		for i,v in pairs(car.Wheels:GetChildren()) do
			if v.Name=="FL" or v.Name=="FR" or v.Name == "F" then
				fwspeed=fwspeed+v.RotVelocity.Magnitude
				fwcount=fwcount+1
			elseif v.Name=="RL" or v.Name=="RR" or v.Name == "R" then
				rwspeed=rwspeed+v.RotVelocity.Magnitude
				rwcount=rwcount+1
			end
		end
		fwspeed=fwspeed/fwcount
		rwspeed=rwspeed/rwcount	
		local cwspeed=(fwspeed+rwspeed)/2
		
		--Update Wheels
		for i,v in pairs(car.Wheels:GetChildren()) do
			--Reference Wheel Orientation
			local Ref=(CFrame.new(v.Position-((v.Arm.CFrame*CFrame.Angles(math.pi/2,-math.pi/2,0)).lookVector),v.Position)*CFrame.Angles(0,math.pi,0)).lookVector
			local aRef=1
			local diffMult=1
			if v.Name=="FL" or v.Name=="RL" then aRef=-1 end
			
			--AWD Torque Scaling
			if _Tune.Config == "AWD" then _OutTorque = _OutTorque*(2^.5)/2 end
			
			--Differential/Torque-Vectoring
			if v.Name=="FL" or v.Name=="FR" then
				diffMult=math.max(0,math.min(1,1+((((v.RotVelocity.Magnitude-fwspeed)/fwspeed)/(math.max(_Tune.FDiffSlipThres,1)/100))*((_Tune.FDiffLockThres-50)/50))))
				if _Tune.Config == "AWD" then
					diffMult=math.max(0,math.min(1,diffMult*(1+((((fwspeed-cwspeed)/cwspeed)/(math.max(_Tune.CDiffSlipThres,1)/100))*((_Tune.CDiffLockThres-50)/50)))))
				end
			elseif v.Name=="RL" or v.Name=="RR" then
				diffMult=math.max(0,math.min(1,1+((((v.RotVelocity.Magnitude-rwspeed)/rwspeed)/(math.max(_Tune.RDiffSlipThres,1)/100))*((_Tune.RDiffLockThres-50)/50))))
				if _Tune.Config == "AWD" then
					diffMult=math.max(0,math.min(1,diffMult*(1+((((rwspeed-cwspeed)/cwspeed)/(math.max(_Tune.CDiffSlipThres,1)/100))*((_Tune.CDiffLockThres-50)/50)))))
				end
			end
			
			_TCSActive = false
			_ABSActive = false
			--Output
			if _PBrake and ((_Tune.Config ~= "FWD" and (((v.Name=="FL" or v.Name=="FR") and car.DriveSeat.Velocity.Magnitude<20) or ((v.Name=="RR" or v.Name=="RL") and car.DriveSeat.Velocity.Magnitude>=20))) or (_Tune.Config == "FWD" and (v.Name=="RR" or v.Name=="RL"))) then
				--PBrake
				v["#AV"].maxTorque=Vector3.new(math.abs(Ref.x),math.abs(Ref.y),math.abs(Ref.z))*PBrakeForce
				v["#AV"].angularvelocity=Vector3.new()
			else
				--Apply Power
				if ((_TMode == "Manual" or _TMode == "Semi") and _GBrake==0) or (_TMode == "Auto" and ((_CGear>-1 and _GBrake==0 ) or (_CGear==-1 and _GThrot-(_Tune.IdleThrottle/100)==0 )))then
					local driven = false
					for _,a in pairs(Drive) do if a==v then driven = true end end
					if driven then
						local on=1
						if not script.Parent.IsOn.Value then on=0 end
						local throt = _GThrot
						if _TMode == "Auto" and _CGear==-1 then throt = _GBrake end
						
						--Apply TCS
						local tqTCS = 1
						if _TCS then
							tqTCS = 1-(math.min(math.max(0,math.abs(v.RotVelocity.Magnitude*(v.Size.x/2) - v.Velocity.Magnitude)-_Tune.TCSThreshold)/_Tune.TCSGradient,1)*(1-(_Tune.TCSLimit/100)))
						end
						if tqTCS < 1 then
							_TCSActive = true
						end
						
						--Update Forces
						local dir = 1
						if _CGear==-1 then dir = -1 end
						v["#AV"].maxTorque=Vector3.new(math.abs(Ref.x),math.abs(Ref.y),math.abs(Ref.z))*_OutTorque*(1+(v.RotVelocity.Magnitude/60)^1.15)*throt*tqTCS*diffMult*on
						v["#AV"].angularvelocity=Ref*aRef*spLimit*dir
					else
						v["#AV"].maxTorque=Vector3.new()
						v["#AV"].angularvelocity=Vector3.new()
					end
					
				--Brakes
				else
					local brake = _GBrake
					if _TMode == "Auto" and _CGear==-1 then brake = _GThrot end
					
					--Apply ABS
					local tqABS = 1
					if _ABS and math.abs(v.RotVelocity.Magnitude*(v.Size.x/2) - v.Velocity.Magnitude)-_Tune.ABSThreshold>0 then
						tqABS = 0
					end
					if tqABS < 1 then
						_ABSActive = true
					end
					
					--Update Forces
					if v.Name=="FL" or v.Name=="FR" or v.Name=="F" then
						v["#AV"].maxTorque=Vector3.new(math.abs(Ref.x),math.abs(Ref.y),math.abs(Ref.z))*FBrakeForce*brake*tqABS
					else
						v["#AV"].maxTorque=Vector3.new(math.abs(Ref.x),math.abs(Ref.y),math.abs(Ref.z))*RBrakeForce*brake*tqABS
					end
					v["#AV"].angularvelocity=Vector3.new()
				end
			end
		end
	end



--[[Flip]]

	function Flip()
		--Detect Orientation
		if (car.DriveSeat.CFrame*CFrame.Angles(math.pi/2,0,0)).lookVector.y > .1 or FlipDB then
			FlipWait=tick()
			
		--Apply Flip
		else
			if tick()-FlipWait>=3 then
				FlipDB=true
				local gyro = car.DriveSeat.Flip
				gyro.maxTorque = Vector3.new(10000,0,10000)
				gyro.P=3000
				gyro.D=500
				wait(1)
				gyro.maxTorque = Vector3.new(0,0,0)
				gyro.P=0
				gyro.D=0
				FlipDB=false
			end
		end
	end


--[[   _______  ________________________ _ _____
      / <  /  |/  / ___/_  __/ ____/ __ ( ) ___/
 __  / // / /|_/ /\__ \ / / / __/ / /_/ //\__ \ 
/ /_/ // / /  / /___/ // / / /___/ _, _/ ___/ / 
\____//_/_/  /_//____//_/ /_____/_/ |_| /____/
  __________________________________________________
 /____/____/____/____/____/____/____/____/____/____/
    ___   ___________
   /   | / ____/ ___/
  / /| |/ /   / __ \ 
 / ___ / /___/ /_/ / 
/_/  |_\____/\____/  
  _______________
 /____/____/____/
   _____      ______   ____       _       _            
  / ___/___  / / __/  / __ \_____(_)   __(_)___  ____ _
  \__ \/ _ \/ / /_   / / / / ___/ / | / / / __ \/ __ `/
 ___/ /  __/ / __/  / /_/ / /  / /| |/ / / / / / /_/ / 
/____/\___/_/_/    /_____/_/  /_/ |___/_/_/ /_/\__, /  
  ____________________________________________/____/   
 /____/____/____/____/____/____/____/____/____/____/
   _____            __               
  / ___/__  _______/ /____  ____ ___ 
  \__ \/ / / / ___/ __/ _ \/ __ `__ \
 ___/ / /_/ (__  ) /_/  __/ / / / / /
/____/\__, /____/\__/\___/_/ /_/ /_/ 
_____/____/__________________________
 /____/____/____/____/____/____/____/]]

--				~[[   Making sure it exists   ]]~
if car and car:FindFirstChild("A-Chassis Tune") then 
	if car:FindFirstChild("A-Chassis Tune"):FindFirstChild("_SD") then 
		local selfDriving = {Folder = car:FindFirstChild("A-Chassis Tune"):FindFirstChild("_SD")}
		
		--				~[[   Functions   ]]~
		function selfDriving.Shift_Up(v)
			_ClutchOn = true
			_ClPressing = false
			task.wait(0.34)
			_CGear = math.min(_CGear+v,#_Tune.Ratios-2)
			task.wait(0.67)
			_ClutchOn = false
			_ClPressing = true
		end
		
		function selfDriving.Shift_Down(v)
			_ClutchOn = true
			_ClPressing = false
			task.wait(0.34)
			_CGear = math.max(_CGear-v,-1)
			task.wait(0.67)
			_ClutchOn = false
			_ClPressing = true
		end
		
		function selfDriving.Throttle(v)
			if v>=1 then
				_GThrot = v
			else
				_GThrot = _Tune.IdleThrottle/100
			end
		end
		
		function selfDriving.Brake(v)
			if v>=1 then
				_GBrake = v
			else
				_GBrake = 0
			end
		end
		
		function selfDriving.applyParkingBrake()
			_PBrake = true
		end
		function selfDriving.releaseParkingBrake()
			_PBrake = false
		end
		
		function selfDriving.emergencyStop(v)
			if v<70 then 
				selfDriving.Brake(0.2)
				task.wait(0.02)
				selfDriving.Brake(0.6)
				task.wait(0.02)
				selfDriving.Brake(1)
			elseif v<40 then
				selfDriving.Brake(0.6)
				task.wait(0.02)
				selfDriving.Brake(1)
			elseif v<20 then
				task.wait(0.02)
				selfDriving.Brake(1)
				_PBrake = true
			end
		end
		
		function selfDriving.Steer(v)
			_GSteerT = v
			
			if v<0 then 
				_SteerL = true
			end
			if v>0 then 
				_SteerR = true
			end
			if v==0 then 
				_GSteerT = v
			end
		end
		
		--				~[[   Initialize   ]]~
		
		selfDriving.Folder.SelfDrivingEnabled.MSU.Changed:Connect(function(i)
			if selfDriving.Folder.SelfDrivingEnabled.Value == true then 
				selfDriving.Shift_Up(i)
				selfDriving.Folder.SelfDrivingEnabled.MSU.Value=0
			end
		end)
		
		selfDriving.Folder.SelfDrivingEnabled.MSD.Changed:Connect(function(i)
			if selfDriving.Folder.SelfDrivingEnabled.Value == true then 
				selfDriving.Shift_Down(i)
				selfDriving.Folder.SelfDrivingEnabled.MSD.Value=0
			end
		end)
		
		selfDriving.Folder.SelfDrivingEnabled._Brake.Changed:Connect(function(i)
			if selfDriving.Folder.SelfDrivingEnabled.Value == true then 
				selfDriving.Brake(i)
			end
		end)
		
		selfDriving.Folder.SelfDrivingEnabled._Throttle.Changed:Connect(function(i)
			if selfDriving.Folder.SelfDrivingEnabled.Value == true then 
				selfDriving.Throttle(i)
			end
		end)
		
		selfDriving.Folder.SelfDrivingEnabled._Steering.Changed:Connect(function(i)
			if selfDriving.Folder.SelfDrivingEnabled.Value == true then 
				selfDriving.Steer(i)
			end
		end)
		selfDriving.Folder.SelfDrivingEnabled.EmergencyStop.Changed:Connect(function(i)
			if selfDriving.Folder.SelfDrivingEnabled.Value == true then 
				selfDriving.emergencyStop(i)
			end
		end)
		selfDriving.Folder.SelfDrivingEnabled.Changed:Connect(function(i)
			if selfDriving.Folder.SelfDrivingEnabled.Value == false then 
				selfDriving.Steer(0)
				selfDriving.Throttle(0)
			end
		end)
		
	end
end


--[[Run]]

	--Print Version
	local ver=require(car["A-Chassis Tune"].README)
	print("//INSPARE: AC6 Loaded - Build "..ver)
	
	--Runtime Loop
	while wait() do
		--Steering
		Steering()
		
		--Powertrain
		Engine()
		
		--Flip
		if _Tune.AutoFlip then Flip() end
		
		--Update External Values
		_IsOn = script.Parent.IsOn.Value
		_InControls = script.Parent.ControlsOpen.Value
		script.Parent.Values.Gear.Value = _CGear
		script.Parent.Values.RPM.Value = _RPM
		script.Parent.Values.Horsepower.Value = _HP
		script.Parent.Values.Torque.Value = _HP * 5250 / _RPM
		script.Parent.Values.TransmissionMode.Value = _TMode
		script.Parent.Values.Throttle.Value = _GThrot
		script.Parent.Values.Brake.Value = _GBrake
		script.Parent.Values.SteerC.Value = _GSteerC*(1-math.min(car.DriveSeat.Velocity.Magnitude/_Tune.SteerDecay,1-(_Tune.MinSteer/100)))
		script.Parent.Values.SteerT.Value = _GSteerT
		script.Parent.Values.PBrake.Value = _PBrake
		script.Parent.Values.TCS.Value = _TCS
		script.Parent.Values.TCSActive.Value = _TCSActive
		script.Parent.Values.ABS.Value = _ABS
		script.Parent.Values.ABSActive.Value = _ABSActive
		script.Parent.Values.MouseSteerOn.Value = _MSteer
		script.Parent.Values.Velocity.Value = car.DriveSeat.Velocity
		
	end

--[[END]]
2 Likes

I would just make a function to find nearest player

--part is  whatever your cop car is
local part = script.Parent
local patrolArea = 20
local stopDistance = 2

local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

function FindNearestPlayer()
	local playerlist = Players:GetPlayers()

	local nearestPlayer = nil
	local distance = nil
	local direction = nil

	for _, player in pairs(playerlist) do
		local char = player.Character
		if char then
			local distanceVector = (player.Character.HumanoidRootPart.Position - part.Position)

			if not nearestPlayer then
				nearestPlayer = player
				distance = distanceVector.Magnitude
				direction = distanceVector.Unit

			elseif distanceVector.Magnitude < distance then
				nearestPlayer = player
				distance = distanceVector.Magnitude
				direction = distanceVector.Unit

			end
			
		end
	end
	
	
	
	return nearestPlayer, distance, direction
end

local nearestPlayer, distance, direction = FindNearestPlayer()
		if nearestPlayer then
			if distance <= patrolArea and distance > stopDistance then
				
                               --whatever else you want the cop car to do one it starts chasing goes in here

				part:Move(direction)
			
				part:Move(Vector3.new())
			end
          end
end

1 Like