Last active
December 16, 2023 18:17
-
-
Save BrandonForbrad/def145706fb63b3a4b5d86cddad95a60 to your computer and use it in GitHub Desktop.
Roblox Lua Input/Device module. This script makes creating inputs for all devices easier and communicating them. (Works with ECS or Event Based/OOP)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--Inputs module | |
--Created by forbrad 12/02/2023 | |
--v1.0.0.1 | |
local Inputs = {} | |
---INPUT BINDS---------------------------------------- | |
--KBM | Keyboard and mouse (PC) | |
--TouchScreen | Any touch screen device (Mobile) | |
--Controller | Anygame controller (Console) | |
--For UiButton the second arguement will be the name of the tag within CollectionService (Set the tag to the relative buttons) | |
Inputs.Binds = { | |
["PlaceBlock"] = { KBM = {"UserInputType", "MouseButton1"}, TouchScreen = {"UserInputType", "Touch"}, Controller = {"KeyCode", "ButtonR1"}}, | |
["PrimaryAttack"] = { KBM = {"UserInputType", "MouseButton1"}, TouchScreen = {"UserInputType", "Touch"}, Controller = {"KeyCode", "ButtonR1"}}, | |
["ToggleInventory"] = { KBM = {"KeyCode", "E"}, TouchScreen = {"UiButton", "ToggleInventoryButton"}, Controller = {"KeyCode", "ButtonX"}, AllDevices = {"UiButton", "ToggleInventoryButton" }}, | |
["HotbarSlot1"] = { KBM = {"KeyCode", "One"}, TouchScreen = {"UiButton", "HotbarSlot1Button"}, Controller = {"UiButton", "HotbarSlot1Button"}, AllDevices = {"UiButton", "HotbarSlot1Button" } }, | |
["HotbarSlot2"] = { KBM = {"KeyCode", "Two"}, TouchScreen = {"UiButton", "HotbarSlot2Button"}, Controller = {"UiButton", "HotbarSlot2Button"}, AllDevices = {"UiButton", "HotbarSlot2Button" }}, | |
["HotbarSlot3"] = { KBM = {"KeyCode", "Three"}, TouchScreen = {"UiButton", "HotbarSlot3Button"}, Controller = {"UiButton", "HotbarSlot3Button"}, AllDevices = {"UiButton", "HotbarSlot3Button" } }, | |
["HotbarSlot4"] = { KBM = {"KeyCode", "Four"}, TouchScreen = {"UiButton", "HotbarSlot4Button"}, Controller = {"UiButton", "HotbarSlot4Button"}, AllDevices = {"UiButton", "HotbarSlot4Button" } }, | |
["HotbarSlot5"] = { KBM = {"KeyCode", "Five"}, TouchScreen = {"UiButton", "HotbarSlot5Button"}, Controller = {"UiButton", "HotbarSlot5Button"}, AllDevices = {"UiButton", "HotbarSlot5Button" } }, | |
["NextHotbarSlot"] = { KBM = {"KeyCode", "Q"}, TouchScreen = {"UiButton", "NextHotbarSlotButton"}, Controller = {"KeyCode", "ButtonR2"}, AllDevices = {"UiButton", "NextHotbarSlotButton" } }, | |
} | |
local AllowDeviceSwitching = true -- ALLOW PLAYERS TO SWITCH DEVICES | |
local DeviceSwitchBinds = { -- BINDS TO SWITCH DEVICE | |
KBM = {"UserInputType", "MouseButton1"}, | |
TouchScreen = {"UserInputType", "Touch"}, | |
Controller = {"KeyCode", "ButtonR1"}, | |
} | |
local Devices = { | |
KBM = "PC", | |
TouchScreen = "Mobile", | |
Controller = "Console" | |
} | |
--Inputs.Began | |
--Inputs.Ended | |
--Inputs.GetDevice | |
--Inputs.GetAllDevices | |
--Inputs.OnNewDevice | |
---------------------------------------------------------- | |
---CORE FUNCTIONALITY | |
Inputs.PlayerDevices = {} | |
Inputs.LocalDevice = "KBM" | |
local CollectionService = game:GetService("CollectionService") | |
local RunService = game:GetService("RunService") | |
local UIS = game:GetService("UserInputService") | |
local GuiService = game:GetService("GuiService") | |
local Signal : { | |
InputBegan: RemoteEvent, | |
InputEnded: RemoteEvent, | |
SetDevice: RemoteEvent | |
} = {} | |
local SetDeviceBind: BindableEvent | |
local function SetLocalDevice(DeviceName) | |
Inputs.LocalDevice = DeviceName | |
Inputs.PlayerDevices[game.Players.LocalPlayer] = DeviceName | |
Signal.SetDevice:FireServer(DeviceName) | |
SetDeviceBind:Fire(DeviceName) | |
end | |
setmetatable(Signal, {__index = function(_self, index) | |
return game.ReplicatedStorage:FindFirstChild(index) | |
end}) | |
function Signal.new(Name: "SignalName") : RemoteEvent | |
local Remote = Instance.new("RemoteEvent") | |
Remote.Name = Name | |
Remote.Parent = game.ReplicatedStorage | |
Signal[Name] = Remote | |
return Remote | |
end | |
local IsServer = RunService:IsServer() | |
function Inputs:Init() : "Starts and Initilizes the input system" | |
SetDeviceBind = Instance.new("BindableEvent") | |
SetDeviceBind.Parent = script | |
SetDeviceBind.Name = "SetDeviceBind" | |
if IsServer then | |
--Server | |
Signal.new("InputBegan") | |
Signal.new("InputEnded") | |
Signal.new("SetDevice") | |
Signal.SetDevice.OnServerEvent:Connect(function(player, Device) | |
if Devices[Device] == nil then return end | |
Inputs.PlayerDevices[player] = Device | |
SetDeviceBind:Fire(player, Device) | |
end) | |
else | |
--Client | |
if UIS.TouchEnabled then | |
SetLocalDevice("TouchScreen") | |
elseif GuiService:IsTenFootInterface() then | |
SetLocalDevice("Controller") | |
else | |
SetLocalDevice("KBM") | |
end | |
if AllowDeviceSwitching then | |
UIS.InputBegan:Connect(function(input, gameProcessedEvent) | |
if gameProcessedEvent then return end | |
for DeviceName, Binds in next, DeviceSwitchBinds do | |
if DeviceName == Inputs.LocalDevice then continue end | |
if input[Binds[1]] == Enum[Binds[1]][Binds[2]] then | |
SetLocalDevice(DeviceName) | |
break | |
end | |
end | |
end) | |
end | |
end | |
end | |
Inputs.Inited= false | |
if not Inputs.Inited then | |
Inputs.Inited = true | |
Inputs:Init() | |
end | |
function Inputs.GetDevice(player: Player): string | |
return Inputs.PlayerDevices[player] | |
end | |
function Inputs.GetAllDevices(): {[Player]: "device string"} | |
return Inputs.PlayerDevices | |
end | |
function Inputs.OnNewDevice(callback): RBXScriptConnection | |
return SetDeviceBind.Event:Connect(callback) | |
end | |
function Inputs.SendInputBegan(InputName, ...) | |
Signal.InputBegan:FireServer(InputName, ...) | |
end | |
function Inputs.SendInputEnded(InputName, ...) | |
Signal.InputEnded:FireServer(InputName, ...) | |
end | |
Inputs.HoldingInputs = {} | |
if IsServer then | |
for bindname, _ in next, Inputs.Binds do | |
Inputs.HoldingInputs[bindname] = {} | |
end | |
else | |
for bindname, _ in next, Inputs.Binds do | |
Inputs.HoldingInputs[bindname] = false | |
end | |
end | |
function Inputs.isHolding(InputName, player):boolean | |
if IsServer then | |
if Inputs.HoldingInputs[InputName] == nil then | |
Inputs.HoldingInputs[InputName] = {} | |
Inputs.HoldingInputs[InputName][player] = false | |
return false | |
end | |
if Inputs.HoldingInputs[InputName][player] == nil then | |
return false | |
end | |
return Inputs.HoldingInputs[InputName][player] | |
else | |
if Inputs.HoldingInputs[InputName] == nil then | |
return false | |
end | |
return Inputs.HoldingInputs[InputName] | |
end | |
end | |
local LastPressButtonName: string = {} | |
function Inputs.Began(InputName: string, callback): RBXScriptConnection | |
if IsServer then | |
--Server | |
return Signal.InputBegan.OnServerEvent:Connect(function(player, input, ...) | |
if input == InputName then | |
Inputs.HoldingInputs[InputName][player] = true | |
callback(player, ...) | |
end | |
end) | |
else | |
--Client | |
local function ThrowCallback() | |
Inputs.HoldingInputs[InputName]= true | |
local ArgsToSend = {callback()} | |
if ArgsToSend[1] ~= true then return end | |
table.remove(ArgsToSend, 1) | |
Signal.InputBegan:FireServer(InputName, unpack(ArgsToSend)) | |
end | |
return UIS.InputBegan:Connect(function(input, gameProcessedEvent) | |
local CurrBinds = Inputs.Binds[InputName][Inputs.LocalDevice] | |
if CurrBinds[1] == "UiButton" then | |
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then | |
for _, Button: GuiButton in CollectionService:GetTagged(CurrBinds[2]) do | |
if Button.GuiState ~= Enum.GuiState.Press then continue end | |
table.insert(LastPressButtonName, InputName) | |
ThrowCallback() | |
break | |
end | |
end | |
return | |
end | |
if input[CurrBinds[1]] == Enum[CurrBinds[1]][CurrBinds[2]] and not gameProcessedEvent then | |
ThrowCallback() | |
end | |
if Inputs.Binds[InputName].AllDevices == nil then return end | |
CurrBinds = Inputs.Binds[InputName].AllDevices | |
if CurrBinds[1] == "UiButton" then | |
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then | |
for _, Button: GuiButton in CollectionService:GetTagged(CurrBinds[2]) do | |
if Button.GuiState ~= Enum.GuiState.Press then continue end | |
table.insert(LastPressButtonName, InputName) | |
ThrowCallback() | |
break | |
end | |
end | |
return | |
end | |
if input[CurrBinds[1]] == Enum[CurrBinds[1]][CurrBinds[2]] and not gameProcessedEvent then | |
ThrowCallback() | |
end | |
end) | |
end | |
end | |
function Inputs.Ended(InputName , callback) : RBXScriptConnection | |
if IsServer then | |
--Server | |
return Signal.InputEnded.OnServerEvent:Connect(function(player, input, ...) | |
if input == InputName then | |
Inputs.HoldingInputs[InputName][player] = false | |
callback(player, ...) | |
end | |
end) | |
else | |
--Client | |
local function ThrowCallback() | |
Inputs.HoldingInputs[InputName] = false | |
local ArgsToSend = {callback()} | |
if ArgsToSend[1] ~= true then return end | |
table.remove(ArgsToSend, 1) | |
Signal.InputEnded:FireServer(InputName, unpack(ArgsToSend)) | |
end | |
return UIS.InputEnded:Connect(function(input) | |
local CurrBinds = Inputs.Binds[InputName][Inputs.LocalDevice] | |
local function Check() | |
if CurrBinds[1] == "UiButton" then | |
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then | |
local fbn = table.find(LastPressButtonName, InputName) | |
if fbn ~= nil then | |
table.remove(LastPressButtonName, fbn) | |
ThrowCallback() | |
end | |
end | |
return | |
end | |
if input[CurrBinds[1]] == Enum[CurrBinds[1]][CurrBinds[2]] then | |
ThrowCallback() | |
end | |
end | |
task.spawn(Check) | |
CurrBinds = Inputs.Binds[InputName].AllDevices | |
if CurrBinds == nil then return end | |
task.spawn(Check) | |
end) | |
end | |
end | |
--for ECS systems | |
Inputs.NewInputList = {} | |
function Inputs.onInputBegan(InputName) | |
if Inputs.NewInputList[InputName.."Began"] == nil then | |
return {} | |
end | |
return Inputs.NewInputList[InputName.."Began"] | |
end | |
function Inputs.onInputEnded(InputName) | |
if Inputs.NewInputList[InputName.."Ended"] == nil then | |
return {} | |
end | |
return Inputs.NewInputList[InputName.."Ended"] | |
end | |
Inputs.IndexedECS = false | |
function Inputs.nextstep() | |
if not Inputs.IndexedECS then | |
Inputs.IndexedECS = true | |
for bindname, _ in next, Inputs.Binds do | |
Inputs.NewInputList[bindname.."Began"] = {} | |
Inputs.Began(bindname, function(...) | |
table.insert(Inputs.NewInputList[bindname.."Began"], { | |
bindname = bindname, | |
SendToServer = function(...) | |
Inputs.SendInputBegan(bindname, ...) | |
end, | |
args = {...} | |
}) | |
end) | |
Inputs.NewInputList[bindname.."Ended"] = {} | |
Inputs.Ended(bindname, function(...) | |
table.insert(Inputs.NewInputList[bindname.."Ended"], { | |
bindname = bindname, | |
SendToServer = function(...) | |
Inputs.SendInputEnded(bindname, ...) | |
end, | |
args = {...} | |
}) | |
end) | |
end | |
end | |
for bindname, _ in next, Inputs.NewInputList do | |
table.clear(Inputs.NewInputList[bindname]) | |
end | |
end | |
return Inputs | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
v1.0.01: added compatibility for ECS and functionality for inputs under the same binds and better support for ROJO/Rewire based systems.