How to make a dragger with ConeHandleAdornment

Well, the title explains all that I’m wondering, I have this script which appearently doesn’t work(I don’t know why)

            local NewHeight = self.Object.Height

            if self.Object.DraggingHeightHandle then
                NewHeight = math.clamp(
                    self.Object.Height - (self.MouseDelta.Y / 10),
                    1,
                    8
                )
            end

            self.Object.Height = math.round(NewHeight / 1) * 1

This is an old topic but I was wondering this as well so I decided to give it a shot in case anyone in the future was wondering how to go about this.


A couple functions we will need, both of which we can omit the depth check:

Ray-plane intersection (obtained from c++ - How to calculate a ray plane intersection - Stack Overflow):

local function rayPlaneIntersection(planePoint, planeNormal, origin, direction)
	return -((origin - planePoint):Dot(planeNormal)) / (planeNormal:Dot(direction))
end

Closest point on line (obtained from Calculating closest point on a line? - #9 by Jaycbee05):

local function closestPointOnLine(v, a, b)
	local ab = b - a
	local abx = ab.X
	local aby = ab.Y

	local av = v - a
	local i = av:Dot(ab) / (abx * abx + aby * aby)
	return a + i * ab
end

My first iteration simply created a supposed plane where the dragger’s normal is and did a plane line intersection where the mouse was, then constrained it such that the only movement was the movement along the dragger’s normal.

What I was using to move the part only along the LookVector:

game:GetService('RunService').RenderStepped:Connect(function()
	if not isDragging then
		return
	end

	local camera = workspace.CurrentCamera

	local coneCFrame = adornee.CFrame * CFrame.new(coneHandleAdornment.SizeRelativeOffset * adornee.Size) * coneHandleAdornment.CFrame
	local axis = initialCFrame.LookVector
    
	local mouseLocation = userInputService:GetMouseLocation()

	local pointWorldSpace = camera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)

    -- find where the 
	local planeIntersectionZ = rayPlaneIntersection(initialCFrame.Position, initialCFrame.UpVector, pointWorldSpace.Origin, pointWorldSpace.Direction)

    -- where the ray intersects the ray in 3d
	local planeIntersectWorldSpace = pointWorldSpace.Origin + planeIntersectionZ * pointWorldSpace.Direction
	
    -- constrain it to the axis
	local objectSpace = initialCFrame:PointToObjectSpace(planeIntersectWorldSpace) * axis
	local worldSpace = initialCFrame:PointToWorldSpace(objectSpace)

	adornee.CFrame = CFrame.new(worldSpace - (CFrame.new(coneHandleAdornment.SizeRelativeOffset * adornee.Size) * coneHandleAdornment.CFrame).Position) * initialCFrame.Rotation
end)

While this was somewhat functional, I noticed a few issues. Namely that the part goes far away when you put your cursor far away in the supposed plane

Medal_Qu8zANJkm5

What I had to figure out now was what the draggers were doing to negate this issue.

I noticed from the new dragger beta that there is a 2d line that is drawn across the screen that goes along the normal of the dragger. It looks like the mouse’s position is projected along this line, and that projected point is where the new position relative to the dragger ends up. This is why the part doesn’t fly off when you put your mouse far along the supposed plane in the background.

A couple of things we have to do to mimic this:
1- Convert the dragger’s normal to screen space:

local coneCFrameScreen = camera:WorldToViewportPoint(coneCFrame.Position)
local coneDirScreen = camera:WorldToViewportPoint((coneCFrame + axis).Position)
local dirScreenSpace = (coneCFrameScreen - coneDirScreen).Unit

2- Find the closest point on that line to the mouse position:

local closestPointToMouse = closestPointOnLine(mouseLocation, coneCFrameScreen, coneCFrameScreen + dirScreenSpace)

3- Use this point instead of the mouse position for the ray-plane intersection:

local pointWorldSpace = camera:ViewportPointToRay(closestPointToMouse.X, closestPointToMouse.Y)

This leaves me with something functionally pretty close to the original draggers:
Medal_xzrJRl2NJB

Here’s a placefile with a full implementation, including making everything relative to the adornee (scripts in StarterGui):
coneDraggers.rbxl (55.6 KB)

1 Like