Creating Soft Destruction

I will be showing you how you can make soft destruction in Roblox! When you try creating destruction, the part would always bounce back? This tutorial will prevent that and provide a smoother destruction system.

Video:

Code:

local RunService = game:GetService("RunService")

local model = script.Parent

local breakThreshold = 1 -- minimum impact speed

local previousVelocities = {} -- stores previous velocities so we can accurately get its velocity after impact

local function CalculateImpactCoefficient(direction, velocityDirection)
	local dotProduct = velocityDirection:Dot(direction)
	local reflectedVelocity = velocityDirection - 2 * dotProduct * direction
	local coefficient = math.clamp(math.abs(reflectedVelocity:Dot(direction)), 0, 1)

	return coefficient
end

local function HasJoints(part)
	local joints = part:GetJoints()

	for i,v in ipairs(joints) do
		if v:IsA("WeldConstraint") then
			return true
		end
	end
end

local function SetUp(part)
	part.Touched:Connect(function(hit)
		if HasJoints(part) then
			local origin = part.Position
			local velocity = previousVelocities[part] or part.AssemblyLinearVelocity -- get its previous velocity
			local mass = part.AssemblyMass -- its total mass including parts connected to that part

			local point = hit:GetClosestPointOnSurface(origin) -- the position on the part that's closest to the origin
			local direction = (origin - point).Unit -- direction from the origin to the point

			local otherVelocity = hit.AssemblyLinearVelocity
			local otherMass = hit.AssemblyMass

			local relativeVelocity = velocity - otherVelocity -- prevent breaking when moving in same direction with same speed
			local coefficient = CalculateImpactCoefficient(direction, relativeVelocity.Unit) -- how much the part will bounce back
			local massRatio = math.clamp((mass + otherMass) / mass - 1, 0, 1) -- more breakable when hit by a heavy object
			local force = relativeVelocity.Magnitude * coefficient * massRatio -- calculate the force of the impact

			if force >= breakThreshold then -- if the force is enough
				local connectedParts = part:GetConnectedParts(true) -- get all parts connected to that part and parts connected to that other part
				part:BreakJoints() -- break all part joints

				local newVelocity = velocity * coefficient -- get the new velocity after impact

				for i,v in ipairs(connectedParts) do
					v.AssemblyLinearVelocity = newVelocity -- maintain its velocity so that it doesn't bounce back after impact
					break -- we don't need to apply it more than once
				end
			end
		end
	end)
end

for i,v in ipairs(model:GetDescendants()) do
	if v:IsA("BasePart") then
		SetUp(v)
	end
end

model.DescendantAdded:Connect(function(descendant)
	if descendant:IsA("BasePart") then
		SetUp(descendant)
	end
end)

RunService.Stepped:Connect(function()
	for i,v in ipairs(model:GetDescendants()) do
		if v:IsA("BasePart") then
			previousVelocities[v] = v.AssemblyLinearVelocity
		end
	end
end)
12 Likes

I love watching it break apart (I watched it like 20 times). Could you please include a model or a .rbxl?

5 Likes

Destructable Cube.rbxl (55.1 KB)

3 Likes

I also created a game that has brick destruction. It uses a different code but also amazing to watch.

Link:

2 Likes

this is pretty cool. limit of 30