Hello. I’m working on car AI, and it’s coming along ok. But for the past few days, I’ve been struggling on making it have better control over the vehicle it’s driving.
Here is a video of my problem:
Notice how when the police car tries to take down my car, it starts swerving alot, and can’t seem to stay in control of the car very well. I’ve tried taking into account the current angle of the steering wheels, and it’s distance from my vehicle, and it still has this issue.
I’m not sure what code I need to show, so I might as well give you the whole thing The cars.rbxl (99.1 KB)
I’ve already tried putting some of my values into a curve function (Such as an exponential curve) and it seems to help, but not to the extent that I want it to.
Here is the code, for those of you who don’t want to download it:
Beware, that it’s a bit messy.
local vehicle = game:GetService("ReplicatedStorage").Car
local cars = {}
game:GetService("ReplicatedStorage").SpawnCar.OnServerEvent:Connect(function()
local clone = vehicle:Clone()
clone.Parent = workspace.AICars
clone.PrimaryPart:SetNetworkOwner(game:GetService("Players"):WaitForChild("x86_architecture"))
cars[clone] = {Throttle = 0, Steer = 0, SteerValue = 0, CurrentClock = os.clock(), ThrottleControl = false, Car = clone}
end)
-- AI Config
local steerThreshold = 0
local frequencyMinThreshold = 0.5
local frequencyMaxThreshold = 1
local frequencyMinDist = 30
local throttleControlAmount = 1.5
local controlGainDist = 15
local followDist = 50
local maxOverSpeed = 20
local stopDist = 30
-- Chassis Config
local maxSteerAngle = 45
local acceleration = 50
local brakingAcceleration = 20
local brakingTorque = 3000
local torque = 5000
local turnSpeed = 5
local maxSpeed = 111
local wheelRad = 2.535 / 2
-- Config End
local lastSteer = 0
local function getAngleFromUnitVectors(a,b)
local dot = a:Dot(b)
return math.acos(dot)
end
local function angleBetweenVectors(u, v)
return math.acos(u:Dot(v)/u.magnitude*v.magnitude)
end
local exp = function(x)
return 2^x
end
local function update(dt, attFL, attFR, motorCyls, invertedCyls, car, seat)
-- Steer:
local steerGoal = seat.Steer
--cars[car].SteerValue = cars[car].SteerValue + (steerGoal - cars[car].SteerValue) * math.min(dt * turnSpeed, 1)
attFL.Orientation = Vector3.new(0, steerGoal, -90)
attFR.Orientation = Vector3.new(0, steerGoal, -90)
-- Throttle:
local throttleGoal = -seat.Throttle * maxSpeed
local torqueGoal = torque
for _, cyl in pairs(motorCyls) do
if invertedCyls[cyl] then
if math.sign(throttleGoal) ~= 0 and math.sign(throttleGoal) == car.Chassis.Platform.Velocity:Dot(-car.Chassis.Platform.CFrame.LookVector) then
cyl.AngularActuatorType = Enum.ActuatorType.Motor
cyl.MotorMaxAngularAcceleration = brakingAcceleration
cyl.MotorMaxTorque = brakingTorque
cyl.AngularVelocity = -throttleGoal
elseif math.sign(throttleGoal) == 0 then
cyl.AngularActuatorType = Enum.ActuatorType.None
else
cyl.AngularActuatorType = Enum.ActuatorType.Motor
cyl.MotorMaxAngularAcceleration = acceleration
cyl.MotorMaxTorque = torqueGoal
cyl.AngularVelocity = -throttleGoal
end
else
if math.sign(throttleGoal) ~= 0 and math.sign(throttleGoal) == math.sign(car.Chassis.Platform.Velocity:Dot(-car.Chassis.Platform.CFrame.LookVector)) then
cyl.AngularActuatorType = Enum.ActuatorType.Motor
cyl.MotorMaxAngularAcceleration = brakingAcceleration
cyl.MotorMaxTorque = brakingTorque
cyl.AngularVelocity = throttleGoal
elseif math.sign(throttleGoal) == 0 then
cyl.AngularActuatorType = Enum.ActuatorType.None
else
cyl.AngularActuatorType = Enum.ActuatorType.Motor
cyl.MotorMaxAngularAcceleration = acceleration
cyl.MotorMaxTorque = torqueGoal
cyl.AngularVelocity = throttleGoal
end
end
end
end
print(os.clock())
game:GetService("RunService").Heartbeat:Connect(function(dt)
for i, clone in pairs(cars) do
coroutine.wrap(function()
local car = clone.Car
local motorCyls = {
["cylBL"] = clone.Car.Chassis.Platform.CylindricalBL,
["cylBR"] = clone.Car.Chassis.Platform.CylindricalBR,
["cylFL"] = clone.Car.Chassis.Platform.CylindricalFL,
["cylFR"] = clone.Car.Chassis.Platform.CylindricalFR,
}
local invertedCyls = {
[clone.Car.Chassis.Platform.CylindricalFL] = true
}
local attFL = clone.Car.Chassis.Platform.AttachmentFL
local attFR = clone.Car.Chassis.Platform.AttachmentFR
local car = clone.Car
local dot = -car.Chassis.Platform.CFrame.LookVector:Dot((workspace.Car.Chassis.TargetPart.Position - car.Chassis.Targetter.Position).Unit)
local distance = (workspace.Car.Chassis.TargetPart.Position - car.Chassis.Targetter.Position).Magnitude
local speed = car.Chassis.Platform.Velocity:Dot(-car.Chassis.Platform.CFrame.LookVector)
local angle = math.deg(getAngleFromUnitVectors((car.Chassis.Targetter.Position - workspace.Car.Chassis.TargetPart.Position).Unit, car.Chassis.Platform.CFrame.LookVector.Unit)) * math.sign(-car.Chassis.Platform.CFrame.RightVector:Dot((car.Chassis.Targetter.Position - workspace.Car.Chassis.TargetPart.Position).Unit))
local steer = math.clamp(angle, -maxSteerAngle, maxSteerAngle)
local distanceCurve = math.min(math.abs(0 - distance) / 100, 1)-- * math.sqrt(math.abs(math.abs(steer) - 0) / maxSteerAngle)
local steerCurve = math.abs( - math.abs(steer)) / maxSteerAngle
steer = steer * steerCurve * distanceCurve
if math.sign(steer) ~= math.sign(lastSteer) then
local Time = os.clock() - clone.CurrentClock
clone.CurrentClock = os.clock()
if Time > frequencyMinThreshold and Time < frequencyMaxThreshold and distance < frequencyMinDist then
--print(1)
clone.ThrottleControl = true
end
end
--print(steer)
if math.abs(steer) < steerThreshold then
steer = 0
end
local throttle = 0
if distance > 0 and not clone.ThrottleClone then
throttle = ((67 - math.abs(steer)) / 67)-- / clone.ThrottleControl
else
throttle = 0
end
local Time = os.clock() - clone.CurrentClock
if Time > frequencyMaxThreshold or distance > controlGainDist and clone.ThrottleControl then
--print(2)
clone.ThrottleControl = false
end
update(dt, attFL, attFR, motorCyls, invertedCyls, car, {Throttle = throttle, Steer = steer, SteerValue = clone.SteerValue})
lastSteer = math.sign(steer)
end)()
end
end)