Heya fellow developers!
I was just updating my Input module for my car game and thought someone else might find it useful, so here you go!
local m = {} -- functions
local inputs = {} -- input states
local controls = {} -- keybinds
inputs.Throttle = 0; -- User input determining the power sent to the wheels
inputs.Steer = 0; -- User input determining the angle at which the wheels should be turned.
inputs.Clutch = 0; -- A mediator between the engine/throttle and the wheels
inputs.Handbrake = 0; -- State of the handbrake which stops the rear wheels from turnning - 0 = released, 1 = engaged.
inputs.Brake = 0;
inputs.GearUp = 0;
inputs.GearDown = 0;
controls.Throttle = {
Enum.KeyCode.W,Enum.KeyCode.Up,Enum.KeyCode.ButtonR2,Enum.UserInputType.Touch
}
controls.Brake = {
Enum.KeyCode.S, Enum.KeyCode.Down, Enum.KeyCode.ButtonL2,Enum.UserInputType.Touch
}
controls.SteerLeft = {
Enum.KeyCode.A, Enum.KeyCode.Left, Enum.KeyCode.Thumbstick1,Enum.UserInputType.Touch
}
controls.SteerRight = {
Enum.KeyCode.D, Enum.KeyCode.Right, Enum.KeyCode.Thumbstick1,Enum.UserInputType.Touch
}
controls.Handbrake = {
Enum.KeyCode.Space, Enum.KeyCode.ButtonA
}
controls.Clutch = {
Enum.KeyCode.LeftControl,Enum.KeyCode.LeftShift,Enum.KeyCode.RightControl, Enum.KeyCode.ButtonL1
}
controls.GearUp = {
Enum.KeyCode.R, Enum.KeyCode.E, Enum.KeyCode.ButtonB
}
controls.GearDown = {
Enum.KeyCode.F,Enum.KeyCode.Q, Enum.KeyCode.ButtonX
}
for ci,control in pairs(controls) do -- converting enums to have readable indexes.
local new = {}
for i,keycode in pairs(control) do
local index = keycode.Value
new[index] = index
end
controls[ci] = new
end
function m.GetControls() -- return the list of user input controls
return controls
end
function m.GetInputs() -- return the state of user inputs
return inputs
end
function m.GetInput(io,gpe) -- inputObject, gameProcessedEvent
local state = io.UserInputState.Name -- setting enums to strings with '.Name' is much easier
local typ = io.UserInputType.Name
local pos = io.Position --pos shows the current position of an input (e,g, mouse.X/Y)
local delta = io.Delta -- delta shows how much has CHANGED from last input
local key = io.KeyCode.Name
local code = io.KeyCode.Value -- gets the KeyCode number value
local gamepad = typ:match("Gamepad") -- is it a gamepad?
pos = Vector3.new(
pos.X * math.abs(pos.X), -- account for deadzone from thumbstick input.
pos.Y * math.abs(pos.Y),
pos.Z * math.abs(pos.Z)
)
--pos.Magnitude <= 0.125 and Vector3.new() or pos -- fixed deadzone variant
if gpe and not key == "Thumbstick1" then return end -- thumbstick is a GPE, who wouldn't known?...
--print(state,typ,delta,pos,code,touch)
-- print("State",state)
-- print("IOType",typ)
-- print("POS:",pos)
-- print("DELTA:",delta)
-- print("KEY:",key)
-- print(controls.Throttle[key])
-- print("Gamepad:",gamepad)
if controls.Throttle[code] then -- Throttle
if key:match("Button") then
inputs.Throttle = pos.Z
else
inputs.Throttle = state == "Begin" and 1 or 0
end
-- print("Throttle",inputs.Throttle)
end
if controls.Brake[code] then -- Brake
if key:match("Button") then
inputs.Brake = pos.Z
else
inputs.Brake = state == "Begin" and 1 or 0
end
-- print("Brake:",inputs.Brake)
end
if controls.SteerLeft[code] or controls.SteerRight[code] then
if key:match("Thumbstick1") then
inputs.Steer = pos.X * (state == "End" and 0 or 1)
else
inputs.Steer = 1 * (controls.SteerLeft[code] and -1 or 1) * (state == "End" and 0 or 1)
end
-- print("Steer",inputs.Steer,pos.X)
end
if state == "Begin" then
if controls.GearUp[code] then
inputs.GearUp = 1
-- print("Gear Up",inputs.GearUp)
end
if controls.GearDown[code] then
inputs.GearDown = 1
-- print("Gear Down",inputs.GearDown)
end
end
if controls.Clutch[code] then
inputs.Clutch = state == "Begin" and 1 or 0
-- print("Clutch",inputs.Clutch)
end
if controls.Handbrake[code] then
inputs.Handbrake = state == "Begin" and 1 or 0
-- print("Clutch",inputs.Clutch)
end
end
return m
Setting up the module
To make use of the module, require it via a LocalScript, then use the UserInputService to fire the module whenever a user input is triggered:
local controlsModule = require(script:WaitForChild("ControlsModule"))
local UserInputService = game:GetService("UserInputService")
UserInputService.InputChanged:Connect(controlsModule.GetInput)
UserInputService.InputBegan:Connect(controlsModule.GetInput)
UserInputService.InputEnded:Connect(controlsModule.GetInput)
Using the module
You can easily add, remove or change the input requirements in the ācontrolsā table.
For example, if you wanted to engage Left Steering when you press the āJā button;
controls.SteerLeft = {
Enum.KeyCode.J
}
You could even manipulate this from an external script, if you wanted to include custom controls.
(however you would probably need to do some work-around for gamepad input).
All inputs return a value between 0 (disengaged) and 1 (engaged) except for Steering, which is a value between -1 and 1.
On line 65 for the module I have included a damping (is that what you call it?) so as to negate dead zone in Gamepad thumbsticks for steering. However I left the damping for all gamepad input as I felt it made a nicer experience for gamepad input.
To get information from it, call the āGetInputsā function to return a list of the inputs.
local input = controlsModule.GetInputs() -- returns the list of 'inputs' and their states
local throttle = input.Throttle -- returns the input for the 'Throttle'
local steer = input.Steer -- returns the input for the 'Steering'
Leave me feedback!
My previous attempt at making a controller system wasā¦ horrible, to say the least, however Iām really curious how you find using this, whether it is useful, and if I can make any improvements!
So let me know down below!
Hope its useful to you. =)