Update is delayed. Can you show how you use KeyHandler?
Attempt to compare number and nil often happens at InputEnded code. If it is in InputBegin code then something is not right with your usage. I can help you if you provide your KeyHandler usage code
Sure, this is a simplified version of my input code because I can’t release the main one, but the important stuff is there.
local Projectiles = KeyHandler.Create({
Keys = {
{"E,ButtonR1"},
},
Func = function(state, ht)
print("Working")
if debounce == false and canUse and state == Enum.UserInputState.Begin then
--do something
elseif state == Enum.UserInputState.Cancel then
return
end
end,
} :: KeyHandler.KeyValues)
My code not uses Enum.UserInputState.Cancel. Its Enum.UserInputState.End
Oh okay, I’ll switch it out and see how that works
Please show the line that causing error(in module)
if AllKeys[key] then
local highest = 0
for _, Key in ipairs(AllKeys[key]) do
print(Key.Priority, highest)
if Key.Priority > highest then --> this one
ActiveKeys[key] = {Key}
highest = Key.Priority
Key.ActiveKeysIndex = 1
elseif Key.Priority == highest then
local function findKeyWithSameParent(parent)
for i,v in ipairs(ActiveKeys[key]) do
if v.Parent == parent then
return i
end
end
end
local index = findKeyWithSameParent(Key.Parent)
if index then
if Key.Index < ActiveKeys[key][index].Index then
ActiveKeys[key][index] = Key
Key.ActiveKeysIndex = index
end
else
table.insert(ActiveKeys[key], Key)
Key.ActiveKeysIndex = #ActiveKeys[key]
end
end
end
table.insert(AllKeys[key], Key)
Key.AllKeysIndex = #AllKeys[key]
else
AllKeys[key] = {LocalKey}
ActiveKeys[key] = {LocalKey}
Key.ActiveKeysIndex = #ActiveKeys[key]
Key.AllKeysIndex = #AllKeys[key]
end
setmetatable(LocalKey, Key)
end
I think, I fixed this bug. If it will happen again contact me
Edit: Use updated script
Hey I think the way keycodes are assigned should be done via Enum.Keycode instead of strings.
Edit: I ended up changing this myself here is the code:
Edit2: Fixed the code because only the last keycode you set would activate the function
--Version 3.2
--Source:
--https://devforum.roblox.com/t/customizable-keybinds-better-uis-or-advanced-cas/2910676
export type KeyValues = {
Keys: {{
key : {KeyCode : Enum.KeyCode},
func : (state: Enum.UserInputType, ht : number) -> nil,
GUI : ImageButton | TextButton
}},
Function: (state: Enum.UserInputType, ht: number) -> nil,
Active : boolean,
ToggledKey : boolean, -- If true, the function will be activated after the key is pressed and deactivated if the key is pressed again. If false, the function will be activated when the key is held down and deactivated when the key is no longer held.
Ordered: boolean,
Priority: number,
MaxHoldTime: number,
AlterFunc: (state: Enum.UserInputType, ht: number) -> nil,
ResetFuncs: boolean,
MaxTime: number,
LateFunc: (state: Enum.UserInputType, ht: number) -> nil
}
local UIS = game:GetService("UserInputService")
local RunS = game:GetService("RunService")
local ActiveKeys = {} --Enabled Keys with Highest Priority
local AllKeys = {} --All asigned Keys
local KeyHandler = {}
KeyHandler.Default = {
ToggledKey = false,
Ordered = false,
ResetFuncs = false,
Priority = 1,
MaxHoldTime = 2,
MaxTime = 3
}
KeyHandler.WasActivated = {}
local Key = {}
Key.__index = Key
function KeyHandler.Create(values : KeyValues)
local KeytableProxy = values
KeytableProxy.Active = true
KeytableProxy.LastHT = 0
KeytableProxy.KeysActive = {}
KeytableProxy.TimeRunningUp = nil
for i,v in pairs(KeyHandler.Default) do
if KeytableProxy[i] == nil then
KeytableProxy[i] = v
end
end
for i, tablekey in ipairs(KeytableProxy.Keys) do
local keycodetable = {}
KeytableProxy.KeysActive[i] = false
for _, keycode in ipairs(tablekey[1]) do
table.insert(keycodetable, keycode.Name)
end
for _, key in ipairs(keycodetable) do
Key.Create(i, key, tablekey[2], tablekey[3], KeytableProxy)
end
end
local KeytableMeta = {}
KeytableMeta.__index = KeytableProxy
KeytableMeta.__newindex = function(t,i,v)
local value = KeytableProxy[i]
if value ~= v then
if i == "Keys" then
if value ~= nil then
--//DeleteKey
for i, keyindex in ipairs(KeytableProxy[i]) do
local keys = string.split(keyindex, ",")
for _, Key in ipairs(keys) do
Key:Destroy()
end
end
end
--//Add Key
for i, tablekey in ipairs(v) do
local keys = string.split(tablekey[1], ",")
KeytableProxy.KeysActive[i] = false
for _, key in ipairs(keys) do
Key.Create(i, key, tablekey[2], tablekey[3], KeytableProxy)
end
end
end
KeytableProxy[i] = v
end
end
local Keytable = {}
return setmetatable(Keytable, KeytableMeta)
end
function Key.Create(i, key : string, func, GUI : ImageButton|TextButton, Parent)
local LocalKey = {
key = key,
Priority = Parent.Priority,
Func = func,
AlterFunc = Parent.AlterFuncs ~= nil and Parent.AlterFuncs[key] or Parent.AlterFuncs ~= nil and Parent.AlterFuncs[i] or nil,
MaxHoldTime = Parent.MaxHoldTime or KeyHandler.MaxHoldTime,
LastHT = 0,
Keytable = Parent,
Index = i
}
if game.UserInputService.TouchEnabled == true then
if GUI ~= nil then
GUI.Visible = true
GUI.MouseButton1Down:Connect(function()
LocalKey:Fire(Enum.UserInputState.Begin)
end)
GUI.MouseButton1Up:Connect(function()
LocalKey:Fire(Enum.UserInputState.End)
end)
end
end
if AllKeys[key] then
local highest = 0
for _, Key in ipairs(AllKeys[key]) do
if Key.Priority > highest then
ActiveKeys[key] = {Key}
highest = Key.Priority
Key.ActiveKeysIndex = 1
elseif Key.Priority == highest then
local function findKeyWithSameParent(parent)
for i,v in ipairs(ActiveKeys[key]) do
if v.Parent == parent then
return i
end
end
end
local index = findKeyWithSameParent(Key.Parent)
if index then
if Key.Index < ActiveKeys[key][index].Index then
ActiveKeys[key][index] = Key
Key.ActiveKeysIndex = index
end
else
table.insert(ActiveKeys[key], Key)
Key.ActiveKeysIndex = #ActiveKeys[key]
end
end
end
table.insert(AllKeys[key], Key)
Key.AllKeysIndex = #AllKeys[key]
else
AllKeys[key] = {LocalKey}
ActiveKeys[key] = {LocalKey}
Key.ActiveKeysIndex = #ActiveKeys[key]
Key.AllKeysIndex = #AllKeys[key]
end
setmetatable(LocalKey, Key)
end
function Key:Fire(state : Enum.UserInputState)
if self.Keytable.Active == false then return false end
if state == Enum.UserInputState.Begin then
if state ~= nil and self.Keytable.Ordered == true and self.Keytable.KeysActive[self.Index-1] ~= nil and self.Keytable.KeysActive[self.Index-1] == false then
return false
end
if self.Keytable.TimeRunningUp == nil then
task.spawn(function()
local MaxTime = 0
self.Keytable.TimeRunningUp = RunS.Heartbeat:Connect(function(dt)
MaxTime += dt
if MaxTime > self.Keytable.MaxTime then
table.clear(KeyHandler.WasActivated)
if self.Keytable.LateFunc ~= nil then
self.Keytable.LateFunc()
end
self.Keytable.TimeRunningUp:Disconnect()
end
end)
end)
end
self.Keytable.KeysActive[self.Index] = true
self.LastHT = tick()
if self.Func then
self.Func(Enum.UserInputState.Begin)
end
if not table.find(self.Keytable.KeysActive, false) then
self.Keytable.LastHT = tick()
self.Keytable.Function(Enum.UserInputState.Begin)
end
else
if tick() - self.LastHT < self.MaxHoldTime then
if not table.find(self.Keytable.KeysActive, false) then
self.Keytable.LastHT = tick() - self.Keytable.LastHT
self.Keytable.Function(Enum.UserInputState.End, self.Keytable.LastHT)
end
if self.Func then
if self.Keytable.ResetFuncs ~= true or self.Keytable.LastHT == 0 then
self.LastHT = tick() - self.LastHT
self.Func(Enum.UserInputState.End, self.LastHT)
end
end
elseif self.AlterFunc then
if self.Keytable.ResetFuncs ~= true or self.Keytable.LastHT == 0 then
self.LastHT = tick() - self.LastHT
self.AlterFunc(Enum.UserInputState.End, self.LastHT)
end
end
self.Keytable.KeysActive[self.Index] = false
end
if state ~= nil then return true end
end
function Key:Destroy()
table.remove(ActiveKeys[self.key], self.ActiveKeysIndex)
table.remove(AllKeys[self.key], self.AllKeysIndex)
end
local KeyConnections = nil
function KeyHandler:Start()
KeyConnections = {
Began = UIS.InputBegan:Connect(function(input, gPE)
local key = input.KeyCode.Name
--if gPE then return end
if ActiveKeys[key] then
for _, Key in ipairs(ActiveKeys[key]) do
local Active = Key:Fire(Enum.UserInputState.Begin)
if Active then table.insert(KeyHandler.WasActivated, Key) end
end
end
end),
Ended = UIS.InputEnded:Connect(function(input, gPE)
local key = input.KeyCode.Name
--if gPE then return end
for _, localKey in ipairs(KeyHandler.WasActivated) do
localKey:Fire(Enum.UserInputState.End)
end
table.clear(KeyHandler.WasActivated)
end)
}
end
function KeyHandler:GetKeys()
print("All Keys: ", AllKeys)
print("Active Keys: ", ActiveKeys)
end
function KeyHandler:Stop()
if KeyConnections ~= nil then
KeyConnections.Begin:Disconnect()
KeyConnections.Ended:Disconnect()
KeyConnections = nil
else
warn("KeyHandler is not active")
end
end
return KeyHandler
Is this still being worked on? If not please let me know because I wanted to use this for my game but may have to change it back.
Still. I had work as Game Designer. Learned script architectures. I was making my own version of Knit, that has KeyHandler and more. The new KeyHandler is a lot faster and has additional funcs.
Maybe release it in 4 days.
I can’t decide to make this module:
- ServerSided - module gets commands to add keybind from server script, binds this key to player and activates func when key pressed.
- ClientSided - all previous versions. Requiring module from localscript, and call func.
Both have pros and cons. I think I will make 3 versions: ClientSided, ServerSided, Hybrid
When you release all 3 versions, can you list the pros and cons of each version?
Alright, thanks for the update
Any update on these new versions being released? Also I think it should be client sided.
Users may need different cases. My pc is broken. I wrote code samples on my phone, but can’t test it. I will use library’s pc to test and publish it. 2-3 days