What this script does
The purpose is simple, but the usual solution is quite an ouroboros of functions and if statements if you don't know how to structure it based on logic or how modules work.
This ModuleScript
returns a function which sets up 2 listeners: InputBegan
and InputEnded
. These listeners are connected once when the function is required, and they listen only for your specified movement keys (W, A, S, and D, defined in the keyMap
table).
When those keys are pressed or released, the listeners write to a proxy table, which uses a __newindex
metamethod to change values in the real MovementKeys
table, defined outside of the returned function scope. Based on logical conditions, the metamethod updates currentDirection
accordingly.
If you’re pressing opposite keys (e.g., A and D or W and S), the corresponding axis is considered inactive.
Finally, the function returns the currentDirection
string, which you can access in any LocalScript. I don’t think I need to explain the use of this script—if you’re here, you probably already know why you need it.
local UIS = game:GetService("UserInputService")
local MovementKeys = {
W = false;
S = false;
A = false;
D = false;
}
local listenersCalled
local currentDirection = "None"
return function()
local proxy = setmetatable({},{
__newindex = function(_, k, v)
MovementKeys[k] = v
local InactiveMovementAxis = {
Y = MovementKeys.W == MovementKeys.S;
X = MovementKeys.A == MovementKeys.D;
}
if InactiveMovementAxis.Y and not InactiveMovementAxis.X then
currentDirection = MovementKeys.A and "Left" or currentDirection
currentDirection = MovementKeys.D and "Right" or currentDirection
end
if InactiveMovementAxis.X and not InactiveMovementAxis.Y then
currentDirection = MovementKeys.W and "Up" or currentDirection
currentDirection = MovementKeys.S and "Down" or currentDirection
end
if not InactiveMovementAxis.X and not InactiveMovementAxis.Y then
currentDirection = MovementKeys.W and MovementKeys.D and "UpRight" or currentDirection
currentDirection = MovementKeys.W and MovementKeys.A and "UpLeft" or currentDirection
currentDirection = MovementKeys.S and MovementKeys.D and "DownRight" or currentDirection
currentDirection = MovementKeys.S and MovementKeys.A and "DownLeft" or currentDirection
elseif InactiveMovementAxis.X and InactiveMovementAxis.Y then
currentDirection = "None"
end
end,
})
local keyMap = {
[Enum.KeyCode.W] = "W";
[Enum.KeyCode.A] = "A";
[Enum.KeyCode.S] = "S";
[Enum.KeyCode.D] = "D";
}
if listenersCalled then return currentDirection end
UIS.InputBegan:Connect(function(input)
if not keyMap[input.KeyCode] then return end
proxy[keyMap[input.KeyCode]] = true
end)
UIS.InputEnded:Connect(function(input)
if not keyMap[input.KeyCode] then return end
proxy[keyMap[input.KeyCode]] = false
end)
listenersCalled = true
return currentDirection
end
Organisation
Incase you don't know how module scripts work, here's where to put it and how to require it:
Create a module script inside an accessible folder (most commonly ReplicatedStorage). Create a local script, which you put in either the player folders or StarterGui, then you require them like this:
local keyHandler = require(game:GetService("ReplicatedStorage").KeyHandler)
In order to use it, you can index it, but keep in mind that it's a FUNCTION, so you need to put brackets (parentheses), otherwise it's only gonna return the function ID
print(keyHandler()) --Prints the currentDirection string value
Let me know if you have any improvements and use this script with grace