So, I’m working on an AI traffic system. I’m using an A* pathfinding module, but that really shouldn’t change anything.
Currently I’ve just added a humanoid to the vehicle and made it move to a certain position, this works, but isn’t enough for a vehicle. I want my vehicle to actually steer, and accelerate / brake to get to the next point. I’m using Roblox’s new Racing Template which I grabbed the vehicle from.
I’m really new to this stuff, and don’t know how I’d even start with making the vehicle drive. I want it to drive similar to as it would if a player was driving it. So if the max speed was 10, I wouldn’t want it to go over that, and all that stuff.
Here’s a snippet my current code which makes the vehicle walk to the next point.
if (path) then
table.insert(path, 1, start)
table.insert(path, goal)
local pathfolder = Instance.new("Folder", workspace)
pathfolder.Name = "VisualPath"
for i = 2,#path do
local p1 = path[i - 1] -- P1 is where the vehicle is located (last waypoint)
local p2 = path[i] -- P2 is where it needs to go.
humNPC:MoveTo(p2) -- Makes the humanoid walk to P2
humNPC.MoveToFinished:Wait() -- Waits, and loops again so it walks to the next point.
end
else
warn("Failed to find Path.")
end
What it currently looks like:
How would I get the vehicle to even move (like it would with a player controlling it) in the first place?
You can create an algorithm using Roblox’s physics.
(Instead of directly using :MoveTo(), use LinearVelocity, for example.)
The car slowly accelerates and decelerates, like in a real car. Think of how a human would drive a car and design the algorithm to create an AI that imitates a human being.
The AI should know where to slow down, and where to speed up to a certain speed, like when it’s making a turn, it should slow down, whereas when it’s going straight on a normal road, it should speed up to 40-50mph for example.
When the car starts and stops, it shouldn’t do it immediately, for example, when it starts moving, it should reach a certain amount of speed in a period, rather than immediately.
This won’t be an easy task, as there are many parameters you have to account for if you wish to create a car AI that resembles a human. It should be configured to feel and look natural, the car, for example, should have weight.
It would be best if you continued using the path system you have right now, but changed how you handle how the car works.
To make the car drive around, all you need to do is hook up the Controller:update function
-- AI driver script
local RunService = game:GetService("RunService")
local Controller = require(script.Parent.Parent.Controller)
local function onStepped(_, deltaTime: number)
Controller:update(deltaTime)
end
RunService.Stepped:Connect(onStepped)
You can now change the steeringInput, throttleInput, nitroInput, and handBrakeInput attributes on Car.Inputs and it will respond accordingly.
Making a proper car driving algorithm is a lot more involved, as you’ll want to do some sort of path planning to optimize the steering/throttle/braking/etc, but here’s a simple example of getting it to drive to a specific target part in workspace.
-- AI driver script
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local Controller = require(script.Parent.Parent.Controller)
local Constants = require(script.Parent.Parent.Constants)
local car = script.Parent.Parent.Parent
local chassis = car.Chassis
local inputs = car.Inputs
local target = Workspace.Target
local MIN_SPEED_DISTANCE = 20
local MAX_SPEED_DISTANCE = 100
local function onStepped(_, deltaTime: number)
-- Calculate the direction to the target
local targetOffset = chassis.CFrame:PointToObjectSpace(target.Position)
local targetAngle = math.atan2(targetOffset.X, -targetOffset.Z)
-- Update the steering direction
inputs:SetAttribute(Constants.STEERING_INPUT_ATTRIBUTE, targetAngle)
-- Calculate the distance to the target
local distance = targetOffset.Magnitude
-- Calculate a throttle speed based on distance. We want to go slower when closer so
-- that our turning radius is tighter
local throttle = math.clamp((distance - MIN_SPEED_DISTANCE) / MAX_SPEED_DISTANCE, 0, 1)
-- Update the throttle
inputs:SetAttribute(Constants.THROTTLE_INPUT_ATTRIBUTE, throttle)
-- Update the controller
Controller:update(deltaTime)
end
RunService.Stepped:Connect(onStepped)
You could modify this to pick the next target in your path once the car gets close enough
This is very similar to how I built self driving vehicles in Garry’s Mod
Additional things I would consider
Graph of nodes placed for every road intersection with links between neighbors
Drive to target by finding nearest node to target, then calculate using Dijkstra’s algorithmn the route towards the vehicle, select the last node in the list as current node to drive towards
Add simple ray tracing in front of the vehicle to brake when it detects an obstacle
Put a bias on targetAngle so the vehicle drives more on the left or right side of the road
If you set the throttleInput attribute to 0 the car will slow to a stop. If you put it in reverse while it’s rolling forward it will automatically brake.
You could do something like
-- inside onStepped
if shouldBeBraking then
-- apply brakes while moving forward
local forwardVelocity = chassis.CFrame:VectorToObjectSpace(chassis.AssemblyLinearVelocity)
-- note: -Z is the forward axis
if forwardVelocity.Z < -5 then
inputs:SetAttribute(Constants.THROTTLE_INPUT_ATTRIBUTE, -1)
else
inputs:SetAttribute(Constants.THROTTLE_INPUT_ATTRIBUTE, 0)
end
end
Alternatively you could apply the handbrake, but that might cause it to skid out