Rotating part based on Mouse Position?

I am designing a UI for a game, and am using a SurfaceGUI so that I can get the desired 3D effect that I am trying to create. I have run into a problem though since I am not too experienced with CFrame math.

What the current UI looks like:

What I have currently tried and ended up with:

What I would like is to have the menu SurfaceGUI rotate slightly based on the player’s mouse position, I have tried doing this somewhat, but the part just keeps spinning forever in my current script, if anyone has any pointers on how this could be done please let me know!

6 Likes

It seems like you’re rotating the Adornee of the SurfaceGui by a small amount based on the coordinates of the mouse cursor on the screen. This causes it to keep rotating even when the mouse isn’t moving. Instead, try rotating it a small amount every time the mouse moves, based on how much it moved. You can find this out with the InputObject.Delta property of InputObjects that are passed to the UserInputService.InputChanged event. As an alternative, you could store the original CFrame of the adornee and every frame rotate it away from that original CFrame by some angle determined by the position of the mouse, rather than the change in position.

5 Likes

I will try this when I get home, I hope you don’t mind me asking, but could you provide an example of your second method so that I can have something to start with?

Sure! Here you go:
local RunService = game:GetService("RunService")
local InputService = game:GetService("UserInputService")

--Should probably be a child of StarterGui/PlayerGui
local surfaceGui = script.Parent 

--Assuming it's been set manually through the Properties menu
local adornee = surfaceGui.Adornee

--In degrees of rotation when mouse is at screen edges.
--	Flipped sign means inverted controls
local sensitivity = Vector2.new(-10, -10) 

function initialize( )
	initialCFrame = adornee.CFrame
	
	--Necessary to find screen size
	camera = game.Workspace.CurrentCamera

	RunService.RenderStepped:Connect(update)
end

function update()
	--Must be updated every frame, because the user might resize window
	local screenSize = camera.ViewportSize
	
	--Offset by half the screen, so that (0, 0) is at center of screen. 
	--Scale by 2/screenSize, so coordinates are from (-1, -1) to (1, 1)
	--	because using absolute/pixel coordinates would yield different results on 
	--	screens of different resolution
	local mousePos = (InputService:GetMouseLocation() - screenSize / 2) * (2/screenSize)
	
	--Rotate pitch first, then yaw
	local rotation = CFrame.Angles(
		math.rad(mousePos.Y * sensitivity.Y), 
		math.rad(mousePos.X * sensitivity.X), 
		0
	)
	
	--Rotate yaw first, then pitch (I think this is a bit more natural- feeling)
--	local rotation = CFrame.Angles(
--		0,
--		math.rad(mousePos.X * sensitivity.X), 
--		0
--	) * CFrame.Angles(
--		math.rad(mousePos.Y * sensitivity.Y), 
--		0, 0
--	)
	
	adornee.CFrame = initialCFrame * rotation
end

initialize()
5 Likes