Vehicle Input Controller Module


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 = {
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
	controls[ci] = new

function m.GetControls() -- return the list of user input controls
	return controls
function m.GetInputs() -- return the state of user inputs
	return inputs

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 =
		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 or pos -- fixed deadzone variant

	if gpe and not key == "Thumbstick1" then return end -- thumbstick is a GPE, who wouldn't known?...

--	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
			inputs.Throttle = state == "Begin" and 1 or 0
--		print("Throttle",inputs.Throttle)

	if controls.Brake[code] then -- Brake
		if key:match("Button") then
			inputs.Brake = pos.Z
			inputs.Brake = state == "Begin" and 1 or 0
--		print("Brake:",inputs.Brake)

	if controls.SteerLeft[code] or controls.SteerRight[code] then
		if key:match("Thumbstick1") then
			inputs.Steer = pos.X * (state == "End" and 0 or 1)
			inputs.Steer = 1 * (controls.SteerLeft[code] and -1 or 1) * (state == "End" and 0 or 1)
--		print("Steer",inputs.Steer,pos.X)

	if state == "Begin" then
		if controls.GearUp[code] then
			inputs.GearUp = 1
--			print("Gear Up",inputs.GearUp)
		if controls.GearDown[code] then
			inputs.GearDown = 1
--			print("Gear Down",inputs.GearDown)
	if controls.Clutch[code] then
		inputs.Clutch = state == "Begin" and 1 or 0
--		print("Clutch",inputs.Clutch)
	if controls.Handbrake[code] then
		inputs.Handbrake = state == "Begin" and 1 or 0
--		print("Clutch",inputs.Clutch)

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")


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 = {

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. =)


Hello fellow devs! Have you had the chance to use this module?

If so, please leave me some feedback on any issues you had, and what can be improved!

Thanks! :upside_down_face:


I will definitely be trying this tomorrow I will give all the feedback I can

1 Like