How to Rig a Car

How to Rig a Car

Rigging a car in Roblox can be hard. Scripting it can be harder. In this four-part series, I walk through the basics of rigging a 3D-modeled car and some basic scripting!

Pick up the car model first!


Part 1 - Setup

Export your car and put it together in Studio.


Part 2 - Rigging the Constraints

Set up all the various attachments and constraints.


Part 3 - Server Scripting

Write the server-side script to handle the car.


Part 4 - Client Scripting

Write the client-side script to allow the player to control the car.


Please provide any constructive feedback!

Shout-out to my roommate hysterically laughing in some of the videos in the background

235 Likes

I have a question. I was under the impression that part’s with specified part types used regular topology based collision detection. Are they using something more accurate? For example is the ball using a ball collision algorithm or is it based on the ball’s geometry?

5 Likes

I was under the assumption that cylinders and balls have more proper collisions based on their types. I could be wrong, but I don’t have a source on this.

10 Likes

I have a feeling you may be right. They said in the future they would be adding a capsule collider which leads me to believe that it will be a new PartType hence them having more accurate collisions.

2 Likes

Pretty much all game engines have different collision detection and resolution algorithms for all primitive shapes. Spheres, cylinders, and capsules collisions are extremely fast to compute due to their round shapes, so it would make sense for Roblox to use these faster algorithms. This can be proven by raycasting onto a sphere and looking at the surface normals the raycast returns.

You can see that the point of intersection on a huge sphere isn’t actually on the mesh, because the mesh isn’t perfectly round like the actual collider itself.

8 Likes

Annd… a little bit of Ackerman steering

12 Likes

Crazyman32 also has a video showing his Ackermann steering here

5 Likes

is there a way to make it so you can disable exiting the vehicle with the space bar like in vehicle sim and blackhawk rescue mission?

2 Likes

I think you can just disable the jump. Or set it’s humanoid jump power to 0.

2 Likes

How would the player get out if the car by pressing a button. For instance, they press F and they get out

2 Likes

I don’t know, but you can search here or in the developer hub about “UserInputService”.

3 Likes

In your client controller, hook up an event to UserInputService to listen for key presses. Be sure to disconnect it when the controller is stopped! Alternatively, you could check for key presses within the Update loop (but doing it via event is better).

If F is pressed, simply make the character jump. You can do this by setting the Humanoid’s Jump property to true.

7 Likes

All I have to say to this incredible tutorial, is wow. Awesome work and I like your other videos, I have learned some stuff from you and from others, but you’re incredible.

Anyway, I’ll try as soon as I have time to do this and see how it can work and learn some more stuff from there. I have to say thanks for all the people that you wrote this tutorial for. :wink:

Keep going the hard work. - @Lil_SharkyBoy

5 Likes

Ah, ok. Thanks, i have been looking for a good car tutorial for a while and this is by far the best.

2 Likes

Just a heads-up, I’m not sure if this is mentioned in the video, but in Roblox spheres work better than cylinders for wheels because the sharp edge of a cylinder can cause the wheel to oscillate rapidly from side to side at high speeds.

For people making cars in Roblox: If it is possible to use something like an invisible sphere as a bounding box for your wheel (in some cases it is, in some cases it isn’t), you should do it.

8 Likes

This is a great point!

If anyone else has any other improvements that could be made, please let me know. I might make a follow-up video on how to improve on the car. Spherical wheels would be good & a justifiable change.

4 Likes

Additionally, do you use servos for steering? Still haven’t checked out the video, but on the topic of improvements, servos are pretty bad in general and rigid forces are almost always better. For example, AngularLimitsEnabled on a CylindricalConstraint, and setting both UpperAngle and LowerAngle to the same value.

1 Like

It sets the attachment orientation via scripting. I’ve found that to be way better. The video shows how to smooth it out so it doesn’t jerk the wheels around when you steer.

1 Like

That’s actually something I thought you might be doing, but didn’t mention in the post.

It’s effectively the same as using angular limits on a CylindricalConstraint. Works just as well, I definitely approve. :stuck_out_tongue: For me, a property I can easily reset to 0 is easier to work with than an attachment that I modify.

Do you do it linearly, exponentially, or do you do it fancily and use a P(I)D controller? :3

I think my standard chassis system does it exponentially, but also allows you to specify a point for the wheels to target. See:

It’s something like… every frame it gets x percent closer to reaching its target. Or the error gets x percent smaller. I don’t remember.

6 Likes

For some reason, my client script doesn’t destroy. I’ve been trying to figure it out but it just doesn’t work.

Here is the code:

local Players = game:GetService("Players")
local Physics = game:GetService("PhysicsService")
local DefaultGroup = "Default"
local CharacterGroup = "Character"
local server = {}
local self = {}
local VehicleModel = script.Parent.Parent
self.Seat = VehicleModel.Seat
self.Model = VehicleModel.Model
self.BoundingBox = VehicleModel.BoundingBox
local OccupiedPlayer 
local OccupiedClientScript
local Client = script.Client
self.CoolDown = 0


local function CoolDown(Duration)
	local CoolDownTag = tick()
	self.CoolDown = CoolDownTag
	delay(Duration,function()
	if (self.CoolDown == CoolDownTag) then
		self.CoolDown = 0
		end
	end)
end

local function CharacterColliding(Character,CanCollide)
	local Group = (CanCollide and DefaultGroup or CharacterGroup)
	for _,Parts in ipairs(Character:GetDescendants()) do
		if (Parts:IsA("BasePart")) then
			Parts.Massless = not CanCollide
			Physics:SetPartCollisionGroup(Parts,Group)
		end
	end
end

local function BoundingBoxTouched(Part)
	if (self.Seat.Occupant or self.CoolDown ~= 0) then return end
	local Character = Part.Parent
	local GetPlayer = Players:GetPlayerFromCharacter(Character)
	if (not GetPlayer) then return end
	local Humanoid = Character:FindFirstChildOfClass("Humanoid")
	if (not Humanoid) then return end 
	self.Seat:Sit(Humanoid)
	OccupiedPlayer = GetPlayer
	CharacterColliding(Character,false)
	VehicleModel.PrimaryPart:SetNetworkOwner(GetPlayer)
	OccupiedClientScript = Client:Clone()
	OccupiedClientScript.Vehicle.Value = VehicleModel
	Client.Parent = GetPlayer.Backpack
	CoolDown(1)
end

local function OccupantChanged()
	if (self.Seat.Occupant) then return end
	if (OccupiedPlayer.Character)then
	CharacterColliding(OccupiedPlayer.Character,true)
	end
	if (OccupiedClientScript.Parent) then
	OccupiedClientScript.Stop.Value = true
	local wu38nq9 = OccupiedClientScript
	delay(1,function()
		wu38nq9:Destroy()
		end)
	end
	VehicleModel.PrimaryPart:SetNetworkOwnershipAuto()
	self.OccupiedPlayer = nil
	OccupiedClientScript = nil
	CoolDown(3)
end

self.BoundingBox.Touched:Connect(BoundingBoxTouched)
self.Seat:GetPropertyChangedSignal("Occupant"):Connect(OccupantChanged)
return server
2 Likes