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.
