So, I'm trying to create some destructible bricks.

The problem is that the :Touched event happens too late. By the time it gets triggered, the colliding part has already lost all its velocity. I want to predict when a part is about to hit this destructible brick and react accordingly.

There's a problem though.

There will be hundreds, if not thousands of destructible bricks in my game, and I want them to be collidable with anything.

That means, for example, if a car made of these destructible bricks crashes into anything, or if anything crashes into it, I want it to break into pieces.

The problem with this wish is that I can't figure out how in the world that would even be possible.

In my destructible ModuleScript, there is this:

local tracked = {}

local function disconnect(part)
	tracked[part] = nil
end

local function goingToTouch(part, otherPart)
	if #part:GetConnectedParts() < 2 then return end
	
	local damage = (otherPart.Velocity - part.Velocity).Magnitude
	
	if damage > 5 then
		part:BreakJoints()
		part.Velocity = otherPart.Velocity
		part.CanCollide = true
	end
end

local function connect(part)
	if part:IsA('BasePart') then
		tracked[part] = true
	end
end

local function getIVertices(cframe, size)
	local vertices = {}
    
    vertices[1] = cframe * CFrame.new(size.X *  0.5, size.Y *  0.5, size.Z *  0.5)
    vertices[2] = cframe * CFrame.new(size.X * -0.5, size.Y *  0.5, size.Z *  0.5)
    vertices[3] = cframe * CFrame.new(size.X *  0.5, size.Y * -0.5, size.Z *  0.5)
    vertices[4] = cframe * CFrame.new(size.X *  0.5, size.Y *  0.5, size.Z * -0.5)
    vertices[5] = cframe * CFrame.new(size.X * -0.5, size.Y * -0.5, size.Z *  0.5)
    vertices[6] = cframe * CFrame.new(size.X *  0.5, size.Y * -0.5, size.Z * -0.5)
    vertices[7] = cframe * CFrame.new(size.X * -0.5, size.Y *  0.5, size.Z * -0.5)
    vertices[8] = cframe * CFrame.new(size.X * -0.5, size.Y * -0.5, size.Z * -0.5)
	
	return vertices
end

local function pointInsideIPart(cframe, size, point)
	-- Not my code
	-- https://scriptinghelpers.org/questions/30656
	
	local rel = cframe:PointToObjectSpace(point)
	size = size / 2
	
	if rel.X < -size.X or rel.X > size.X then
		return false
	elseif rel.Y < -size.Y or rel.Y > size.Y then
		return false
	elseif rel.Z < -size.Z or rel.Z > size.Z then
		return false
	end
	
	return true 
end

local function doIPartsCollide(cf1, size1, cf2, size2)
	local vertices1 = getIVertices(cf1, size1)
	
	for _, vertex in ipairs(vertices1) do
		if pointInsideIPart(cf2, size2, vertex.Position) then
			return true
		end
	end
	
	local vertices2 = getIVertices(cf2, size2)
	
	for _, vertex in ipairs(vertices2) do
		if pointInsideIPart(cf1, size1, vertex.Position) then
			return true
		end
	end
	
	return false
end

local parts = {}

for _, desc in ipairs(workspace:GetDescendants()) do
	if desc:IsA('BasePart') then
		parts[desc] = true
	end
end

workspace.DescendantAdded:Connect(function(desc)
	if desc:IsA('BasePart') then
		parts[desc] = true
	end
end)

workspace.DescendantRemoving:Connect(function(desc)
	if desc:IsA('BasePart') then
		parts[desc] = nil
	end
end)

local connection
connection = game:GetService('RunService').Stepped:Connect(function(t)
	for part, _ in pairs(parts) do
		if part.CanCollide == true then
			for destructible, _ in pairs(tracked) do
				if (part.Velocity - destructible.Velocity).Magnitude > (part.CFrame.Position - destructible.CFrame.Position).Magnitude - (part.Size.Magnitude + destructible.Size.Magnitude) / 2 and doIPartsCollide(part.CFrame + part.Velocity * t, part.Size, destructible.CFrame + destructible.Velocity * t, destructible.Size) then
					goingToTouch(destructible, part)
				end
			end
		end
	end
	
	print('did the thing')
end)

return {connect = connect, disconnect = disconnect}

There is a MESS on line 103 that crushes ALL my performance. Commenting out 103-105 gives all the performance back, so I know it's the problem.

I don't even know if this is possible in ROBLOX using Lua. Should I just use Glue joints or something? What should I do?


P.S. The script that requires the ModuleScript uses CollectionService to keep track of the destructible bricks. I don't otherwise include it because it's pretty simple, but if you need to test it just put this into a ModuleScript in ReplicatedStorage called DestructibleCollection, and put the code I posted above in a child of the script (another ModuleScript) called DestructionModule:

local collectionService = game:GetService('CollectionService')
local destruction = require(script.DestructionModule)

for _, part in ipairs(collectionService:GetTagged('destructible')) do
	destruction.connect(part)
end

collectionService:GetInstanceAddedSignal('destructible'):Connect(destruction.connect)
collectionService:GetInstanceRemovedSignal('destructible'):Connect(destruction.disconnect)

return nil

And in a script somewhere else, both on client and server, I use this code:

local replicatedStorage = game:GetService('ReplicatedStorage')

require(replicatedStorage.DestructibleCollection)

Sorry if I left any info out, I couldn't revise this post much as I'm short on time. I'll review it later.