Best way to get InputObjects?

I know for gamepad buttons you can use Documentation - Roblox Creator Hub but that doesn’t work for the general case

Is there anything better than this?

local key = Enum.KeyCode.A
local o

game:GetService("UserInputService").InputBegan:Connect(function(obj)
	if not o and obj.KeyCode == key then
		o = obj
	end
end)

What do you mean by ‘the general case?’ For keyboard input, UserInputService:GetKeysPressed() works well.

By general case I mean getting non gamepad keys such as the example which gets A

Also I’m looking to get the InputObject as fast as possible so :GetKeysPressed() is very inefficient compared to InputBegan

Here is some code for a custom controller that I’ve written for one of my games. I put this in a local script named ‘Controller’ in ‘StarterPlayerScripts’. To see what it is doing you’ll have to know that I keep all my remote events/functions in a folder called ‘Connections’ in the workspace, with subfolders separating them into similar responsibilities. What this script does is listen to what inputs the server is interested in by ‘binding’ them to one or more ‘actions’. When the interesting keys are pressed, I send all the actions invoked back to the server.

local Input = game:GetService 'UserInputService'
local Players = game:GetService 'Players'

local connections = workspace.Connections[script.Name]
local keys = {}
local mouse = Players.LocalPlayer:GetMouse()

local function fire(actions, ...)
	for action, TRUE in next, actions do
		connections.act:FireServer(action, ...)
	end
end

local function handle(input)
	local key = input.KeyCode ~= Enum.KeyCode.Unknown and input.KeyCode or input.UserInputType
	if keys[key] then
		local act = input.UserInputState == Enum.UserInputState.Begin
		if key == Enum.UserInputType.MouseButton2 then
			Input.MouseBehavior = act and Enum.MouseBehavior.LockCurrentPosition or Enum.MouseBehavior.Default
		end
		if key == Enum.UserInputType.MouseButton1 then
			fire(keys[key], act, mouse.Target)
		else 
			fire(keys[key], act, input.Delta, input.Position)
		end
	end
end

Input.InputBegan:Connect(function(input, processed)
	handle(input)
end)

Input.InputEnded:Connect(function(input, processed)
	handle(input)
end)

Input.InputChanged:Connect(function(input, processed)
	handle(input)
end)

function bind(key, action)
	if keys[key] then
		keys[key][action] = true
	else
		keys[key] = {[action] = true}
	end
end

function unbind(key, action)
	if keys[key] then
		keys[key][action] = nil
	end
end

connections.bind.OnClientEvent:Connect(bind)
connections.unbind.OnClientEvent:Connect(unbind)
connections.ready:FireServer()

I’m not sure how to make this more clear
The question is not to tell the server what keys the client has pressed

I’m looking for the fastest way to get the InputObject of a particular Enum.KeyCode

Ah Okay, you are you probably looking for is the ContextActionService, which allows you to listen to individual keystrokes. It basically does what my codes does above without as much work. I choose not to use it because it filters out some keystrokes (if I remember correctly w, a, s, and d don’t fire).

I don’t see any method there for getting the InputObject

ContextActionService:BindAction(string actionName, function functionToBind, bool createTouchButton, [KeyCode input]+)

takes a callback, functionToBind defined like so:

void functionToBind(string actionName, Enum.UserInputState inputState, InputObject inputObj)

Where inputObj is, I believe, what you are looking for.

1 Like

It still fires at the same time as InputBegan

Here’s a fun one. Using GetGamepadState(), you can actually get all the InputObjects for a given controller. InputObjects are mutable (i.e. they can change), which means you only have to call it once. From there, you can access the current input of a controller any time you want.

Here’s an example that throws all the inputs into a table using the KeyCode as the key:

local UIS = game:GetService("UserInputService")

-- Get input states and reference them based on the KeyCode:
local states = {}
for _,inputObj in pairs(gamepad1State = UIS:GetGamepadState(Enum.UserInputType.Gamepad1)) do
	states[inputObj.KeyCode] = inputObj
end

-- Thumbstick input:
local leftThumbstick = states[Enum.KeyCode.Thumbstick1]

-- Output thumbstick position every second:
while (true) do
	print("LeftThumbstick:", leftThumbstick.Position)
	wait(1)
end

Edit: Apparently there’s a Changed event on InputObjects too, so using the leftThumbstick variable from the above example:

leftThumbstick.Changed:Connect(function()
   print("LeftThumbstick:", leftThumbstick.Position)
end)

I’m not aware of any other way to collect InputObjects beforehand.

3 Likes

And my current method (the example code in the OP - which is also where that quote is from) caches the input object so I am aware that it can change (which is why I’m looking to get the InputObject)

Determining what is called before InputBegan, if anything, has a couple issues. First, it is a very small detail and may accidently change at some point. Second, determining which is faster may not always be reliable. Third, it is hard to tell without looking at Roblox source code or lots of testing… For most cases making a single handler that connects to all important input events and then decides which connection to run first is probably the way to go. If just two scripts need to be executed in the right order, have them communicate with each other using a custom event or the global table.

That being said, it could be that the UserInputService queues the InputBegan connection callbacks later in the game loop, so using RunService:BindToRenderStep(string name, int priority, function callback) with the proper priority (Enum.RenderPriority.Input, maybe +/- 1) would allow you to call GetKeysPressed and see them before the connected functions. If you don’t need the InputObject then you have a couple more options, like the Mouse object from different sources (player, tool, plugin).

2 Likes

I was hoping for something that lets you get them immediately without having to wait for key pressed