Gamepad Virtual Cursor for UI is Live! [Opt-in Only]

Just put in StarterGui “VirtualCursorMode” to “Enabled”. And then you connect a controller to your device to test it in studio.

1 Like

CoreGui.RobloxGui.Modules.VirtualCursor.VirtualCursorMain:99: attempt to index nil with ‘GetPropertyChangedSignal’

Seems to not work

Line erroring:

GamepadService:GetPropertyChangedSignal(“GamepadCursorEnabled”):Connect(function()

Entire code:

--[[
	// FileName: GamepadVirtualCursor.lua
	// Written by: dangel (Garnold)
	// Description: Implementation of the Virtual Cursor selection mode for gamepads
]]

local VirtualCursorFolder = script.Parent

local GuiService = game:GetService("GuiService")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local GamepadService = game:GetService("GamepadService")

-- Submodules
local input = require(VirtualCursorFolder:WaitForChild("Input"))
local interface = require(VirtualCursorFolder:WaitForChild("Interface"))
local onRenderStep = require(VirtualCursorFolder:WaitForChild("OnRenderStep"))

-- There should only be one instance of virtual cursor.
-- This will allow it to stay a class, while also still being able to rely on guiservice enabling/disabling
local VirtualCursorSingleton = nil
local lastInputTypeChangedEventConnection = nil
local menuOpenedEventConnection = nil

local bindToRenderStepName = "VirtualCursorStepped"

local function enableVirtualCursor(position)
	if not VirtualCursorSingleton then return end
	position = position or VirtualCursorSingleton.CursorPosition

	VirtualCursorSingleton.Enabled = true
	VirtualCursorSingleton.CursorPosition = position

	interface:EnableUI(position)
	input:EnableInput()

	RunService:BindToRenderStep(bindToRenderStepName, Enum.RenderPriority.Input.Value + 1, VirtualCursorSingleton.OnRenderStep)

	lastInputTypeChangedEventConnection = UserInputService.LastInputTypeChanged:Connect(function(inputType)
		if inputType ~= Enum.UserInputType.Gamepad1 then
			GamepadService.GamepadCursorEnabled = false
		end
	end)
	menuOpenedEventConnection = GuiService.MenuOpened:Connect(function()
		GamepadService.GamepadCursorEnabled = false
	end)
end

local function disableVirtualCursor()
	-- disconnect events
	lastInputTypeChangedEventConnection:Disconnect()
	lastInputTypeChangedEventConnection = nil

	menuOpenedEventConnection:Disconnect()
	menuOpenedEventConnection = nil
	VirtualCursorSingleton.PreviouslySelectedObject = nil

	interface:DisableUI()
	input:DisableInput()

	RunService:UnbindFromRenderStep(bindToRenderStepName)

	if VirtualCursorSingleton.SelectedObject then
		GuiService.SelectedObject = nil -- if we are in hybrid, rely on the engine to handle this
		GuiService.SelectedCoreObject = nil
		VirtualCursorSingleton.SelectedObject = nil
	end

	VirtualCursorSingleton.Enabled = false
end

-- define class
local VirtualCursor = {}
VirtualCursor.__index = VirtualCursor

function VirtualCursor:GetEnabled()
	return self.Enabled
end

function VirtualCursor.new()
	local self = {
		Enabled = false,
		CursorPosition = Vector2.new(),
		SelectedObject = nil,
		PreviouslySelectedObject = nil,
	}

	self.OnRenderStep = function(delta)
		onRenderStep(self, delta)
	end

	setmetatable(self, VirtualCursor)

	VirtualCursorSingleton = self

	return self
end

GamepadService:GetPropertyChangedSignal("GamepadCursorEnabled"):Connect(function()
	local state = GamepadService.GamepadCursorEnabled
	if VirtualCursorSingleton then
		if state then
			local position = UserInputService:GetMouseLocation()
			enableVirtualCursor(position)
		else
			disableVirtualCursor()
		end
	end
end)

return VirtualCursor.new()

1 Like

Holy crap this is amazing. I always felt that on console it was hard to use the UI’s and sometimes it would but out making you need to rejoin. I am definitely gonna be using this on a new project I am making rn

Cant seem to use this on xbox yet?

No, for now the virtual cursor is only opt in. You can enable it for any Roblox experience though by setting VirtualCursorMode under the StarterGui to Enabled.

Hey! We’ve received a lot of feedback around customization, how it looks, the size etc. It’s definitely something we’ve talked about internally but for now we’re focused on making sure the virtual cursor works well in any game and solving issues related to that. Once virtual cursor is in a more stable state, we’ll revisit the idea of customization. Thank you for the feedback !

Hey! Thank you for your concern. We know that making big updates like this are challenging. We have no plans currently to change default to enabled. And if that time does happen we’ll make sure there’s plenty of time and notices before it changes. We’ve also left a way for developers to use their own custom UI logic and virtual cursor won’t interfere with that. If you have specific issues with virtual cursor, please reach out! I’m happy to help!

P.s. Roblox Uno is quite enjoyable :slight_smile:

3 Likes

Hey! Do you have a specific use case for why something would break if Virtual Cursor gets turned off? Exposing new APIs, especially if they’re temporary, can lead to some challenges later on. If you have specific questions about virtual cursor interactions, feel free to message me directly and I can help you

Hey! Do you have an example of a mouse event that doesn’t work with virtual cursor? The end goal is that the virtual cursor will behave the same as a mouse. If there are things that don’t work please let me know and I can look into them.

Scrolling frames support is something many people have been asking for and we’re currently working though some solutions and we’ll be sure to post updates as things develop.

1 Like

For now no, they cannot change the sensitivity. If in the future we look at other customization options, we’ll discuss this one too. If you are on a specific device where the sensitivity feels bad/wrong please let us know! We’ll take a look

Hey I tried it out and it works good! Thanks for enabling it!

1 Like

The problem I am having is that GuiService.SelectedObject constantly gets set to whatever the virtual cursor is hovering over. The way my current system works is that when the B/back button on the controller is pressed, depending on what it’s set to currently, SelectedObject gets set to a different gui button (a previous part of the menu e.g. from red trail menu to all trails menu). With the virtual cursor enabled, when SelectedObject gets set to the different gui button, it instantly gets set back to whatever the cursor is hovering over and this creates a loop that prevents users from traversing the menu unless they move their cursor off any buttons.

I didn’t think about it too much before but this is just a problem with the way I implemented my system and can easily be fixed. Having the security be ReadOnly would allow me to fix this much quicker but it’s definitely not as much of an issue as I originally thought.

1 Like

Hey! Support for scrolling frames is by far the top requested feature and is something actively being worked on. I can’t give an exact date when they’ll be supported but I’ll be sure to update the post when they are.

In terms of your other two points, do you have specific use cases around BillboardGui or SurfaceGuis you would like to use virtual cursor with? Also, just to be clear, toolbar items are not broken with virtual cursor, you are still able to equip them normally with the shoulder buttons, it’s just clicking on them with virtual cursor (like you would with a mouse) is not quite finished being implemented yet.

Glad you like the feature, and thanks for the feedback, it’s always appreciated and helpful.

1 Like

Hey! This should not be happening, can you tell me a little bit more about your setup? Do you have a gamepad plugged in?

Hey! You’ve found the unofficial support for scrolling frames, good job on your research! Going forward scrolling frames will either work like you describe in the first video or using the dpad. For example if you’re highlighting an object in a scrolling frame and you press dpad down, you’ll get the next object down. We haven’t picked a final solution yet but once it’s done I’ll be sure to update the post that scrolling frames are officially supported. It’s a highly requested feature to be supported and it is currently being worked on but I can’t say for sure when it’ll be done yet. Thanks for the feedback and trying it out in your experiences!

1 Like

Hey! When I move the cursor to the edge of the screen, cuts about half the cursor off and then stops. Are you seeing something different? I’d be interested to see a use case where virtual cursor cannot select a UI element because it is out of range.

Your dpad solution is quite nice! Scrolling frames are currently being worked on and will maybe function similarly to how yours does. I can’t say exactly when it’ll be done but I’ll be sure to update when it’s shipped.

Glad you like the feature! Thanks for trying it out!

1 Like

This is not possible and currently there are no plans to support this. If you feel you have good custom solution for UI control with gamepad feel free to use it over virtual cursor. We made virtual cursor as a way for developers to have a generally good solution out of the box if they didn’t have the time/skills to create something more complicated.

Hey! We are aware of this issue, and working on a fix currently. What platform are you on? Do you have a gamepad plugged in? Also is this in every experience you try or just specific ones?

Hey! I know you’ve been waiting a while for this! This should work on xbox, where are you seeing issues?

I actually just tested it on console and didn’t experience the issue with the cursor in the corner, it seems like it was only happening in studio, maybe due to my resolution. Great work with the virtual cursor so far - the players of my game seem to like it a lot!

1 Like