Input - Input handling module that turns keyboard, touch, and gamepad inputs to 1 | INSANELY USEFUL & ORGANIZED

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
    setting exlcusiveInput to true. For the imput methods you don’t want to use, just
    set them to nil

  • exclusiveKeybind parameter has been removed as it wasn’t finished and it was
    replaced with exclusiveInput

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!

7 Likes

Would be nice if we could yield away option for parameters, such as not having to use a button and or gamepad.

1 Like

I’m going to add that in the next version sometime this week, thanks for the feedback.

Are there any advantages to this over the Input Action System?

1 Like

Also interested in this, I will get this once the new version is released

1 Like

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
    setting exlcusiveInput to true. For the imput methods you don’t want to use, just
    set them to nil

  • exclusiveKeybind parameter has been removed as it wasn’t finished and it was
    replaced with exclusiveInput

It’s updated now, give me some feedback and let me know how you feel about it!

1 Like

The purpose of this system was to construct a way to create inputs without the messy code and also a way to incorporate all three input methods (gamepad, key bind, and touch) without having to construct a whole codebase for it. It’s also memory safe because It offers a way to disconnect all methods and functions used as well as clean up the signals fired in the main script.

1 Like

Hey, it’s updated, I hope you like it!

1 Like

Okay but is this a superior alternative to the Input Action System? You don’t have to “construct a whole codebase” around that, its about the same amount of work and it has better integration with the engine as well as a future for server authority.

If you’re asking if my module has anything that makes it better than ROBLOX’s code itself, then the answer is no. My purpose of writing this module was to incorporate all three methods without having to write a whole bunch of code for it. You don’t have to use it because it doesn’t change the performance at all (unless you didn’t clean your code properly) but if you want something less tedious or messy, then this is the code for you.

Except IAS can incorporate all of those methods without a “whole bunch of code”? That’s literally what it was designed to do.

Then use that? The people who want to use this and see my purpose for it will use it, I will never force you or tell you that my system is better than the next, you’re free to any system on this platform.

Right but I’m asking if there’s any advantage this module provides? That’s why I’m not saying “this is useless because IAS exists”

1 Like

I have my own system similar to this. Wouldn’t it be better if a new Input Object accepted a table of inputs instead of just one?

1 Like

I’m actually thinking of making an update specifically for this, since I’ve noticed I needed a method like that for my game, I’m gonna implement it on the weekend when I have time.

1 Like

I’d say no, I just felt like making this to be an easier method for my inputs since my input handling can feel very cluttered when I include all three methods.