I created this inputManager module to quickly set up many input types (Holds, Taps, Positive/Negative, etc.) It has currently not given me any problems so far, but i feel like it is a bit bulking and wanted to know if there’s some ways to reduce it and maybe some optimizations too.
here’s the module script:
local cas = game:GetService("ContextActionService")
local uis = game:GetService("UserInputService")
local inputs = {}
local bools = {}
local UIManager = {}
UIManager.__index = UIManager
local function CreateAxis(actionName, actionState, positiveName, negativeName)
if bools[positiveName] == nil then bools[positiveName] = false end
if bools[negativeName] == nil then bools[negativeName] = false end
if actionName == positiveName then
bools[positiveName] = actionState == Enum.UserInputState.Begin
end
if actionName == negativeName then
bools[negativeName] = actionState == Enum.UserInputState.Begin
end
if bools[positiveName] == bools[negativeName] then
return 0
else
if bools[positiveName] then
return 1
elseif bools[negativeName] then
return -1
end
end
end
local function CreateToggle(actionName, actionState, name, initialBool)
if bools[name] == nil then bools[name] = initialBool end
if actionState == Enum.UserInputState.Begin then
bools[name] = not bools[name]
end
return bools[name]
end
local function CreateHold(actionName, actionState, name, invert)
if actionState == Enum.UserInputState.Begin then
bools[name] = not invert
elseif actionState == Enum.UserInputState.End then
bools[name] = invert
end
return bools[name]
end
local function CreateTap(actionName, actionState, name, tapType, changedEvent)
if actionState == Enum.UserInputState.Begin then
if tapType == "KeyDown" then
changedEvent:Fire()
end
end
if actionState == Enum.UserInputState.End then
if tapType == "KeyUp" then
changedEvent:Fire()
end
end
end
UIManager.GetInputs = function()
return inputs
end
UIManager.CreateMouseAxis = function(baseName, mouseAxis, bind)
local InputMouseAxis = setmetatable({},UIManager)
local changedEvent = Instance.new("BindableEvent")
local unbindEvent = Instance.new("BindableEvent")
local bindEvent = Instance.new("BindableEvent")
local lastInput = 0
local bindNow = bind or false
local mouseConnection = nil
InputMouseAxis.Value = 0
InputMouseAxis.Name = baseName
InputMouseAxis.UnBinding = unbindEvent.Event
InputMouseAxis.Binding = bindEvent.Event
InputMouseAxis.Changed = changedEvent.Event
InputMouseAxis.Binded = false
local function GetDelta()
local deltaVector = uis:GetMouseDelta().Unit
if deltaVector.Magnitude > 0 then
if mouseAxis == "Vertical" then
InputMouseAxis.Value = deltaVector.Y
elseif mouseAxis == "Horizontal" then
InputMouseAxis.Value = deltaVector.X
else
InputMouseAxis.Value = deltaVector
end
else
if mouseAxis == "Vertical" or mouseAxis == "Horizontal" then
InputMouseAxis.Value = 0
else
InputMouseAxis.Value = Vector2.zero
end
end
end
local changedCheck = game:GetService("RunService").RenderStepped:Connect(function()
if InputMouseAxis.Value == lastInput then return end
changedEvent:Fire(InputMouseAxis.Value)
lastInput = InputMouseAxis.Value
end)
function InputMouseAxis:UnBind(resetValue)
mouseConnection:Disconnect()
mouseConnection = nil
unbindEvent:Fire()
self.Binded = false
if typeof(self.Value) == "number" then
self.Value = 0
else
self.Value = Vector2.zero
end
end
function InputMouseAxis:Bind()
mouseConnection = game:GetService("RunService").RenderStepped:Connect(GetDelta)
bindEvent:Fire()
self.Binded = true
end
if bindNow then
InputMouseAxis:Bind()
end
inputs[baseName] = InputMouseAxis
end
UIManager.CreateAxis = function(baseName, positiveInput, negativeInput, GUIButton, bind)
local InputAxis = setmetatable({}, UIManager)
local changedEvent = Instance.new("BindableEvent")
local unbindEvent = Instance.new("BindableEvent")
local bindEvent = Instance.new("BindableEvent")
local deleteEvent = Instance.new("BindableEvent")
local lastInput = 0
local bindNow = bind or false
InputAxis.Value = 0
InputAxis.Name = baseName
InputAxis.PositiveName = tostring(baseName .. "Pos")
InputAxis.NegativeName = tostring(baseName .. "Neg")
InputAxis.Changed = changedEvent.Event
InputAxis.UnBinding = unbindEvent.Event
InputAxis.Binding = bindEvent.Event
InputAxis.Deleting = deleteEvent.Event
InputAxis.Binded = false
local func = function(as, an)
InputAxis.Value = CreateAxis(as, an, InputAxis.PositiveName, InputAxis.NegativeName)
end
local changedCheck = game:GetService("RunService").RenderStepped:Connect(function()
if InputAxis.Value == lastInput then return end
changedEvent:Fire(InputAxis.Value)
lastInput = InputAxis.Value
end)
function InputAxis:UnBind(resetValue)
local reset = resetValue or false
cas:UnbindAction(self.PositiveName)
cas:UnbindAction(self.NegativeName)
unbindEvent:Fire()
self.Binded = false
if reset then
self.Value = 0
end
end
function InputAxis:Bind()
cas:BindAction(self.PositiveName, func, GUIButton, positiveInput)
cas:BindAction(self.NegativeName, func, GUIButton, negativeInput)
bindEvent:Fire()
self.Binded = true
end
function InputAxis:Delete()
if self.Binded then
cas:UnbindAction(self.PositiveName)
cas:UnbindAction(self.NegativeName)
end
changedCheck:Disconnect()
inputs[self.Name] = nil
bools[self.PositiveName] = nil
bools[self.NegativeName] = nil
deleteEvent:Fire()
self = nil
end
if bindNow then
InputAxis:Bind()
end
inputs[baseName] = InputAxis
end
UIManager.CreateToggle = function(baseName, toggleInput, GUIButton, initialBool, bind)
local InputToggle = setmetatable({}, UIManager)
local changedEvent = Instance.new("BindableEvent")
local unbindEvent = Instance.new("BindableEvent")
local bindEvent = Instance.new("BindableEvent")
local deleteEvent = Instance.new("BindableEvent")
local lastInput = initialBool
local bindNow = bind or false
InputToggle.Value = initialBool
InputToggle.Name = baseName
InputToggle.Changed = changedEvent.Event
InputToggle.UnBinding = unbindEvent.Event
InputToggle.Binding = bindEvent.Event
InputToggle.Deleting = deleteEvent.Event
InputToggle.Binded = false
local func = function(as, an)
InputToggle.Value = CreateToggle(as, an, baseName, initialBool)
end
local changedCheck = game:GetService("RunService").RenderStepped:Connect(function()
if InputToggle.Value == lastInput then return end
changedEvent:Fire(InputToggle.Value)
lastInput = InputToggle.Value
end)
function InputToggle:UnBind(resetValue)
local reset = resetValue or false
cas:UnbindAction(self.Name .. "Toggle")
unbindEvent:Fire()
self.Binded = false
if reset then
self.Value = initialBool
end
end
function InputToggle:Bind()
cas:BindAction(self.Name .. "Toggle", func, GUIButton, toggleInput)
bindEvent:Fire()
self.Binded = true
end
function InputToggle:Delete()
if self.Binded then
cas:UnbindAction(self.Name .. "Toggle")
end
changedCheck:Disconnect()
inputs[self.Name] = nil
bools[self.Name .. "Toggle"] = nil
deleteEvent:Fire()
self = nil
end
if bindNow then
InputToggle:Bind()
end
inputs[baseName] = InputToggle
end
UIManager.CreateHold = function(baseName, holdInput, GUIButton, invert, bind)
local InputHold = setmetatable({}, UIManager)
local changedEvent = Instance.new("BindableEvent")
local unbindEvent = Instance.new("BindableEvent")
local bindEvent = Instance.new("BindableEvent")
local deleteEvent = Instance.new("BindableEvent")
local lastInput = invert
local bindNow = bind or false
bools[baseName] = invert
InputHold.Value = invert
InputHold.Name = baseName
InputHold.Changed = changedEvent.Event
InputHold.UnBinding = unbindEvent.Event
InputHold.Binding = bindEvent.Event
InputHold.Deleting = deleteEvent.Event
InputHold.Binded = false
local func = function(as, am)
InputHold.Value = CreateHold(as, am, baseName, invert)
end
local changedCheck = game:GetService("RunService").RenderStepped:Connect(function()
if InputHold.Value == lastInput then return end
changedEvent:Fire(InputHold.Value)
lastInput = InputHold.Value
end)
function InputHold:UnBind(resetValue)
local reset = resetValue or false
cas:UnbindAction(self.Name .. "Hold")
unbindEvent:Fire()
self.Binded = false
if reset then
self.Value = invert
end
end
function InputHold:Bind()
cas:BindAction(self.Name .. "Hold", func, GUIButton, holdInput)
bindEvent:Fire()
self.Binded = true
end
function InputHold:Delete()
if self.Binded then
cas:UnbindAction(self.Name .. "Hold")
end
changedCheck:Disconnect()
inputs[self.Name] = nil
bools[self.Name .. "Hold"] = nil
deleteEvent:Fire()
self = nil
end
if bindNow then
InputHold:Bind()
end
inputs[baseName] = InputHold
end
UIManager.CreateTap = function(baseName, tapInput, tapType, GUIButton, bind)
local InputTap = setmetatable({}, UIManager)
local changedEvent = Instance.new("BindableEvent")
local unbindEvent = Instance.new("BindableEvent")
local bindEvent = Instance.new("BindableEvent")
local deleteEvent = Instance.new("BindableEvent")
local bindNow = bind or false
InputTap.Name = baseName
InputTap.Fired = changedEvent.Event
InputTap.UnBinding = unbindEvent.Event
InputTap.Binding = bindEvent.Event
InputTap.Deleting = deleteEvent.Event
InputTap.Binded = bindNow
local func = function(as, am)
CreateTap(as, am, baseName, tapType, changedEvent)
end
function InputTap:UnBind(resetValue)
cas:UnbindAction(self.Name .. "Tap")
unbindEvent:Fire()
self.Binded = false
end
function InputTap:Bind()
cas:BindAction(self.Name .. "Tap", func, GUIButton, tapInput)
bindEvent:Fire()
self.Binded = true
end
function InputTap:Delete()
if self.Binded then
cas:UnbindAction(self.Name .. "Tap")
end
inputs[self.Name] = nil
deleteEvent:Fire()
self = nil
end
if bindNow then
InputTap:Bind()
end
inputs[baseName] = InputTap
end
return UIManager
here’s an example of how it works on a local script in case you need to know:
local inputManager = require(game:GetService("ReplicatedStorage").InputManager)
--create inputs here
inputManager.CreateAxis("TestAxis", Enum.KeyCode.Up, Enum.KeyCode.Down, false, false)
local testAxis = inputManager.GetInputs().TestAxis
--binds input
testAxis:Bind()
--unbinds input with an optional reset value
testAxis:UnBind(0)
--deletes the input object
testAxis:Delete()
testAxis.Value -- current input value
testAxis.Binded -- boolean for if input is currently binded
testAxis.Binding:Connect(function()
--fires whenever the input is binded
end)
testAxis.UnBinding:Connect(function()
--fires whenever the input is unbinded
end)
testAxis.Deleting:Connect(function()
--fires whenever the input object gets deleted
end)
testAxis.Changed:Connect(function(inputValue)
--fires whenever the input changes
end)