Looking for physicists / mathematicians with deep understanding of rigid body physics

I am willing to pay an amount as high as you want, because I don’t even expect there to be physicists or mathematicians who would even play Roblox to begin with, so finding a real one would be a blessing for me, i won’t even mind 100k robux

The first thing I want you to think of is Happy Wheels, because I’m technically trying to replicate the game physics inside Roblox (Box2D physics)

(game name: Blox and Glory)
image

image

But the thing is, it’s very difficulty to calculate the accurate physical properties in a way that would result in realistic damage system, as those numbers aren’t natively exposed to us

||||||||||||||||||||||||||||||||||||||

Below is the detailed documentation of how all the physics in my game work

There are 2 limb types
1. Smashable limbs (torso and head)
2. Snappable limbs (arms, legs and neck), also consider the joints between each limb

||||||||||||||||||||||||||||||||||||||

Let’s start off with smashable limbs as they’re more straightforward

Our example will be a ball flying at the torso, exploding it
smashable pic 1
smashable pic 2

Now to calculate the dynamic impact force, the velocity vector (direction and magnitude) and the center of mass of the torso plays an important factor in the calculation

If the ball’s velocity vector directs at the center of mass of the torso, the damage will be maximum
smashable pic 3

Whereas if the ball’s velocity vector if offsetted near the edges, the damage will be lower
smashable pic 4

Now all this time the velocity vector is still perpendicular to the surface’s normal vector, if it’s tilted, the damage would be even lower (if formed angle is 90 degrees then damage should be non-existent)

So, it concludes that the damage for smashable limbs is based on:

  • The distance between the center of mass and the velocity vector
  • The angle between the surface’s normal vector and the velocity vector
  • The magnitude of the velocity vector

(Also make sure to take account of the torso’s linear velocity and angular velocity, aka impact relativity)

And as you guessed it, this is where my question for finding the touching point between 2 points and their touching surface (to find the surface’s normal vector) comes to play. But I would also like to know what is the most reliable unit of measures for determining damage, is it the delta impulse between the 2 objects upon touching?

||||||||||||||||||||||||||||||||||||||

Now for snappable limbs, which involve things not even I know how to observe

Let’s use the example of a player’s leg falling / landing on the ground, depending on the angle and touch point, it will determine whether the leg is gonna snap or not

snappable pic 1
Easy observation tells that the force inflicted closest to the joint deals the least damage, whereas the one furthest deals the most damage

snappable pic 2
However, when the leg is perpendicular to the ground, it becomes hard to determine the damage; the biggest dilemma with this is that the leg is the limb that has to fall onto the ground the most, and its most likely to break, so the damage formula here has to be merciful on that too

snappable pic 3
And of course, the pull force, in which I am completely clueless about.
What is the trigger condition for it? It certainly doesn’t involve .Touched, does it have to be a .Heartbeat / loop that checks the force threshold constantly? What unit of measure does it operate on? Is it a vector? Where does it apply at?

||||||||||||||||||||||||||||||||||||||

P.S: I have come to another realization that a leg falling onto the ground is ENTIRELY DIFFERENT from a leg being hit by a ball (similar to the smashable limb example), since it’s about pressure (area of touch), not just touch points. I could document even more about that, but I’m afraid every reader on this post is gonna have a stroke comprehending all that

for your first challenge which was its hard to calculate the interaction point between parts, simply try like region3 or part:GetTouchingParts(), these methods gets the touching part, but if you want the exact position or list of contact positions

local function getBoxOverlapPoints(box1, box2)
	local cf1, size1 = box1.CFrame, box1.Size
	local cf2, size2 = box2.CFrame, box2.Size

	local function getBoxCorners(cf, size)
		local corners = {}
		local sx, sy, sz = size.X / 2, size.Y / 2, size.Z / 2
		for x = -1, 1, 2 do
			for y = -1, 1, 2 do
				for z = -1, 1, 2 do
					table.insert(corners, cf * Vector3.new(x * sx, y * sy, z * sz))
				end
			end
		end
		return corners
	end

	
	local function boxesIntersect(corners1, corners2)
		return true
	end

	
	local function linePlaneIntersection(lineStart, lineEnd, planePoint, planeNormal)
		local lineDir = (lineEnd - lineStart).Unit
		local dot = planeNormal:Dot(lineDir)

		
		if math.abs(dot) < 1e-6 then
			return nil
		end

		local t = (planeNormal:Dot(planePoint - lineStart)) / dot
		if t < 0 or t > (lineEnd - lineStart).Magnitude then
			return nil -- Intersection is outside the line segment
		end

		return lineStart + lineDir * t
	end

	
	local function getBoxFaces(corners)
		return {
			{corners[1], corners[2], corners[3], corners[4]}, -- Front face
			{corners[5], corners[6], corners[7], corners[8]}, -- Back face
			{corners[1], corners[2], corners[6], corners[5]}, -- Right face
			{corners[3], corners[4], corners[8], corners[7]}, -- Left face
			{corners[1], corners[4], corners[8], corners[5]}, -- Top face
			{corners[2], corners[3], corners[7], corners[6]}  -- Bottom face
		}
	end

	
	local corners1 = getBoxCorners(cf1, size1)
	local corners2 = getBoxCorners(cf2, size2)

	
	if not boxesIntersect(corners1, corners2) then
		return {}
	end

	
	local faces2 = getBoxFaces(corners2)

	local intersectionPoints = {}

	
	for i = 1, #corners1 do
		for j = i + 1, #corners1 do
			local edgeStart = corners1[i]
			local edgeEnd = corners1[j]

			
			for _, face in pairs(faces2) do
				
				local planePoint = face[1]
				local planeNormal = (face[2] - face[1]):Cross(face[3] - face[1]).Unit

				
				local intersectionPoint = linePlaneIntersection(edgeStart, edgeEnd, planePoint, planeNormal)
				if intersectionPoint then
					
					local isInside = true
					for k = 1, #face do
						local nextK = k % #face + 1
						local edge = face[nextK] - face[k]
						local toPoint = intersectionPoint - face[k]
						if edge:Cross(toPoint):Dot(planeNormal) < 0 then
							isInside = false
							break
						end
					end

					if isInside then
						table.insert(intersectionPoints, intersectionPoint)
					end
				end
			end
		end
	end

	return intersectionPoints
end


local box1 = workspace.Box1
local box2 = workspace.Box2
local overlapPoints = getBoxOverlapPoints(box1, box2)
for _, pos in pairs(overlapPoints) do
	local Part = Instance.new("Part", workspace)
	Part.Size = Vector3.new(0.1, 0.1, 0.1)
	Part.Color = Color3.fromRGB(255, 217, 0)
	Part.Anchored = true
	Part.Position = pos
end

this sample code


it gets the list of position where these 2 parts touch, it doesn’t get the full area that they touch and i assume it only works with cubes not spheres or mesh parts. plus it may not meet your need let me know if you have any problem with it,

when calculating normal vectors of touching surface, especially for mesh and unions, its challenging due to complex geometry as meshes and unions have irregular shapes, plus trying to get the precision by getting the normals in a mesh/union is a recipe for a bad performance depending on how complex the mesh/union is if done in real time, However, there are ways to approximate or calculate the normal vector, even for non conventional parts such as getting mesh

local function getContactPointsAndNormals(part1, part2)
    local contactPoints = {}
    local contactNormals = {}

    
    for x = -1, 1, 0.5 do
        for y = -1, 1, 0.5 do
            for z = -1, 1, 0.5 do
                local rayOrigin = part1.Position + Vector3.new(x, y, z)
                local rayDirection = (part2.Position - part1.Position).Unit
                local raycastParams = RaycastParams.new()
                raycastParams.FilterDescendantsInstances = {part1}
                raycastParams.FilterType = Enum.RaycastFilterType.Blacklist

                local raycastResult = workspace:Raycast(rayOrigin, rayDirection * 10, raycastParams)
                if raycastResult and raycastResult.Instance == part2 then
                    table.insert(contactPoints, raycastResult.Position)
                    table.insert(contactNormals, raycastResult.Normal)
                end
            end
        end
    end

    return contactPoints, contactNormals
end

local function visualizeContactPointsAndNormals(contactPoints, contactNormals)
    for i, pos in pairs(contactPoints) do
     
        local contactPart = Instance.new("Part")
        contactPart.Size = Vector3.new(0.2, 0.2, 0.2)
        contactPart.Position = pos
        contactPart.Anchored = true
        contactPart.CanCollide = false
        contactPart.Color = Color3.new(1, 0, 0) -- Red color
        contactPart.Parent = workspace

        
        local normal = contactNormals[i]
        local arrow = Instance.new("Part")
        arrow.Size = Vector3.new(0.1, 0.1, 1) -- Length of the arrow
        arrow.Position = pos + normal * 0.5 -- Offset to center the arrow
        arrow.Anchored = true
        arrow.CanCollide = false
        arrow.Color = Color3.new(0, 0, 1) -- Blue color
        arrow.CFrame = CFrame.lookAt(arrow.Position, arrow.Position + normal)
        arrow.Parent = workspace
    end
end


local part1 = workspace.MeshPart1
local part2 = workspace.MeshPart2
local contactPoints, contactNormals = getContactPointsAndNormals(part1, part2)
visualizeContactPointsAndNormals(contactPoints, contactNormals)

for i, pos in pairs(contactPoints) do
    print("Contact point:", pos)
    print("Contact normal:", contactNormals[i])
end


heres what it looks like with 2 mesh parts.

i forgot to mention about the happy wheel physics system you wanted, as a matter of fact Creating a realistic limb damage system like in Happy Wheels is significantly more complex than calculating simple impact forces.

local function detachLimbIfStressed(limb, joint, stressThreshold)
	while true do
		task.wait(0.1)

		
		local parent = joint.Part0
		if not parent or not limb then
			break
		end

		
		local relativeVelocity = limb.Velocity - parent.Velocity
		local relativeAngularVelocity = limb.RotVelocity - parent.RotVelocity

		
		local stress = relativeVelocity.Magnitude + relativeAngularVelocity.Magnitude
		print("Relative Velocity Magnitude:", relativeVelocity.Magnitude)
		print("Relative Angular Velocity Magnitude:", relativeAngularVelocity.Magnitude)
		print("Total Stress:", stress)

		
		if stress > stressThreshold then
			
			joint.Enabled = false
			print("Limb detached due to stress!")
			break
		end
	end
end



-- Example usage
local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local limb = character:FindFirstChild("RightUpperArm")
local joint = limb and limb:FindFirstChild("RightShoulder")
local stressThreshold = 35

if not limb then
	warn("Limb not found!")
	return
end

if not joint then
	warn("Joint not found!")
	return
end

print("Limb and joint found!")

detachLimbIfStressed(limb, joint, stressThreshold)

this code applies for r15 but you can change it to r6 as based on the video you sent, the character is r6 so let me know if you need anything.

Looking back at my post, i realized i didn’t cover EVERYTHING about the physics rules, my bad

I have updated it, adding more documentations on it, can you please have another look at it

Not true, plenty of us play. Roblox is an escape and the kind that you need can be found at any college. They may not want to be paid in robux though, that might be a hang. Plenty of online help too via tutor websites, etc.

1 Like

I can’t exactly give you precise formulas or such, though I hope I could get you in the right direction.

The first case (smashable limbs) seems fairly doable, at least the explanation.

When the ball hits the torso, the direction that it bounces off in is under the same angle that it hit the surface of the torso with. And the amount of force put on the torso is, IIRC, the force of a perpendicular collision multiplied by the dot product of the inverse normal of the surface and the direction of the ball. So if the ball hits the surface perpendicularly, the dot product will be equal to 1, and as the angle decreases, so does the dot product. I hope that’s what you needed lol. If not, tell me and I’ll try to help further. Tho be warned, I’m not an expert in this field either.

I’m a little confused as to what the rest of the first segment is asking tho. Are you trying to find the collision point between the ball and the torso?
If so, I’d find the closest point on each face of the torso & ball and then choose the closest of those. There are likely plenty of sources in the internet with a simple google search to find out how to do that.

And, do take this with a grain of salt, based off of intuition, I don’t see why the distance from the center of mass would contribute to the impact force. Unless it’s like a leverage thing, where the force would start rotating the body more & more instead of directing all the force straight into the object the further the impact point is from the center of mass.

It’s not actually different at all though. In reality there is always a non-zero contact area when there is a collision, so it generalizes to the same math, at least at the macro level ( i.e. Newtonian physics, not quantum physics ).

Pardon my greediness, but I would prefer if you could write the details out in either a math formula or in lua code, because I’m likely to misinterpret things written in raw text

After a while of examining ideas from the replies, I became most interested in your formula for calculating joint stress

In the process I invented the most accurate way to achieve a joint effect that behaves just like the one in Happy Wheels, using 4 parts and 3 constraints

  • 1 BallSocketConstraint joins the pink and cyan blocks (hidden mechanism blocks)
  • 1 PrismaticConstraint joins the cyan block with the blue block
  • 1 PrismaticConstraint joins the pink block with the red block
    image

This will achieve a detachable pin joint effect (as in the parts are allowed to slide away from their pin point), that allows calculating the sudden change in velocity to determine the stress (best used when the ball socket constraint has Restitution set above 0)

However, your formula doesn’t take account of the mass of each part, and I wonder is it intentional. Because I can only assume that a heavier object pulling down a lighter object would result in a higher chance of snapping the joint, whereas its the opposite vice versa

1 Like

i see what you want here i included it to take account of the mass

local function detachLimbIfStressed(limb, joint, stressThreshold)
	while true do
		task.wait(0.1)

		
		local parent = joint.Part0
		if not parent or not limb then
			break
		end

		
		local relativeVelocity = limb.Velocity - parent.Velocity
		local relativeAngularVelocity = limb.RotVelocity - parent.RotVelocity

		
		local mass = limb:GetMass()  -- Get the mass of the limb
		local stress = (relativeVelocity.Magnitude + relativeAngularVelocity.Magnitude) * mass

		
		print("Relative Velocity Magnitude:", relativeVelocity.Magnitude)
		print("Relative Angular Velocity Magnitude:", relativeAngularVelocity.Magnitude)
		print("Limb Mass:", mass)
		print("Total Stress:", stress)

		
		if stress > stressThreshold then
			
			joint.Enabled = false
			print("Limb detached due to stress!")
			break
		end
	end
end


local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local limb = character:FindFirstChild("RightUpperArm")
local joint = limb and limb:FindFirstChild("RightShoulder")
local stressThreshold = 35

if not limb then
	warn("Limb not found!")
	return
end

if not joint then
	warn("Joint not found!")
	return
end

print("Limb and joint found!")

detachLimbIfStressed(limb, joint, stressThreshold)

the reason for not including it is i didnt think of taking account of mass, however this code only counts for the parts mass, if you connect to it like a heavy part, it wont affect its total mass, it only like effects its pull stress

1 Like

You seem to have only taken account of the mass of the limb / dangling object, but I wonder what about the mass of the torso? Since it’s about relativity

local function detachLimbIfStressed(limb, joint, stressThreshold)
    while true do
        task.wait(0.1)

        
        local parent = joint.Part0
        if not parent or not limb then
            break
        end

        
        local relativeVelocity = limb.Velocity - parent.Velocity
        local relativeAngularVelocity = limb.RotVelocity - parent.RotVelocity

      
        local limbMass = limb:GetMass()
        local torsoMass = parent:GetMass()

        
        local stress = (relativeVelocity.Magnitude + relativeAngularVelocity.Magnitude) * (limbMass + torsoMass)

       
        print("Relative Velocity Magnitude:", relativeVelocity.Magnitude)
        print("Relative Angular Velocity Magnitude:", relativeAngularVelocity.Magnitude)
        print("Limb Mass:", limbMass)
        print("Torso Mass:", torsoMass)
        print("Total Stress:", stress)

       
        if stress > stressThreshold then
          
            joint.Enabled = false
            print("Limb detached due to stress!")
            break
        end
    end
end


local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local limb = character:FindFirstChild("RightUpperArm")
local joint = limb and limb:FindFirstChild("RightShoulder")
local stressThreshold = 35


if not limb then
    warn("Limb not found!")
    return
end

if not joint then
    warn("Joint not found!")
    return
end

print("Limb and joint found!")


detachLimbIfStressed(limb, joint, stressThreshold)

here this code includes to take account of the torso mass, let me know if you need anything

So you just add the 2 mass in? That doesn’t feel mathematically right, i thought it should at least be like the average of the 2 mass, or the harmonic mean (m1*m2)/(m1+m2)

I need to make sure you actually know the physics behind this field to provide me the correct formula, since that’s what I wanted to look for in the first place

oh ok, here i made it use harmonic mean like what you said

local function detachLimbIfStressed(limb, joint, stressThreshold)
	while true do
		task.wait(0.1)

		
		local parent = joint.Part0
		if not parent or not limb then
			break
		end

		
		local relativeVelocity = limb.Velocity - parent.Velocity
		local relativeAngularVelocity = limb.RotVelocity - parent.RotVelocity

		
		local massLimb = limb:GetMass()
		local massParent = parent:GetMass()

		
		local harmonicMeanMass = (2 * massLimb * massParent) / (massLimb + massParent)

		
		local stress = (relativeVelocity.Magnitude + relativeAngularVelocity.Magnitude) * harmonicMeanMass

		
		print("Relative Velocity Magnitude:", relativeVelocity.Magnitude)
		print("Relative Angular Velocity Magnitude:", relativeAngularVelocity.Magnitude)
		print("Limb Mass:", massLimb)
		print("Parent Mass:", massParent)
		print("Harmonic Mean Mass:", harmonicMeanMass)
		print("Total Stress:", stress)

		
		if stress > stressThreshold then
			
			joint.Enabled = false
			print("Limb detached due to stress!")
			break
		end
	end
end


local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local limb = character:FindFirstChild("RightUpperArm")
local joint = limb and limb:FindFirstChild("RightShoulder")
local stressThreshold = 35

if not limb then
	warn("Limb not found!")
	return
end

if not joint then
	warn("Joint not found!")
	return
end

print("Limb and joint found!")

detachLimbIfStressed(limb, joint, stressThreshold)

the formula i used is: (2 * m1 * m2)/(m1 + m2)

and the stress is calculated like this:
Stress = (Relative Velocity Magnitude + Relative Angular Velocity Magnitude ) × Harmonic Mean Mass

1 Like