How do I make a Dragger?

YESS okay I found a good solution

I went and revisited this, and realised i can just use the other part as the ‘canvas’

Heres the completed code if anyone cares:

local Dragger = {ClassName = "Dragger"}
Dragger.__index = Dragger

function Dragger.new()
	local self = {}
	self.Target = nil

	-- to be set manually
	self.Increment = 0
	self.RaycastRange = 100

	setmetatable(self, Dragger)
	return self
end

function Dragger:MouseDown(target: "Part/Model to move")
	self.Target = target
end

function Dragger:CalcCanvas(canvas)
	local canvasSize = canvas.Size

	local up = Vector3.new(0, 1, 0)
	local back = -self.SurfaceVector

	local dot = back:Dot(Vector3.new(0, 1, 0))
	local axis = (math.abs(dot) == 1) and Vector3.new(-dot, 0, 0) or up

	local right = CFrame.fromAxisAngle(axis, math.pi/2) * back
	local top = back:Cross(right).unit

	local cf = canvas.CFrame * CFrame.fromMatrix(-back*canvasSize/2, right, top, back)
	local size = Vector2.new((canvasSize * right).magnitude, (canvasSize * top).magnitude)

	return cf, size
end
function Dragger:CalcPlacementCFrame(position, rotation, surfaceVector, canvas)
	self.SurfaceVector = surfaceVector
	local cf, size = self:CalcCanvas(canvas)

	local tSize = self.Target.Size
	if self.Target.ClassName == "Model" then tSize = self.Target.PrimaryPart.Size end

	local modelSize = CFrame.fromEulerAnglesYXZ(0, rotation, 0) * tSize
	modelSize = Vector3.new(math.abs(modelSize.x), math.abs(modelSize.y), math.abs(modelSize.z))

	local lpos = cf:pointToObjectSpace(position);
	local size2 = (size - Vector2.new(modelSize.x, modelSize.z))/2

	if size2.x < 0 or size2.y < 0 then return end

	local x = math.clamp(lpos.x, -size2.x, size2.x);
	local y = math.clamp(lpos.y, -size2.y, size2.y);

	local g = self.Increment
	if (g > 0) then
		x = math.sign(x)*((math.abs(x) - math.abs(x) % g) + (size2.x % g))
		y = math.sign(y)*((math.abs(y) - math.abs(y) % g) + (size2.y % g))
	end

	return cf * CFrame.new(x, y, -modelSize.y/2) * CFrame.Angles(-math.pi/2, rotation, 0)
end

function Dragger:MouseMove(unitRay: Ray, params: RaycastParams)

	local rayStart = unitRay.Origin
	local rayDirection = unitRay.Direction

	if self.Target then
		if not params then
			params = RaycastParams.new()
			params.FilterType = Enum.RaycastFilterType.Exclude
			params.IgnoreWater = true
			params.FilterDescendantsInstances = {self.Target}
		end

		local hit = game.Workspace:Raycast(rayStart, rayDirection * self.RaycastRange, params)
		if hit and hit.Instance and hit.Instance ~= game.Workspace.Terrain then
			local pos = hit.Position

			local surface
			local dotY = hit.Normal:Dot(Vector3.new(0,1,0))
			local dotX = hit.Normal:Dot(Vector3.new(1,0,0))
			local dotZ = hit.Normal:Dot(Vector3.new(0,0,1))
			if (math.abs(dotY) >= 0.9) then
				if (dotY < 0) then
					surface = Enum.NormalId.Bottom
				else
					surface = Enum.NormalId.Top
				end
			elseif (math.abs(dotX) >= 0.9) then
				if (dotX < 0) then
					surface = Enum.NormalId.Left
				else
					surface = Enum.NormalId.Right
				end
			else
				if (dotZ < 0) then
					surface = Enum.NormalId.Front
				else
					surface = Enum.NormalId.Back
				end
			end

			local c = self:CalcPlacementCFrame(pos, 0, Vector3.FromNormalId(surface), hit.Instance)


			if not c then return end

			if self.Target.ClassName == "Model" then
				self.Target:PivotTo(c)
			else
				self.Target.CFrame = c
			end
		end
	else
		warn("No target was found.")
	end
end

function Dragger:MouseUp()
	self.Target = nil
end

return Dragger

2 Likes