How should I go about calculating this?

I want to find an equation that I can use that makes it so that a parts CFrame always on top of a part, although the issue Is I don’t know how to do this, and the equations I’ve been trying to use would not help.

image

3 Likes

The center of a part is half its Y size away from the top surface and the bottom surface if the bottom and top surfaces are the surfaces that are perpendicular to part.CFrame.UpVector.

partThatShouldBeOnTop.CFrame = partThatShouldBeUnder.CFrame * CFrame.new(0, partThatShouldBeUnder.Size.Y / 2 +  partThatShouldBeOnTop.Size.Y / 2, 0).

If the top surface of the part that should be under the part being positioned isn’t the surface perpendicular to the UpVector of the CFrame of the part and in the direction of the upvector from the center, the code above won’t work. Here’s an alternative.

local function getNormalVectorAndNormalAxisNameOfMostUpFacingSurface(part)
	local rotationCf = part.CFrame - part.Position
	local highestY = 0
	local normalVectorWithHighestY
	local axisNameOfNormalVectorWithHighestY
	for _, axis in Enum.Axis:GetEnumItems() do
		local positiveAxisVectorInObjectSpace = Vector3.fromAxis(axis)
		local positiveAxisVectorInWorldSpace = rotationCf * positiveAxisVectorInObjectSpace
		local positiveAxisYInWorldSpace = positiveAxisVectorInWorldSpace.Y
		if positiveAxisYInWorldSpace > highestY then
			highestY = positiveAxisYInWorldSpace
			normalVectorWithHighestY = positiveAxisVectorInWorldSpace
			axisNameOfNormalVectorWithHighestY = axis.Name
		elseif -positiveAxisYInWorldSpace > highestY then
			-- elseif because both conditions can't be true because either both Ys are 0	or one is negative and one is positive
			-- and the initial highest y is 0 so 0 or a negative value can never be higher than highestY.
			highestY = -positiveAxisYInWorldSpace
			normalVectorWithHighestY = -positiveAxisVectorInWorldSpace
			axisNameOfNormalVectorWithHighestY = axis.Name
		end
	end
	return normalVectorWithHighestY, axisNameOfNormalVectorWithHighestY
end

local function positionPartOnMostUpFacingSurfaceOfAnotherPart(partThatShouldBeUnder, partThatShouldBeOnTop)
	local topNormal, topAxisName = getNormalVectorAndNormalAxisNameOfMostUpFacingSurface(partThatShouldBeUnder)
	local dotProduct = Vector3.yAxis:Dot(topNormal)
	local rotationCf = dotProduct < 1 - 1e-4 and CFrame.fromAxisAngle(Vector3.yAxis:Cross(topNormal).Unit, math.acos(dotProduct)) or CFrame.new()
	local position = partThatShouldBeUnder.Position + topNormal * (partThatShouldBeUnder.Size[topAxisName] + partThatShouldBeOnTop.Size.Y) / 2
	partThatShouldBeOnTop.CFrame = rotationCf + position
end
2 Likes

For rectangles the y equation to do that is the following:

local y = bottomPart.Position.Y+(bottomPart.Size.Y+smallPart.Size.Y)/2
print(y)

if it doesn’t work change the axis of the size params until it does(for example if a part is rotated you may want to use z instead of y etc)

basically, the main idea behind this equation is that you want to move the part half the size of the big part above the big part and offset it half the size of the small part so they align perfectly.

1 Like
local part = -- path to the model or part you want the second part to be on
local secondpart = -- path to the part you want to be on top of the first part

if part:IsA("Model") then
secondpart.CFrame = part.WorldPivot*CFrame.new(0,part:GetExtentsSize().Y/2,0)
else
secondpart.CFrame = part.CFrame*CFrame.new(0,part.Size.Y/2,0)
end

If you want a part to be on TOP of the model no matter its orientation then you can use is raycasting. cast a ray from a rough top position and then just set the secondparts position to the position that the raycast gave us. or use RoBoPoJu’s (goofy name ngl) solution

1 Like