How to make vehicle crash system

So I wanted to show you how you could make your own vehicle damage system when hit at a certain speed. It’s quite a long code but great results. Let’s get started!

So first things first, you want to insert a Server Script, place it in a model, and set the RunContext to Client.

Now we would want to create a folder named “Explosions” and place it inside the workspace to store our explosions.
image

If you have a model already, create a folder inside the model and name it “Parts” and store any parts you want to be able to break.



Ok, now we want to set the network ownership of our model to a player you want for smooth physics.
Create an ObjectValue called “OwnerValue” and place it inside the model.
Insert a Server Script inside the model and paste this code in:

local model = script.Parent
local Parts = model:WaitForChild("Parts")
local VehicleSeat = Parts:WaitForChild("VehicleSeat")

local OwnerValue = model:WaitForChild("OwnerValue")
local oldestPlayer = nil

local function SetNetworkOwner(owner)
	OwnerValue.Value = owner
	
	for i,v in ipairs(model:GetDescendants()) do
		if v:IsA("BasePart") then
			local result = v:CanSetNetworkOwnership() -- checks if it can be set
			
			if result then
				v:SetNetworkOwner(owner)
			end
		end
	end
end

local function CheckOwnership()
	if VehicleSeat.Occupant then
		local player = game.Players:GetPlayerFromCharacter(VehicleSeat.Occupant.Parent)

		if player then
			SetNetworkOwner(player)
		end
	else
		if not OwnerValue.Value and oldestPlayer then -- if there is no owner of the vehicle, set the ownership to the oldest player
			SetNetworkOwner(oldestPlayer)
		end
	end
end

VehicleSeat:GetPropertyChangedSignal("Occupant"):Connect(function() -- if the occupant has changed
	CheckOwnership()
end)

CheckOwnership()

--------------------------------------------------------------------------------------------------

game.Players.PlayerAdded:Once(function(player)
	oldestPlayer = player -- the player that joined the game first will be the oldest player in the server
	CheckOwnership()
end)

image



Now we will create the crash system.

Create a Server Script inside the model and name it “CrashHandler”.
Set the RunContext to Client.
image

Now we will start coding our crash system!

Here are some variables you will need:

local RunService = game:GetService("RunService")

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Remotes = ReplicatedStorage:WaitForChild("Remotes") -- create a folder in RS and name it "Remotes"

local model = script.Parent -- our model
local Parts = model:WaitForChild("Parts") -- our Parts folder

local blastPower = 50 -- the power to blast the part away after break

local crashSpeed = 10 -- minimum speed to break
local crashSpeedY = 10 -- minimum speed from falling to break

local explodedParts = {} -- store parts that have already been crashed
local previousPartVelocities = {} -- store the part's previous velocities
local velocityConnections = {} -- store our velocity loops in this table
local velocityCoroutineConnections = {} -- store our velocity coroutines in this table

local Explosions = workspace:WaitForChild("Explosions")


Now we will need some functions to handle this:

local function IsAPlayer(hit)
	local character = hit:FindFirstAncestorWhichIsA("Model") -- checks if "hit" is inside a model

	if character then
		local player = game.Players:GetPlayerFromCharacter(character) -- if it really is a player

		if player then
			return true -- its a player
		end
	end
end

local function CheckIfPartIsConnected(part)
	local getConnectedParts = part:GetConnectedParts(true) -- get every part that is connected to that part
	local connectedParts = #getConnectedParts -- total parts connected to that part

	if connectedParts > 0 then -- if there is more than 0 parts connected to that part
		return true -- it is connected
	end
end

local function CanBreak(part, hit, previousVelocity)
	if previousVelocity then
		local speed = math.abs((previousVelocity - hit.AssemblyLinearVelocity).Magnitude) -- speed of the traveling part
		local yVelocity = math.abs(previousVelocity.Y - hit.AssemblyLinearVelocity.Y) -- speed of the part that is falling

		local isExploded = table.find(explodedParts, hit) -- checks if "hit" is inside the table

		if not isExploded then -- if it hasen't crashed
			if hit.Position.Y < part.Position.Y then -- if the part is higher than the "hit" part
				if yVelocity >= crashSpeedY then
					return true -- can be broken
				end
			else
				if speed >= crashSpeed then -- if it is at the minimum speed
					return true -- can be broken
				end
			end
		end
	end
end

More functions…

local function BreakPart(part)
	part:BreakJoints() -- breaks every joint inside the parts like welds

	if previousPartVelocities[part] then -- if it exists
		previousPartVelocities[part] = nil -- make it not exist
	end

	if velocityConnections[part] then
		velocityConnections[part]:Disconnect() -- disconnect the loop
		velocityConnections[part] = nil
	end

	if velocityCoroutineConnections[part] then
		coroutine.close(velocityCoroutineConnections[part]) -- stop the coroutine
		velocityCoroutineConnections[part] = nil
	end
end

local function Explode(part)
	BreakPart(part)

	local isConnected = CheckIfPartIsConnected(part) -- if the part is connected to anything
	local isExploded = table.find(explodedParts, part) -- if its has crashed

	if isConnected and not isExploded then -- if its connected and hasn't crashed
		table.insert(explodedParts, part) -- insert inside the table to exclude it

		local explosion = Instance.new("Explosion")
		explosion.BlastPressure = 0
		explosion.BlastRadius = 3 -- adjust this radius to your likings
		explosion.DestroyJointRadiusPercent = 0
		explosion.Position = part.Position
		explosion.Visible = false
		explosion.Parent = Explosions

		Remotes.ExplodePart:FireServer(part, explosion.Position, blastPower)

		explosion.Hit:Connect(function(hit)
			local isConnected = CheckIfPartIsConnected(hit)
			local isExploded = table.find(explodedParts, hit)

			if isConnected and not isExploded then
				table.insert(explodedParts, hit)

				BreakPart(hit) -- break the part before breaking it in the server for smoothness
				Remotes.ExplodePart:FireServer(hit, explosion.Position, blastPower) -- sends a remote to the server to break the part
			end
		end)
	end
end

local function Crash(part, hit, previousVelocity)
	local canBreak = CanBreak(part, hit, previousVelocity) -- checks if part can be broken

	if canBreak then -- if so..
		Explode(part)
	end
end

local function CheckHit(part)
	previousPartVelocities[part] = part.AssemblyLinearVelocity -- set the previous velocity of the part

	part.Touched:Connect(function(hit)
		local previousVelocity = previousPartVelocities[part] -- get the part previous velocity
		local isAPlayer = IsAPlayer(hit) -- if the part has hit a player

		if not isAPlayer then -- exclude the player
			Crash(part, hit, previousVelocity)
		end
	end)

	velocityCoroutineConnections[part] = coroutine.wrap(function()
		velocityConnections[part] = RunService.Heartbeat:Connect(function()
			if previousPartVelocities[part] then
				previousPartVelocities[part] = part.AssemblyLinearVelocity -- will always update the previous velocity because without it, if a part touches the ground, this would be the difference: (updated velocity: 80, non-updated velocity: 40) if you understand.
			end
		end)
	end)()
end

And now our final bits of code:

model.DescendantAdded:Connect(function(child) -- we do this because sometimes, parts load after the server has loaded
	if child:IsA("BasePart") and child:IsDescendantOf(Parts) then
		CheckHit(child)
	end
end)

for i,v in ipairs(Parts:GetDescendants()) do -- just in case
	if v:IsA("BasePart") and v:IsDescendantOf(Parts) then
		CheckHit(v)
	end
end

We are done now! This would be the full code:

local RunService = game:GetService("RunService")

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Remotes = ReplicatedStorage:WaitForChild("Remotes")

local model = script.Parent
local Parts = model:WaitForChild("Parts")

local blastPower = 50

local crashSpeed = 10
local crashSpeedY = 10

local explodedParts = {}
local previousPartVelocities = {}
local velocityConnections = {}
local velocityCoroutineConnections = {}

local Explosions = workspace:WaitForChild("Explosions")

local function IsAPlayer(hit)
	local character = hit:FindFirstAncestorWhichIsA("Model")

	if character then
		local player = game.Players:GetPlayerFromCharacter(character)

		if player then
			return true
		end
	end
end

local function CheckIfPartIsConnected(part)
	local getConnectedParts = part:GetConnectedParts(true)
	local connectedParts = #getConnectedParts

	if connectedParts > 0 then
		return true
	end
end

local function CanBreak(part, hit, previousVelocity)
	if previousVelocity then
		local speed = math.abs((previousVelocity - hit.AssemblyLinearVelocity).Magnitude)
		local yVelocity = math.abs(previousVelocity.Y - hit.AssemblyLinearVelocity.Y)

		local isExploded = table.find(explodedParts, hit)

		if not isExploded then
			if hit.Position.Y < part.Position.Y then
				if yVelocity >= crashSpeedY then
					return true
				end
			else
				if speed >= crashSpeed then
					return true
				end
			end
		end
	end
end

local function DestroyPart(part)
	part:BreakJoints()

	if previousPartVelocities[part] then
		previousPartVelocities[part] = nil
	end

	if velocityConnections[part] then
		velocityConnections[part]:Disconnect()
		velocityConnections[part] = nil
	end

	if velocityCoroutineConnections[part] then
		coroutine.close(velocityCoroutineConnections[part])
		velocityCoroutineConnections[part] = nil
	end
end

local function Explode(part)
	DestroyPart(part)

	local isConnected = CheckIfPartIsConnected(part)
	local isExploded = table.find(explodedParts, part)

	if isConnected and not isExploded then
		table.insert(explodedParts, part)

		local explosion = Instance.new("Explosion")
		explosion.BlastPressure = 0
		explosion.BlastRadius = 3
		explosion.DestroyJointRadiusPercent = 0
		explosion.Position = part.Position
		explosion.Visible = false
		explosion.Parent = Explosions

		Remotes.ExplodePart:FireServer(part, explosion.Position, blastPower)

		explosion.Hit:Connect(function(hit)
			local isConnected = CheckIfPartIsConnected(hit)
			local isExploded = table.find(explodedParts, hit)

			if isConnected and not isExploded then
				table.insert(explodedParts, hit)

				DestroyPart(hit)
				Remotes.ExplodePart:FireServer(hit, explosion.Position, blastPower)
			end
		end)
	end
end

local function Crash(part, hit, previousVelocity)
	local canBreak = CanBreak(part, hit, previousVelocity)

	if canBreak then
		Explode(part)
	end
end

local function CheckHit(part)
	previousPartVelocities[part] = part.AssemblyLinearVelocity

	part.Touched:Connect(function(hit)
		local previousVelocity = previousPartVelocities[part]
		local isAPlayer = IsAPlayer(hit)

		if not isAPlayer then
			Crash(part, hit, previousVelocity)
		end
	end)

	velocityCoroutineConnections[part] = coroutine.wrap(function()
		velocityConnections[part] = RunService.Heartbeat:Connect(function()
			if previousPartVelocities[part] then
				previousPartVelocities[part] = part.AssemblyLinearVelocity
			end
		end)
	end)()
end

model.DescendantAdded:Connect(function(child)
	if child:IsA("BasePart") and child:IsDescendantOf(Parts) then
		CheckHit(child)
	end
end)

for i,v in ipairs(Parts:GetDescendants()) do
	if v:IsA("BasePart") and v:IsDescendantOf(Parts) then
		CheckHit(v)
	end
end

This crash system works very good so enjoy!



Here are some results:





29 Likes

Hello there! I want to use this system but recreate it on my own, can you explain what the code does?

  1. It first creates a touched event for each basepart which runs a function that checks the touched event.

  2. On touched, it will check its previous velocity which is ran first before the velocity loop which changes its previous velocity so that it would print its velocity before it hits something.

  3. It will exclude the player so that it would break when hit by a player.

  4. It would check if the part is allowed to break by running the function CanBreak which would check if the “hit” part Y position is lower than the part which means if the part is above the “hit.”

  5. But if it isn’t above the “hit”, it would just check the speed and return true if it reaches its crash speed.

  6. It would check if the part is connected to anything so that it would reduce the performance to run the function even though the part is connected.

  7. Then it would break the part using an Explosion to get parts in the radius to also get destroyed if it isn’t been exploded already in the table which collects parts that have been exploded.

  8. It would fire a remote event to the server to launch the part away from the explosion.

Is this still being worked on? I saw this post when it came out but I have just stumbled upon it again today. I was interested in this system for messing around with random vehicles in roblox studio.