INPUT Version 1.0.2 - A small library that handles keyboard, touch, and gamepad inputs with one function.
Hi, I’m Flipitagainpls and today I’d like to come and share a module I recently made called “Input”. Input is a module that handles keyboard, gamepad, and touch inputs by the use of one function which turns that tedious task of handling inputs within your game much easier. This module uses signals and metadata to handle and manage your inputs securely while giving you creative control by giving precise settings that are all changeable. Thanks for using Input!
Update: Version 1.0.2 (12/27/2025)
-
Yeilding away parameters to make your input exclusive to one or two methods is now
enabled, meaning you can now register inputs for one device rather than all three by
settingexlcusiveInputto true. For the imput methods you don’t want to use, just
set them tonil -
exclusiveKeybindparameter has been removed as it wasn’t finished and it was
replaced withexclusiveInput
Remote API:
-- CLIENT
Input.new{
keybind: EnumItem,
holdable: boolean,
button: GuiButton,
gamepadKeybind: EnumItem,
exclusiveInput: boolean,
inputIndex: string,
}
input:Started: (self: Input, RBXScriptConnection) -> (),
input:Finished: (self: Input, RBXScriptConnection) -> (),
input:Destroy: (self: Input, ...any) -> (...any),
Input Tutorial:
Click to open the Input tutorial
local ReplicatedStorage = game:GetService'ReplicatedStorage'
local Input = require(ReplicatedStorage.Input)
--[[
@Input.new
@the function setup that enables the rest of the script
]]
local registeredInput = Input.new{
holdable = false, -- determines whether the input is holdable or not
button = script.Parent, -- the button for mobile inputs
inputIndex = 'TestInput', -- the index that the handler will refer to
keybind = Enum.KeyCode.F, -- the keybind for pc
exclusiveInput = false, -- if the input is exclusive to one device, will just run whichever method is given,
gamepadKeybind = Enum.KeyCode.ButtonR1, -- the gamepad button for console
}
--[[
@input:Started
@param inputFunction: RBXScriptConnection
@the function is ran once the input is pressed / held
]]
registeredInput:Started(function(...)
print'held'
end)
--[[
@input:Finished
@param inputFunction: RBXScriptConnection
@the function is ran once the input is released / let go (only works if holdable is set to true)
@notice: can cause memory leaks if the function is here but not being used; don't write the function if the input isn't holdable
]]
registeredInput:Finished(function(...)
print'released'
end)
--[[
@input:Destroy
@the function that is ran once a character dies or the character leaves, a memory cleanup method
]]
registeredInput:Destroy()
Just place the module wherever you’d like and begin your magic!
Link: Input by Flipitagainpls - Creator Store
Source Code:
Input.lua
--!strict
local Input = {}
Input.__index = Input
local RunService = game:GetService'RunService'
export type Input = {
keybind: EnumItem,
holdable: boolean,
button: GuiButton,
gamepadKeybind: EnumItem,
exclusiveKeybind: boolean,
inputIndex: string,
--@functions
Started: (self: Input, RBXScriptConnection) -> (),
Finished: (self: Input, RBXScriptConnection) -> (),
Destroy: (self: Input, ...any) -> (...any),
}
local InputHandler = require(script.InputHandler)
function Input.new(
inputData: Input
)
assert(inputData.inputIndex, `index "inputIndex" is missing or nil.`)
assert(type(inputData.inputIndex) == 'string', `given "inputIndex" is not a string`)
if not inputData.exclusiveKeybind then inputData.exclusiveKeybind = false end
if not inputData.exclusiveKeybind then
assert(inputData.keybind, `index "Keybind" is missing or nil.`)
assert(inputData.button, `index "button" is missing or nil.`)
end
if RunService:IsServer() then
return
end
return table.freeze(setmetatable({
keybind = inputData.keybind,
holdable = inputData.holdable or false,
button = inputData.button,
inputIndex = inputData.inputIndex,
gamepadKeybind = inputData.gamepadKeybind or nil
} :: Input, Input))
end
--[[
@input:Started
@param inputFunction: RBXScriptConnection
@the function is ran once the input is pressed / held
]]
function Input:Started(inputFunction: RBXScriptConnection)
if not self.exclusiveKeybind then
assert(typeof(self.keybind) == 'EnumItem', `given "keybind" is not an EnumItem`)
if self.button then if not self.button:IsA'GuiButton' then error(`Given "button" is not a guiButton`) end end
end
InputHandler.RegisterInput{
button = self.button,
keybind = self.keybind,
holdable = self.holdable,
inputIndex = self.inputIndex,
gamepadKeybind = self.gamepadKeybind,
inputFunction = inputFunction
}
end
--[[
@input:Finished
@param inputFunction: RBXScriptConnection
@the function is ran once the input is released / let go (only works if holdable is set to true)
@notice: can cause memory leaks if the function is here but not being used; don't write the function if the input isn't holdable
]]
function Input:Finished(inputFunction: RBXScriptConnection)
if not self.holdable then return end
InputHandler.UnregisterInput{
button = self.button,
keybind = self.keybind,
inputIndex = self.inputIndex,
gamepadKeybind = self.gamepadKeybind,
inputFunction = inputFunction
}
end
--[[
@input:Destroy
@the function that is ran once a character dies or the character leaves, a memory cleanup method
]]
function Input:Destroy(...)
local cache = InputHandler._GetCache(self.inputIndex)
local connections = InputHandler._GetConnections(self.inputIndex)
cache.signal:DisconnectAll()
cache.connection:Disconnect()
for _, connection in connections do
connection:Disconnect()
end;
end
return Input
Thanks for reading, please give me critiques as I’ll be implementing this module into my own projects and I’d like to know how to make this module better for others to use. I’m open to hear any suggestions for functions you think are useful as well. Please remember this module is really new and is not prone to errors, please let me know if you find any and I’ll try to fix them as efficiently and effectively as possible. Have fun!
