Need help with Emote System

I need a little help with an emote system; mainly the emote selection.
Like I need to get the degrees of the cursor relative from the center point;

I’m not good at math… so any help would be appreciated.

1 Like

atan2. You basically just tell it how horizontally and vertically far the mouse is from the center, and it spits out the angle

The only catch is that it gives you the answer in a measurement called radians, so you have to do a quick conversion to get the degrees

So what’s happening here is pretty simple. You first establish the center of your emote wheel, which is usually the middle of the screen. Then you find the distance from that center point to your mouse’s current x and y position

The math.atan2(y, x) takes those distances and calculates the angle. Most atan2 functions are a little weird and want the y value first

1 Like

Thanks. I’ll try that out and let you know if it works.


It works… but it seems to have a noticeable offset or… imprecision. If the mouse is on the leftmost or rightmost side of the screen the rotation is actually not presicely 0 degrees or -0 degrees; and has a slight offset or imprecision.

Ah, that small offset

The problem is that for your angle to be a perfect 0 or 180 degrees, the vertical distance from your mouse to the center has to be exactly zero. That tiny offset you’re seeing happens because it’s not. I’d say, probably because of the UI top bar - your script is probably calculating the center of the entire window, but the mouse reports its position starting from below that top bar, so the two are never perfectly aligned.
The fix is pretty simple: you need to tell your script to get the center of the actual game viewport, not the whole window.

Grab the camera and use its ViewportSize.

I tried that; but it seems to still have that weird offset.

local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local Camera = workspace.CurrentCamera

local UI = script.Parent
local Wheel = UI:WaitForChild("Wheel")

local Rotations = {
	[-120] = Wheel.Emote1,
	[-60] = Wheel.Emote2,
	[0] = Wheel.Emote3,
	[60] = Wheel.Emote4,
	[120] = Wheel.Emote5,
	[180] = Wheel.Emote6,
}

local function getAngle()
	local center = Camera.ViewportSize / 2
	local cursor = UserInputService:GetMouseLocation()

	local dx = cursor.X - center.X
	local dy = cursor.Y - center.Y
	return math.deg(math.atan2(dy, dx))
end

local function getClosestEmote(angle)
	local closestKey = nil
	local smallestDiff = math.huge
	for key, emote in pairs(Rotations) do
		local diff = math.abs(angle - key)
		if diff < smallestDiff then
			smallestDiff = diff
			closestKey = key
		end
	end
	return Rotations[closestKey]
end

RunService.RenderStepped:Connect(function()
	local degrees = getAngle()
	local selectedEmote = getClosestEmote(degrees)

	for _, emote in pairs(Rotations) do
		emote.ImageTransparency = (emote == selectedEmote) and 0.4 or 0.7
	end
end)

I also tried enabling the IgnoreGuiInset property… but that also doesn’t seem to quite work.

The offset is happening because Camera.ViewportSize and UserInputService:GetMouseLocation() use two different coordinate systems.

So, your center.Y is correct for the viewport, but your cursor.Y is offset by 36 pixels. That’s where the imprecision comes from. The IgnoreGuiInset property only affects how the GUI itself is positioned, it doesn’t change the coordinates returned by this function.

local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local mouse = player:GetMouse()
local Camera = workspace.CurrentCamera
local UI = script.Parent
local Wheel = UI:WaitForChild("Wheel")
local Rotations = {
	[-120] = Wheel.Emote1,
	[-60] = Wheel.Emote2,
	[0] = Wheel.Emote3,
	[60] = Wheel.Emote4,
	[120] = Wheel.Emote5,
	[180] = Wheel.Emote6,
}

local function getAngle()
	local center = Camera.ViewportSize / 2
	local cursor = Vector2.new(mouse.X, mouse.Y)

	local dx = cursor.X - center.X
	local dy = cursor.Y - center.Y
	return math.deg(math.atan2(dy, dx))
end

local function getClosestEmote(angle)
	local closestKey = nil
	local smallestDiff = math.huge

	for key, emote in pairs(Rotations) do
		local diff = math.abs(angle - key)
		if diff > 180 then
			diff = 360 - diff
		end

		if diff < smallestDiff then
			smallestDiff = diff
			closestKey = key
		end
	end
	return Rotations[closestKey]
end

RunService.RenderStepped:Connect(function()
	local degrees = getAngle()
	local selectedEmote = getClosestEmote(degrees)

	for _, emote in pairs(Rotations) do
		emote.ImageTransparency = (emote == selectedEmote) and 0.4 or 0.7
	end
end)

Apparently my emote wheel selection logic was bugged; which is why it looked as if the rotation was being returned wrong. Thanks for the solution!

1 Like

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