Detecting when a model is not grounded, not sure how to approach

Hello!

A core part of gameplay (for an upcoming game) is to stomp on buildings/ai’s and essentially “bully” them.

As you will see in this video that the building is made out of default bricks (4, 1, 2). When you use your ability, it raycasts (or however they do it) and gets all the parts hit and unanchors them, and applies a slight impulse on them. I have been able to figure most of it out, except that when the building can’t stand on its own, it falls down.

What I am confused about is how they detect that, or if they do something else.

The game

Right now, all of the parts are anchored at runtime. I have tried to keep them unanchored, but they end up toppling over and the abilities are rendered useless as they can just push them.

What our game is doing:

Before I recorded, I stomped all of the building and it didn’t fall down due to the fact that the parts are anchored. That is pretty much my problem.

It seems that they don’t have the parts anchored because otherwise all of the building would fall apart, it seems to be held by welds or something, I can’t quite figure out what they are doing.

Any help is appreciated, thanks…

They probably just welded parts to the parts they’re touching and kept everything unanchored. That’s the easiest solution.

The harder solution would be to basically run a flood fill from each of the parts that are touching the parts you just removed, and mark any regions that aren’t connected to the ground. It would be a fun idea, but the first is a bit more practical.

1 Like

Thanks for your reply! I have gone ahead and written a snippet of code I tested on a small model with default parts, I will update this if it works!

local lastPart

local function weld(part1, part2)
	local wc = Instance.new("WeldConstraint", part1)
	wc.Part0 = part1
	wc.Part1 = part2
end

for _, child in ipairs(script.Parent:GetChildren()) do
	if (child:IsA("BasePart")) then
		if (lastPart ~= nil) then
			weld(child, lastPart)
		end
		
		lastPart = child
	end
end

Not too bad, but you might have unexpected welds between arbitrary parts—just because they’re next to each other in that loop doesn’t mean that they’re physically next to each other.

1 Like

Hmm, makes sense. Do you have a better proposal for a solution? I’m still testing.

Hmmm you could try BasePart | Roblox Creator Documentation but that might not work well.

You can try just calling Model | Roblox Creator Documentation on the model but that method has been deprecated and might not work. I wish there was a replacement that used WeldConstraints.

As a final idea, you could write your own “GetTouchingParts” by creating a Region3 that’s slightly larger than the part itself, and then get all the parts inside that region.

Or if your parts are regularly spaced on a grid or something it’s much easier because you can just lookup on a grid.

1 Like

I’m not too familiar with Region3s…
Are you able to provide some pseudocode? I’m actively trying to find a solution.

Here’s some real code for you because I got interested!

It only really works if the parts are rotated only in 90 degree increments (no weird angles).

You can fix that by using Rotated Region 3 Module or something instead.

I tried to comment it up a bit, let me know if you have any questions.

It’s also possibly not very fast, so don’t run it too often :slight_smile:

-- creates a Region3 around a part which 
-- is slightly larger than the part.
-- requires that the part be rotated only
-- in 90 degree increments
local function GetRegionFromPart(part, distance)
    distance = distance or 0.05
    
    -- remove 90 degree rotations in the size
    -- in other words, get the size of the
    -- part as if it weren't rotated
    local size = part.CFrame:VectorToWorldSpace(part.Size)
    size = Vector3.new(math.abs(size.X), math.abs(size.X), math.abs(size.X))
    
    local offset = Vector3.new(distance, distance, distance)
    
    local lower = part.Position - size/2 - offset
    local upper = part.Position + size/2 + offset
    
    return Region3.new(lower, upper)
end

-- creates a WeldConstraint for each pair
-- of touching parts in the model.
-- only works well if the parts are axis-
-- aligned (i.e. no 45 degree angles)
local function WeldTouching(model)
    -- keep track of already welded
    local welded = {}

    local descendants = model:GetDescendants()
    
    for _, child in pairs(descendants) do
        if child:IsA("BasePart") then
            welded[child] = true
            local region = GetRegionFromPart(child)
            
            for _, connected in pairs(workspace:FindPartsInRegion3WithIgnoreList(region, descendants)) do
                -- don't double weld
                if connected ~= child and not welded[connected] then
                    local weld = Instance.new("WeldConstraint")
                    weld.Part0 = child
                    weld.Part1 = connected
                    weld.Parent = child
                end
            end
        end
    end
end

-- example usage
WeldTouching(workspace.Model)
2 Likes

Thank you!

I originally decided to use MakeJoints and it was extremely laggy… this eliminated it almost completely for me.

I appreciate you taking the time to help me with this.