Making a Valve/Steering Wheel that can be dragged with a mouse

My actual goal here is to make a manual stirring process. I’ve spent like 5 hours trying to find a helpful post in this forum and eventually ended up giving the built in AI a try with this one (It was painful) and this was the best result I could get:

  • Having the cursor too close to the primary part ruins the immersion
  • The primary part locks onto the cursor whenever it is pressed, I want it to spin along with it rather than snapping onto it

Fixing the jumpy rotation error might be simple but I’m really struggling with getting this work without the model snapping onto my cursor.

The Function Im currently stuck with
function UpdateModel()
	if rotatable then
		local currentMousePosition = UserInputService:GetMouseLocation()
		local ray = workspace.CurrentCamera:ScreenPointToRay(currentMousePosition.X, currentMousePosition.Y)
		local planePosition = model.PrimaryPart.Position
		local planeNormal = Vector3.new(0, 1, 0) -- Assuming the part is on a horizontal plane

		local denominator = ray.Direction:Dot(planeNormal)
		if math.abs(denominator) > 1e-6 then -- Check if the ray is not parallel to the plane
			local t = (planePosition - ray.Origin):Dot(planeNormal) / denominator
			local intersectionPoint = ray.Origin + t * ray.Direction

			local direction = (intersectionPoint - planePosition).Unit
			if direction.Magnitude > 0 then
				local targetCFrame = CFrame.lookAt(planePosition, planePosition + direction)
				model:SetPrimaryPartCFrame(targetCFrame)
			end
		end
	else
		return
	end
end
1 Like

The easiest strategy for these kinds of things is to just use a drag detector. In your case, I might set its drag style to rotate axis and measure drag frame to determine when the player has stirred enough. To get the spoon to stay at the edge of the pot, I’d offset the origin of the spoon’s model so that rotating the model’s pivot ends up at the center while the spoon itself appears to be at the edge of the pot.

Thanks for pointing that out! I didnt know such thing was there

I’ll leave the code here if anyone wishes to use it as well:

Code
local dragDetector = workspace:WaitForChild("spin").wheel.DragDetector
local previousRotationX = nil
local Ongoing = false
local halfwaydone = false
local loopamount = 0
local requiredloops = 3

dragDetector.DragStart:Connect(function()
	Ongoing = true
end)

dragDetector.DragEnd:Connect(function()
	Ongoing = false
	halfwaydone = false
	--loopamount = 0  Doesn't make sense but It's an option
	previousRotationX = nil
end)

dragDetector.DragContinue:Connect(function(dragFrame)
	local rotationX, _, _ = dragDetector.DragFrame:ToEulerAnglesXYZ()
	local currentRotationX = math.deg(rotationX)
	
	if previousRotationX then
		-- print(math.floor(currentRotationX))
		if (math.floor(currentRotationX) >= -180 and math.floor(currentRotationX) <= -170) or (math.floor(currentRotationX) <= 180 and math.floor(currentRotationX) >= 170) and Ongoing then
			--[[ 
			the reason I didn't do == 180's is because the degrees are so janky it can skip the exact number sometimes.
			After this point, the current rotation will be reversed to minus and will drastically go down to zero
			
			! You should also keep in mind the player might spin the other way around from here !
			]]--
			halfwaydone = true
		end
		if (math.floor(currentRotationX) >= -10 and math.floor(currentRotationX) <= -0) or (math.floor(currentRotationX) <= 10 and math.floor(currentRotationX) >= 0) and Ongoing then
			if halfwaydone == true then
				halfwaydone = false
				loopamount = loopamount + 1
				warn("loop amount: "..loopamount)
				if loopamount >= requiredloops then
					warn("Complete")
					dragDetector.Enabled = false
					dragDetector.Parent["victory.wav"]:Play()
				end
			end
		end
	else
		previousRotationX = currentRotationX
	end
end)