I don’t know why it floating I check in the server and everything should fall correctly but it didn’t idk what is going on
code (there a lot of stuff that I didn’t use idk and messy af)
local Physic = {}
local RunService = game:GetService("RunService")
local CollectionService = game:GetService("CollectionService")
local Blink = require(game.ReplicatedStorage.Blink.Server)
local Config = require(script.Parent.Config)
local MsConvert = require(script.Parent.MSConvert)
local WeldCache = require(script.WeldCache)
local Queuer = require(script.Parent.Queue)
local SAT = require(script.Parent.SAT)
local Cache = WeldCache.new(10000, 100)
local Visited = {}
local WeldQueue = {}
local WeldLookup = {}
local IDS = {}
local DeleteTable = {}
local ID = 0
local FallID = 0
local function assignId(a: BasePart)
local id = IDS[a]
if not id then
ID += 1
id = ID
IDS[a] = id
end
return id
end
local function getKey(a: BasePart, b: BasePart): string
if a:HasTag("Anchored") and not b:HasTag("Anchored") then
a, b = b, a
end
local aKey, bKey = assignId(a), assignId(b)
if aKey < bKey then
return aKey.."_"..bKey
else
return bKey.."_"..aKey
end
end
local function hasRequiredTag(part)
return part:HasTag("Anchored") or part:HasTag(Config.tag)
end
local function CanConnect(a : BasePart, b : BasePart)
if hasRequiredTag(a) ~= hasRequiredTag(b) then
return false
end
if a:HasTag("NoPhysics") ~= b:HasTag("NoPhysics") then
return false
end
local aFall = a:GetAttribute("_FALLID")
local bFall = b:GetAttribute("_FALLID")
if aFall or bFall then
if aFall ~= bFall then
return false
end
end
return true
end
local function tblToLookup(tbl)
local lookup = {}
for _, value in ipairs(tbl) do
lookup[value] = true
end
return lookup
end
local function GetConnected(Part : BasePart,IgnoreParts : {BasePart},IsPreWelding : boolean)
local Start = os.clock()
local Optimize = tblToLookup(IgnoreParts)
local Queue = {Part}
local Results = {} -- :P
local IsTouchingGround = false
while #Queue > 0 do
local currentPart = table.remove(Queue, 1)
if not Visited[currentPart] and not Optimize[currentPart] then
Visited[currentPart] = true
table.insert(Results,currentPart)
if currentPart:HasTag("Anchored") or currentPart:HasTag("NoPhysics") then
IsTouchingGround = true
end
local neighbors = workspace:GetPartBoundsInBox(currentPart.CFrame, currentPart.Size + vector.create(0.001,0.001,0.001))
for _,neighbor in pairs(neighbors) do
if neighbor ~= currentPart and not Visited[neighbor] and not Optimize[neighbor] then
if CanConnect(currentPart,neighbor) then
local Key = getKey(currentPart,neighbor)
if neighbor:IsA("Part") then
neighbor.Shape = Enum.PartType.Block
end
if neighbor:HasTag("Anchored") or neighbor:HasTag("NoPhysics") then
IsTouchingGround = true
end
if IsPreWelding then
table.insert(Queue,neighbor)
else
if not neighbor:HasTag("Anchored") then
table.insert(Queue,neighbor)
end
end
if not WeldLookup[Key] then
WeldLookup[Key] = true
table.insert(WeldQueue,{currentPart,neighbor})
end
end
end
end
end
end
--print("[DEBUG] Search Time :",MsConvert.Convert(Start))
return Results,IsTouchingGround
end
local function getLayers(parts, layerThreshold)
layerThreshold = layerThreshold or 2
local layers = {}
local currentLayer = {}
local lastY = nil
for _, part in ipairs(parts) do
if part:HasTag("Anchored") then continue end
if not lastY or part.Position.Y - lastY > layerThreshold then
if #currentLayer > 0 then
table.insert(layers, currentLayer)
end
currentLayer = {}
end
table.insert(currentLayer, part)
lastY = part.Position.Y
end
if #currentLayer > 0 then
table.insert(layers, currentLayer)
end
return layers
end
local RunService = game:GetService("RunService")
local function CheckIntegrity(parts : {BasePart}, layerThreshold : number)
layerThreshold = layerThreshold or 2
local layers = getLayers(parts, layerThreshold)
if not layers[1] or #layers[1] == 0 then
warn("No bottom layer found")
return false
end
local failedLayers = {}
local totalMass = 0
for i = 1, #layers - 1 do
local lower = layers[i]
local upper = layers[i + 1]
local lowerMass, upperMass = 0, 0
for _, p in ipairs(lower) do
if p:IsA("BasePart") then
lowerMass += p:GetMass() + totalMass
end
end
totalMass += lowerMass
for _, p in ipairs(upper) do
if p:IsA("BasePart") then
upperMass += p:GetMass()
end
end
if lowerMass < upperMass then
print(("Layer %d failed! Lower mass: %.2f < Upper mass: %.2f"):format(i, lowerMass, upperMass))
table.insert(failedLayers,lower)
end
end
return true,failedLayers
end
local function getShape(visualize)
local destroyableParts = CollectionService:GetTagged("Destroyable")
local visited = {}
local clusters = {}
local function findCluster(startPart)
local cluster = {}
local stack = {startPart}
while #stack > 0 do
local current = table.remove(stack)
if not visited[current] then
visited[current] = true
table.insert(cluster, current)
local touching = workspace:GetPartBoundsInBox(current.CFrame, current.Size + vector.create(0.001,0.001,0.001))
for _, otherPart in ipairs(touching) do
if not visited[otherPart] and otherPart ~= current then
table.insert(stack, otherPart)
end
end
end
end
return cluster
end
local function checkTouchGround(cluster)
for _, clusterPart in ipairs(cluster) do
if clusterPart:GetAttribute("_FALLID") then
return false
end
end
local anchoredParts = CollectionService:GetTagged("Anchored")
local params = OverlapParams.new()
params.FilterDescendantsInstances = {anchoredParts}
params.FilterType = Enum.RaycastFilterType.Include
local center,size = getBoundingBox(cluster)
local parts = workspace:GetPartBoundsInBox(center,size,params)
if #parts > 0 then
return true
end
return false
end
for _, part in ipairs(destroyableParts) do
if not visited[part] then
local cluster = findCluster(part)
if #cluster > 0 then
local firstPart = cluster[1]
local id = firstPart:GetAttribute("_CLUSTERID")
if not id then
id = tick() + math.random()
for _, p in ipairs(cluster) do
p:SetAttribute("_CLUSTERID", id)
end
end
local clusterData = {
parts = cluster,
touchGround = checkTouchGround(cluster),
id = id,
}
table.insert(clusters, clusterData)
end
end
end
if visualize then
for i, clusterData in ipairs(clusters) do
local model = Instance.new("Model")
model.Name = "Cluster_" .. i .. (clusterData.touchGround and "_Grounded" or "_Floating")
model.Parent = workspace
local highlight = Instance.new("Highlight")
highlight.FillColor = clusterData.touchGround and Color3.new(0, 1, 0) or Color3.new(1, 0, 0)
highlight.OutlineColor = Color3.new(1, 1, 1)
highlight.Parent = model
for _, part in ipairs(clusterData.parts) do
part.Parent = model
end
end
end
return clusters
end
local function extractPartData(parts)
local data = table.create(#parts)
for i, part in ipairs(parts) do
data[i] = {
CFrame = part.CFrame,
Size = part.Size,
}
end
return data
end
function getBoundingBox(parts)
if #parts == 0 then return nil end
if #parts == 1 then
local p = parts[1]
return p.CFrame, p.Size
end
local data = extractPartData(parts)
task.desynchronize()
local centerPos = Vector3.zero
for i = 1, #data do
centerPos += data[i].CFrame.Position
end
centerPos /= #data
local bestCF = CFrame.new(centerPos)
local bestVolume = math.huge
for i = 1, #data do
local partCF = data[i].CFrame
local testCF = CFrame.new(centerPos) * (partCF - partCF.Position)
local minX, minY, minZ = math.huge, math.huge, math.huge
local maxX, maxY, maxZ = -math.huge, -math.huge, -math.huge
for j = 1, #data do
local p = data[j]
local relCF = testCF:Inverse() * p.CFrame
local size = p.Size
local sx, sy, sz = size.X / 2, size.Y / 2, size.Z / 2
for _, corner in ipairs({
Vector3.new(sx, sy, sz),
Vector3.new(sx, sy, -sz),
Vector3.new(sx, -sy, sz),
Vector3.new(sx, -sy, -sz),
Vector3.new(-sx, sy, sz),
Vector3.new(-sx, sy, -sz),
Vector3.new(-sx, -sy, sz),
Vector3.new(-sx, -sy, -sz),
}) do
local worldCorner = relCF * corner
minX = math.min(minX, worldCorner.X)
minY = math.min(minY, worldCorner.Y)
minZ = math.min(minZ, worldCorner.Z)
maxX = math.max(maxX, worldCorner.X)
maxY = math.max(maxY, worldCorner.Y)
maxZ = math.max(maxZ, worldCorner.Z)
end
end
local volume = (maxX - minX) * (maxY - minY) * (maxZ - minZ)
if volume < bestVolume then
bestVolume = volume
bestCF = testCF * CFrame.new(
(minX + maxX) / 2,
(minY + maxY) / 2,
(minZ + maxZ) / 2
)
end
end
local minX, minY, minZ = math.huge, math.huge, math.huge
local maxX, maxY, maxZ = -math.huge, -math.huge, -math.huge
for i = 1, #data do
local relCF = bestCF:Inverse() * data[i].CFrame
local size = data[i].Size
local sx, sy, sz = size.X / 2, size.Y / 2, size.Z / 2
for _, corner in ipairs({
Vector3.new(sx, sy, sz),
Vector3.new(sx, sy, -sz),
Vector3.new(sx, -sy, sz),
Vector3.new(sx, -sy, -sz),
Vector3.new(-sx, sy, sz),
Vector3.new(-sx, sy, -sz),
Vector3.new(-sx, -sy, sz),
Vector3.new(-sx, -sy, -sz),
}) do
local worldCorner = relCF * corner
minX = math.min(minX, worldCorner.X)
minY = math.min(minY, worldCorner.Y)
minZ = math.min(minZ, worldCorner.Z)
maxX = math.max(maxX, worldCorner.X)
maxY = math.max(maxY, worldCorner.Y)
maxZ = math.max(maxZ, worldCorner.Z)
end
end
task.synchronize()
local size = Vector3.new(maxX - minX, maxY - minY, maxZ - minZ)
return bestCF, size
end
local function getCenterPart(cluster, cf)
local center, bestPart, bestDist = cf.Position, nil, math.huge
for _, part in ipairs(cluster.parts) do
local dist = (part.Position - center).Magnitude
if dist < bestDist then
bestDist = dist
bestPart = part
end
end
return bestPart
end
function Physic.Process(Parts : {BasePart},IgnoreParts : {BasePart},Voxels : {BasePart},IsPreWelding : boolean)
Parts = Parts or {}
IgnoreParts = IgnoreParts or {}
Voxels = Voxels or {}
IsPreWelding = IsPreWelding or false
WeldQueue = {}
DeleteTable = {}
Visited = {}
local queue = Queuer:Fetch(Parts) or Queuer.New(Parts,false)
if #Voxels > 0 then
for _,voxel in pairs(Voxels) do
local parts = workspace:GetPartBoundsInBox(voxel.CFrame,voxel.Size + vector.create(0.01,0.01,0.01))
for _,part in pairs(parts) do
if not hasRequiredTag(part) then continue end
table.insert(Parts,part)
end
end
end
local processTable = {}
local s = os.clock()
for _,part in pairs(Parts) do
if not Visited[part] then
local connected,isTouchingGround = GetConnected(part,IgnoreParts,IsPreWelding)
if not isTouchingGround then
FallID += 1
end
for i,v in pairs(connected) do
if not v:HasTag("Anchored") then
table.insert(processTable,v)
if not isTouchingGround then
--v.Parent = workspace.Fall
local id = v:GetAttribute("_IDVoxel")
if id then
table.insert(DeleteTable,id)
end
if not v:GetAttribute("_FALLID") then
v:SetAttribute("_FALLID",FallID)
end
else
if v:HasTag("NoPhysics") then
local id = v:GetAttribute("_IDVoxel")
if id then
table.insert(DeleteTable,id)
end
end
end
end
end
end
end
for i,data in pairs(WeldQueue) do
local Weld = Cache:getWeld()
Weld.Part0 = data[1]
Weld.Part1 = data[2]
Weld.Parent = data[1]
end
--print("Total :",MsConvert.Convert(s))
--print(#processTable)
for _,part in pairs(processTable) do
part.Anchored = false
end
Blink.DeleteEvent.FireAll(DeleteTable)
--[[
local list = getShape()
for _,cluster in pairs(list) do
local isStable,failedLayers = CheckIntegrity(cluster.parts)
end
]]
end
--[[
local hitboxes = {}
RunService.Heartbeat:Connect(function()
local list = getShape()
for _, cluster in pairs(list) do
local vel = cluster.parts[1].AssemblyLinearVelocity
local id = cluster.id
if vel.Magnitude > 2 then
if not cluster.touchGround then
local cf, size = getBoundingBox(cluster.parts)
local hitbox = hitboxes[id]
if not hitbox or not hitbox.Parent then
hitbox = Instance.new("Part")
hitbox.Anchored = true
hitbox.CastShadow = false
hitbox.Transparency = 0.5
hitbox.CanCollide = false
hitbox.Size = size
hitbox.CFrame = cf
hitbox.Parent = workspace
local centerPart = getCenterPart(cluster,cf)
local weld = Instance.new("WeldConstraint")
weld.Part0 = hitbox
weld.Part1 = centerPart
weld.Parent = hitbox
hitboxes[id] = hitbox
end
game.ReplicatedStorage.Assets.Remotes.Bindable.Impact:Fire(hitbox, cluster.parts)
end
else
if hitboxes[id] then
hitboxes[id]:Destroy()
hitboxes[id] = nil
end
end
end
end)
]]
return Physic

