Vehicle Input Controller Module

gamepad
xbox
vehicle
input
control
#1

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

20 Likes
#2

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:

3 Likes
#4

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

1 Like