I’m trying to make an input buffer for my fighting game and would like some help with improving what I currently have which will hopefully put me in the right direction I’m aware this is quite an advanced subject, but I’d like to learn and give it an attempt anyways. I’m getting the majority of my info from this article I think it’s pretty resourceful (you can read the second section “Input Buffers?” which explains things well) I’m just having difficulties being able to translate what’s being said into code and it doesn’t help that the code being used is C++ haha.
Quick side note I’ve made a working input buffer script in the past which works quite well and I used another article (which was sadly taken down) to make it so I’ll share the code for anyone curious LuaU Input Buffer - Pastebin.com. I’d say the meat and potatoes of this module is the addCommand()
, update()
, and findBestActionMatch()
functions everything else is just kind of utility.
Now for the main topic my new input buffer system, I wanna improve on the old one there are a few issues such as continuously adding the same input instead of just once when it’s first pressed, not really tracking what frame it was first pressed on or how many frames the button was pressed for, etc. So the article I’m currently reading from what I’ve dissected from it you can kind of break down the input buffer into 3 sections Virtual buttons, Storing the commands, and Reading the buffer. For the Virtual buttons, it basically talks about how you shouldn’t map game actions to physical buttons “Create a set of generic buttons representing the functions you need and then handle which physical button is meant to activate which function separately.” and I think I managed to get this part done.
Code Explanation: Everything in Enums.InputButtons
is basically just a number Up_Direction = 4, Right_Direction = 8, Down_Direction = 2, etc and then in self.keymap
I map those numbers to keys and then when one of those keys are pressed I just add the value to self.inputs
so if W and D is pressed (which is the UpRight action) self.inputs
will be 12 since 4 + 8 is 12 and now I know W and D AKA UpRight is pressed.
Enums.InputButtons = {
Neutral_Input = bit32.lshift(1, 18),
None = 0,
Down_Direction = bit32.lshift(1, 0),
Left_Direction = bit32.lshift(1, 1),
Up_Direction = bit32.lshift(1, 2),
Right_Direction = bit32.lshift(1, 3),
DownLeft_Direction = bit32.bor(bit32.lshift(1, 0), bit32.lshift(1, 1)),
DownRight_Direction = bit32.bor(bit32.lshift(1, 0), bit32.lshift(1, 3)),
UpLeft_Direction = bit32.bor(bit32.lshift(1, 2), bit32.lshift(1, 1)),
UpRight_Direction = bit32.bor(bit32.lshift(1, 2), bit32.lshift(1, 3)),
TaptoHoldButtonShift = 19,
HeldDown_Direction = bit32.lshift(bit32.lshift(1, 0), 19),
Guard_Button = bit32.lshift(1, 4),
Punch_Button = bit32.lshift(1, 5),
Kick_Button = bit32.lshift(1, 6),
Tech_Button = bit32.lshift(1, 7),
Trigger_Button = bit32.lshift(1, 8),
Selection_Button = bit32.lshift(1, 9),
Cancel_Button = bit32.lshift(1, 10),
Modifier_Button = bit32.lshift(1, 11),
Menu_Pause_Button = bit32.lshift(1, 12),
}
function Input.new()
local self = setmetatable({},Input)
self.inputs = 0 -- Players inputs
self.buttonCounters = {} -- Store currently pressed buttons
self.inputBuffer = {}
self.buttonsPressedLastFrame = {}
self.keybinds = {
["Left"] = Enum.KeyCode.A,
["Right"] = Enum.KeyCode.D,
["Up"] = Enum.KeyCode.W,
["Down"] = Enum.KeyCode.S,
["LightAttack"] = Enum.KeyCode.H,
}
self.keymap = {
[self.keybinds["Left"]] = inputButtons.Left_Direction,
[self.keybinds["Right"]] = inputButtons.Right_Direction,
[self.keybinds["Up"]] = inputButtons.Up_Direction,
[self.keybinds["Down"]] = inputButtons.Down_Direction,
[self.keybinds["LightAttack"]] = inputButtons.Punch_Button,
}
UserInputService.InputBegan:Connect(function(inputObject, gameProcessed)
self:inputBegan(inputObject, gameProcessed)
end)
UserInputService.InputEnded:Connect(function(inputObject, gameProcessed)
self:inputEnded(inputObject, gameProcessed)
end)
return self
end
function Input:inputBegan(inputObject, gameProcessed)
if gameProcessed then
return
end
if inputObject.UserInputType ~= Enum.UserInputType.Keyboard then
return
end
-- gonna be something like 8, 2, 4, etc
local input = self.keymap[inputObject.KeyCode]
-- set the input bit in the inputs number
self.inputs = bit32.bor(self.inputs, input)
end
function Input:inputEnded(inputObject, gameProcessed)
if gameProcessed then
return
end
if inputObject.UserInputType ~= Enum.UserInputType.Keyboard then
return
end
local input = self.keymap[inputObject.KeyCode]
-- unset the input bit in the inputs number
self.inputs = bit32.band(self.inputs, bit32.bnot(input))
end
After this, the next part would be to store the commands/input which is what I’m currently stuck on I feel like the code I currently have kind of does what it’s supposed to I can now track what frame a button was pressed on, and for how long, but it’s still continuously added, but unlike my last input buffer the input is at least limited to 1.
function Input:isBitSet(bit)
return bit32.band(self.inputs, bit) == bit
end
-- Update the button counters
function Input:updateButtonCounters()
-- Loop through all buttons
for keyCode,bit in pairs(self.keymap) do
local isPressed = self:isBitSet(bit)
if isPressed then
-- If the button is pressed, increment its counter
if self.buttonCounters[bit] == nil then
self.buttonCounters[bit] = 0
end
self.buttonCounters[bit] = self.buttonCounters[bit] + 1
else
-- If the button is not pressed, reset its counter
if self.buttonCounters[bit] and self.buttonCounters[bit] > 0 then
print(bit,"was held for",self.buttonCounters[bit],"frames")
end
self.buttonCounters[bit] = 0
end
end
end
-- Add a button press to the input buffer
function Input:addButtonToInputBuffer(inputBuffer, bit, framesHeld)
-- Check if the button was pressed for a certain number of frames
if framesHeld >= 1 then
-- Add the button press to the input buffer
table.insert(inputBuffer, {action = bit, framesHeld = framesHeld})
end
end
function Input:processInput()
local inputBuffer = {}
-- Update the button counters
self:updateButtonCounters()
-- Loop through the button counters
for bit, framesHeld in pairs(self.buttonCounters) do
-- Add the button press to the input buffer
self:addButtonToInputBuffer(inputBuffer, bit, framesHeld)
end
if #inputBuffer > 0 then
print(inputBuffer)
end
return inputBuffer
end
function Input:update(currentFrame)
local buffer = self:processInput()
end