Well I’m finished, (as far as I know)
This code should be functional.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local BlocksPlace = game.Workspace.Blocks
local DistanceToCheckAbove = 25 --> Raycast will only go 25 studs up to check for parts before returning nil.
local DistanceToCheckSides = 10 --> Raycast will only go 10 studs (plus the size of the part to make it similar to casting from the edge)
ReplicatedStorage.place.OnServerEvent:Connect(function(player, Positiona, Matpicked)
if not ReplicatedStorage.Blocks:FindFirstChild(Matpicked) then
warn("Material not found: " .. Matpicked)
return
end
local shape = ReplicatedStorage.Blocks[Matpicked]:Clone()
local tag = Instance.new("StringValue")
tag.Name = "blockowner"
tag.Value = player.Name
tag.Parent = shape
shape.Size = Vector3.new(3, 3, 3)
shape.Anchored = true
shape.CFrame = Positiona
shape.Parent = BlocksPlace --> Always parent things after you've applied all the initial properties
end)
local RayParams = RaycastParams.new()
RayParams.FilterType = Enum.RaycastFilterType.Exclude
RayParams.IgnoreWater = true
local function RecursiveGetParts(Part) --> Recursively check if there's a part above the last
local Parts = {} --> Make table to store the parts we find.
local Current = Part --> Make a variable to store the current part we're casting from.
local Result = nil --> Make a variable to store the RaycastResult (this helps with breaking the recursive loop)
repeat
RayParams.FilterDescendantsInstances = {Current} --> Update filter so we don't just get the same part each time.
local Origin = Current.Position --> Current part position (we will raycast from this)
local Direction = Vector3.new(0,DistanceToCheck,0) --> We want the ray to go vertically, or up, so we use 0,DistanceToCheck,0
Result = workspace:Raycast(Origin, Direction, RayParams) --> Raycast from Origin, following Direction, and use our RaycastParams to filter the cast. Then store the result in our Result variable.
if Result --> Ensure we did get a result from the Raycast.
and Result.Instance --> Ensure the Raycast returned an object.
and Result.Instance:IsA("BasePart") --> Ensure it's a BasePart (covers all Part types, such as MeshPart, Part, CornerWedge, etc)
and Result.Instance:FindFirstChild("blockowner") --> Ensure it's a placed part (Has "blockowner" value)
and not table.find(Parts, Result.Instance) --> Ensure we haven't already gotten this part.
and not Result.Instance == Current --> Ensure the part we got was not our current part (should be impossible but this is a failsafe to prevent an infinite loop)
then
table.insert(Parts, Result.Instance) --> Insert the new part to the Parts storage table.
Current = Result.Instance --> Set the next target (Current) to the part we got.
else
break --> In case any of our failsafes/checks go off, we should break the loop to prevent an infinite loop.
end
until Result == nil or (Result and Result.Instance == nil) --> If the Raycast does not return a result, or there is no Result.Instance, we break the loop.
return Parts --> Return the parts list
end
local function CheckIfTouching(Part) --> Check if a part is touching any other parts
RayParams.FilterDescendantsInstances = {Part} --> Update filter
local Touching = false --> Cache Touching so we can set it later
local Origin = Part.Position
local Sides = { --> Define side directions
Vector3.new(DistanceToCheckSides,0,0) + (Part.Size * Vector3.new(0.5,0,0)), --> Left
Vector3.new(-DistanceToCheckSides,0,0) - (Part.Size * Vector3.new(0.5,0,0)), --> Right
Vector3.new(0,0,DistanceToCheckSides) + (Part.Size * Vector3.new(0,0,0.5)), --> Front
Vector3.new(0,0,-DistanceToCheckSides) - (Part.Size * Vector3.new(0,0,0.5)) --> Back
}
for _, Side in pairs(Sides) do --> Loop through Sides
local Result = workspace:Raycast(Origin, Side, RayParams) --> Raycast with current side direction and our filter RayParams.
if Result and Result.Instance and Result.Instance:IsA("Part") and Result.Instance:FindFirstChild("blockowner") then --> Same checks as in RecursiveGetParts, besides the checks for the Current part and table.find
Touching = true --> We found a part that we're touching!
break --> We don't need to continue through the rest of the sides.
end
end
return Touching --> Return Touching as it tells us if it's touching or not.
end
local function GetPartsAboveNotTouching(part) --> Get all parts that are above the target AND actually floating (not touching another part) (This option is more intensive than the other function) (option 1)
local List = RecursiveGetParts(part) --> The following code checks to see if the parts are actually floating (does not touch anything else)
for Index, Part in ipairs(List) do --> Loop through all the parts
if CheckIfTouching(Part) then --> Check if the part is touching something
table.remove(List, Index) --> If it's touching, remove the part from the list.
end
end
return List
end
local function GetPartsAbove(part) --> Get all parts that are above the target, regardless of if they are touching anything (option 2)
return RecursiveGetParts(part) --> The following code checks to see if the parts are actually floating (does not touch anything else)
end
ReplicatedStorage.del.OnServerEvent:Connect(function(player, target)
if target and target:IsA("BasePart") then
local Floating = GetPartsAbove(target) --> You can use either GetPartsAbove(target) or GetPartsAboveNotTouching(target) to get these parts. GetPartsAboveNotTouching will be more performance heavy.
-- Now that we've got all the floating parts, we can do something with them
for _, Part in pairs(Floating) do
Part.Anchored = false -- In this case, we'll just unanchor the part so it falls, you can make this do whatever you want.
end
target:Destroy()
else
warn("Invalid target for deletion")
end
end)
Let me know if anything goes wrong, breaks, or otherwise does not work.
I’ve added comments to explain everything I changed and to explain the different options I added.