How to make arrow follow cursor but with a fixed position (and also rotates)


i want to make it so when the cursor, for example, is nearest to the the inventory icon, the arrow’s position change to beside the icon and its rotation change to -180

how can i do this? any help will be greatly appreciated

thanks in advance

1 Like

could you show the frame containing the icons in explorer?

You would need to calculate the position of the mouse and the position of the ImageLabels and get the distance between them and change the Cursor rotation and position depending on the results.

1 Like

I’d say the easiest way to go about this without doing large amounts of math is to make a spinner out of a frame, set the anchorpoint to (0.5, 0.5), position it in the middle of your wheel, and put an imagelabel containing the arrow itself on the tip, and from there you rotate it around as you wish.

Setup and code
image

local ButtonWheel = script.Parent.Parent.ButtonWheel
local tweenService = game:GetService("TweenService")
local tweenInfo = TweenInfo.new(0.25, Enum.EasingStyle.Sine)
local spinner = script.Parent
local lastbutton = "Left" -- Default posiiton

ButtonWheel.Left.MouseEnter:Connect(function()
	tweenService:Create(spinner, tweenInfo, {Rotation = 0}):Play()
	lastbutton = "Left"
end)

ButtonWheel.Right.MouseEnter:Connect(function()
	tweenService:Create(spinner, tweenInfo, {Rotation = 180}):Play()
	if lastbutton == "Down" then
		spinner.Rotation = 270
	end
	lastbutton = "Right"
end)

ButtonWheel.Up.MouseEnter:Connect(function()
	tweenService:Create(spinner, tweenInfo, {Rotation = 90}):Play()
	lastbutton = "Up"
end)

ButtonWheel.Down.MouseEnter:Connect(function()
	tweenService:Create(spinner, tweenInfo, {Rotation = -90}):Play()
	if lastbutton == "Right" then
		spinner.Rotation = -180
	end
	lastbutton = "Down"
end)

Edit 2: Realized it was just tweenService, fixed the code the wheel spinning works perfectly now.

1 Like

FIrst I would like to say that @doggyshot123 has already provided an effective and simple solution that involves little math. If you don’t like math, I do not blame you for using such an elegantly simple solution.

For those wondering though, the mathy way is possible to do, and I thought it would be a little fun to try this out. Beware, it is a long read, but this problem really involves a lot of vector trigonometry that can be applied to many places in game development.

First, you would need to convert the mouse position to an angle relative to the middle of the circle. For this, I will use the angle between two vectors formula (where u and v are two vectors and the dot in the numerator is the dot product):
image
Because we are going to use unit vectors (vectors with a length of 1), the bottom part of that equation cancels out (since those vertical bars represents length so it is a division by 1), and to find the angle theta, you just arccos the dot product of the two vectors.

This can be done by getting the mouse position via UserInputService and then finding the dot product between a vector pointing straight up (Vector2.new(0, 1)) and the unit vector of the difference between the mouse position and the middle of the circle (local differenceVector = (mousePosition - middlePosition).Unit). In total, this would look like this:

local middlePosition = [[INSERT MIDDLE POSITION OF THE SELECTOR HERE]]
local mousePosition = game:GetService("UserInputService"):GetMouseLocation() -- Add Vector2.new(0, 36) if your GUI does not account for GUI inset
local differenceVector = (mousePosition - middlePosition).Unit
local angle = math.acos(differenceVector:Dot(Vector2.new(0, 1))) * math.sign(differenceVector.X)

You may be wondering, where did the math.sign part come from? It is used because, for example, an angle of 45 degrees to the left of the vector pointing up will have the same value as an angle to the right 45 degrees (the dot product formula does not know the difference). So to figure out if the angle is left or right of the up vector the sign of the x coordinate of the mouse direction can be used where negative sign mean left and positive sign means right.
image
Now with the angle figured out, you can now find which angle is closest to the angle where the buttons are. The bag button, for example, would have a value of -90 degrees. Also, the angle returned with math.acos is in radians so convert this to degrees using math.deg(angle) to compare your angles to your buttons on the selector. Also note that the gear button is at the bottom of the screen so it would have both -180 and 180 degrees as its angle.

Okay, so that was to figure out which angle is closest to select the button, but what about having the arrow follow your cursor?

This is why that angle variable is so valuable. Using the radius of the circle and more trigonometry we can figure out where the arrow should be.

local circleRadius = [[INSERT CIRCLE RADIUS HERE]]
local unitX = math.sin(angle) 
local unitY = math.cos(angle) 
local arrowPositionUnitVector = Vector2.new(unitX, unitY)
local arrowPosition = middlePosition + arrowPositionUnitVector * circleRadius

Given an angle, cosine and sine can be used to convert it to coordinates on a unit circle (a circle with a radius of 1). Multiplying this vector by the circle radius will give the position of where the arrow needs to be.

Side note for those who are familiar with traditional trigonometry.

Usually x is the cosine of the angle and y is the sine of an angle; I have flipped it. This is because the vector I am comparing to is upwards and not in the +x direction like the traditional coordinate system.

So with the arrowPosition property, you can now set the position of the arrow to the correct place around the circle. If you want the arrow to snap to a button, you can lerp the angle instead, but the formula above still holds.

One final thing: the arrow needs to be rotated, and to do this, I would convert the angle to degrees and apply the normal rotation property for frames.

Overall, this was a bit of a tangent (no pun intended), but I hope it gives some insight of how to tackle such problems with math. Since I spent so much time writing this reply, I also would not combining the fragmented code I wrote into one functional script, so if anyone would like that as a resource, I would not mind doing it. I probably would need the guis as a reference. Of course, since the code is so fragmented, I may need to correct some things so a functional script may also squeeze some of those errors out. I hope this read was somewhat educational!

2 Likes

CnP_10092024_19.26.40

i honestly really appreciate how much work you put into this (this is the longest reply i ever got)

i don’t really understand much of it since i havent really learnt trigonometry all that much and tbh since english is not my first language its really confusing to read but i’ll try to follow your instructions

i’ll reply once i understand it (sooner or later)

tysm i appreciate the reply, i will try this if i dont understand @LE4FBUG 's script or it doesn’t work

otherwise, i still really appreciate for the reply!

im back… but i still don’t understand it very well
i want to ask a few questions about the first and second script,
first off im a bit confused on how to get the exact value of middle position,
and second, would the circle radius be vector2 too or an integer

other than that, i’ll try again… but if i don’t understand i’ll use the other guy’s solution

The middle position is basically just the position of the center of the frame. I think you can use absolute size and position to find it pretty easily.

local middlePosition = circleFrame.AbsolutePosition + circleFrame.AbsoluteSize / 2

The circle radius is just one singular number. It basically would be the pixel offset the arrow would be from the middlePosition when the arrow moves around the circle. Setting it to half the x or z absolute size might be good enough or you might want to add a little more for space and looks. Honestly though, it does not matter right now as it is easy to change later, so choose any number that would fit within the screen.

last question, for the arrowPosition would i have to convert it to udim2 first? (sorry this is prob a dumb question)

Its not a dumb question, and I did not make that obvious in my explanation, but yes, you would convert it to UDim2 using offset. I also suggest setting the AnchorPoint to Vector2.new(0.5, 0.5) to center the arrow on that position.

alright, thank you very much, i will probably try make this into a working script (if i cant i’ll probably just use the simpler script) but i appreciate your reply very much this has been very educational for me

as i said before, i thank you very much!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.