02-10-2023, 01:39 PM
Okay, let's see how simple I can make this. Here's what I put in the header:
The constants at the top are user operations in your program. For my game project, that's movement, face buttons, pause button, OK and cancel for menu navigation, etc.
The two arrays at the bottom are where inputs are stored and updated. press() is what keys are pressed on this loop, hold() is what was pressed last loop. So every loop, when update_inputs is called, press() gets copied over to hold().
Routines in my input handling library:
new_press is the function you would check if you want to see if something is being freshly pressed (not held down) on this loop. For example, if new_press(ok_key) = true then [accept menu option]. Any loop where you're taking user input needs to have an update_inputs call. If you want to clear user input completely, you can call set_hold true. The bulk of the data is carried in set_key_data, as well as a block of code such as this:
Once you have this data set, you can call set_default_keybinds and all the assignments above are made. You would have to code your own front-end keybinding menu, I can share what I've done for my game with that, but it may be very project-specific.
So to sum up, you just use these routines, set the defaults as you see fit, call update_inputs in your loops, and check new_press() using the input constants at the top, to detect if that key was freshly pressed. You could also check press() and hold() together to see if it's being held down or released during a given loop.
Here's a sample loop, a stripped-down version of my game's title screen routine, I've omitted most of the stuff that you don't need to wade through, like text draws:
Let me know if you need me to clarify anything, hopefully this was a little clearer than mud!
Code: (Select All)
' References for press function and hold array
const up_key = 1
const down_key = 2
const left_key = 3
const right_key = 4
const weapon_key = 5
const shield_key = 6
const engine_key = 7
const energy_key = 8
const wpn_sel_right = 9
const wpn_sel_left = 10
const ok_key = 11
const cancel_key = 12
const pause_key = 13
const enter_key = 14
const esc_key = 15
' Input reference and binding data
const keybind_count = 15 ' Number of gameplay functions, including enter and esc
dim shared dev_keyboard as byte ' Store device index, to be re-checked whenever inputs are involved
dim shared dev_gamepad as byte
dim shared dev_mouse as byte
const keyboard = 1
const gamepad = 2
const mouse = 3
kc = keybind_count
dim shared key_name$(512, 2) ' Names of keyboard keys and gamepad buttons
dim shared keybind(kc, 2) as integer ' Contains key code assigned by player
dim shared keybind_default(kc, 2) as integer ' Defaults in case player wants to reset
dim shared axis_threshold as single ' Amount a stick needs to be tilted before input is registered
axis_threshold = 0.7
set_key_data
' Input tracking flags
dim shared press(kc) as byte ' What was pressed this frame
dim shared hold(kc) as byte ' What was pressed last frame
The constants at the top are user operations in your program. For my game project, that's movement, face buttons, pause button, OK and cancel for menu navigation, etc.
The two arrays at the bottom are where inputs are stored and updated. press() is what keys are pressed on this loop, hold() is what was pressed last loop. So every loop, when update_inputs is called, press() gets copied over to hold().
Routines in my input handling library:
Code: (Select All)
function new_press(b)
new_press = false
if press(b) = true and hold(b) = false then new_press = true
end function
sub update_inputs
call detect_devices
for b = 1 to keybind_count
hold(b) = press(b)
press(b) = false
d = keyboard
if dev_keyboard <> false then
z = deviceinput(dev_keyboard)
if button(keybind(b, d)) = true then press(b) = true
end if
d = gamepad
if dev_gamepad <> false and keybind(b, d) <> false then
z = deviceinput(dev_gamepad)
if keybind(b, d) < 100 then ' Button
if button(keybind(b, d)) = true then press(b) = true
' Stick handling:
' keybind() set to 101, 102 etc. is an assignment of stick 1, 2 etc. in the negative direction
' keybind() set to 201, 202 etc. is an assignment of stick 1, 2 etc. in the positive direction
elseif keybind(b, d) > 200 then ' Stick positive
if axis(keybind(b, d) - 200) > option_sensitivity * 0.1 then press(b) = true
else ' Stick negative
if axis(keybind(b, d) - 100) < -option_sensitivity * 0.1 then press(b) = true
end if
end if
next b
if dev_mouse = false then exit sub
dim mb(3)
for b = 1 to 3
mb(b) = mouse_press(b)
next b
mouse_change = false
do while mouseinput <> false and mouse_change = false
mouse_pos_x = mousex
mouse_pos_y = mousey
for b = 1 to 3
mb(b) = mousebutton(b)
if mb(b) <> mouse_press(b) then mouse_change = true
next b
loop
for b = 1 to 3
mouse_hold(b) = mouse_press(b)
mouse_press(b) = mb(b)
if mouse_press(b) = true and mouse_hold(b) = false then
mouse_press_x(b) = mouse_pos_x ' Store position mouse button was pressed
mouse_press_y(b) = mouse_pos_y
elseif mouse_press(b) = false and mouse_hold(b) = true then
mouse_release_x(b) = mouse_pos_x ' Store position mouse button was released
mouse_release_y(b) = mouse_pos_y
end if
next b
end sub
sub detect_devices
dev_keyboard = false
dev_gamepad = false
dev_mouse = false
d = devices
for n = d to 1 step -1
if left$(device$(n), 10) = "[KEYBOARD]" then dev_keyboard = n
if left$(device$(n), 12) = "[CONTROLLER]" then dev_gamepad = n
if left$(device$(n), 7) = "[MOUSE]" then dev_mouse = n
next n
end sub
sub set_press(p)
for b = 1 to keybind_count
press(b) = p
next b
end sub
sub set_hold(p)
for b = 1 to keybind_count
hold(b) = p
next b
end sub
sub press_any_key
do
limit 60
display
for b = 1 to keybind_count
if new_press(b) = true then exit sub
next b
call update_inputs
loop
end sub
sub set_default_keybinds
call detect_devices
for b = 1 to keybind_count
keybind(b, keyboard) = keybind_default(b, keyboard)
keybind(b, gamepad) = keybind_default(b, gamepad)
next b
if dev_gamepad = false then exit sub
' Eliminate any defaults that go beyond a gamepad's features
d = gamepad
for b = 1 to keybind_count
if keybind(b, d) < 100 and keybind(b, d) > lastbutton(dev_gamepad) then keybind(b, d) = false
next b
if lastaxis(dev_gamepad) < 2 then
keybind(up_key, d) = false
keybind(down_key, d) = false
keybind(left_key, d) = false
keybind(right_key, d) = false
end if
end sub
sub set_key_data
key_name$(false, keyboard) = "NOT SET"
key_name$(false, gamepad) = "NOT SET"
d = keyboard
for n = 1 to 512
key_name$(n, d) = "UNKNOWN"
next n
key_name$(2, d) = "ESC"
key_name$(60, d) = "F1"
key_name$(61, d) = "F2"
key_name$(62, d) = "F3"
key_name$(63, d) = "F4"
key_name$(64, d) = "F5"
key_name$(65, d) = "F6"
key_name$(66, d) = "F7"
key_name$(67, d) = "F8"
key_name$(68, d) = "F9"
key_name$(88, d) = "F11"
key_name$(89, d) = "F12"
key_name$(42, d) = "~"
key_name$(3, d) = "1"
key_name$(4, d) = "2"
key_name$(5, d) = "3"
key_name$(6, d) = "4"
key_name$(7, d) = "5"
key_name$(8, d) = "6"
key_name$(9, d) = "7"
key_name$(10, d) = "8"
key_name$(11, d) = "9"
key_name$(12, d) = "0"
key_name$(13, d) = "-"
key_name$(14, d) = "="
key_name$(15, d) = "BKSP"
key_name$(16, d) = "TAB"
key_name$(17, d) = "Q"
key_name$(18, d) = "W"
key_name$(19, d) = "E"
key_name$(20, d) = "R"
key_name$(21, d) = "T"
key_name$(22, d) = "Y"
key_name$(23, d) = "U"
key_name$(24, d) = "I"
key_name$(25, d) = "O"
key_name$(26, d) = "P"
key_name$(27, d) = "["
key_name$(28, d) = "]"
key_name$(44, d) = "\"
key_name$(31, d) = "A"
key_name$(32, d) = "S"
key_name$(33, d) = "D"
key_name$(34, d) = "F"
key_name$(35, d) = "G"
key_name$(36, d) = "H"
key_name$(37, d) = "J"
key_name$(38, d) = "K"
key_name$(39, d) = "L"
key_name$(40, d) = ";"
key_name$(41, d) = "'"
key_name$(29, d) = "ENTER"
key_name$(43, d) = "L SHIFT"
key_name$(45, d) = "Z"
key_name$(46, d) = "X"
key_name$(47, d) = "C"
key_name$(48, d) = "V"
key_name$(49, d) = "B"
key_name$(50, d) = "N"
key_name$(51, d) = "M"
key_name$(52, d) = ","
key_name$(53, d) = "."
key_name$(54, d) = "/"
key_name$(55, d) = "R SHIFT"
key_name$(30, d) = "L CTRL"
key_name$(58, d) = "SPACE"
key_name$(286, d) = "R CTRL"
key_name$(339, d) = "INS"
key_name$(340, d) = "DEL"
key_name$(328, d) = "HOME"
key_name$(336, d) = "END"
key_name$(330, d) = "PG UP"
key_name$(338, d) = "PG DN"
key_name$(329, d) = "UP"
key_name$(337, d) = "DOWN"
key_name$(332, d) = "LEFT"
key_name$(334, d) = "RIGHT"
key_name$(310, d) = "NUM /"
key_name$(56, d) = "NUM *"
key_name$(75, d) = "NUM -"
key_name$(79, d) = "NUM +"
key_name$(285, d) = "NUM ENTER"
key_name$(72, d) = "NUM 7"
key_name$(73, d) = "NUM 8"
key_name$(74, d) = "NUM 9"
key_name$(76, d) = "NUM 4"
key_name$(77, d) = "NUM 5"
key_name$(78, d) = "NUM 6"
key_name$(80, d) = "NUM 1"
key_name$(81, d) = "NUM 2"
key_name$(82, d) = "NUM 3"
key_name$(83, d) = "NUM 0"
key_name$(84, d) = "NUM ."
' Troublesome keyboard codes:
' 71 - Scroll Lock
' 70 - Pause
' 59 - Caps Lock
' 348 - Windows Left
' 349 - Windows Right?
' 350 - Menu
' 326 - Num Lock
d = gamepad
for n = 1 to 20
key_name$(n, d) = "BUTTON" + str$(n)
next n
for n = 1 to 8
key_name$(n + 100, d) = "AXIS" + str$(n) + "-" ' *** Check that first axis is horizontal
key_name$(n + 200, d) = "AXIS" + str$(n) + "+"
next n
end sub
new_press is the function you would check if you want to see if something is being freshly pressed (not held down) on this loop. For example, if new_press(ok_key) = true then [accept menu option]. Any loop where you're taking user input needs to have an update_inputs call. If you want to clear user input completely, you can call set_hold true. The bulk of the data is carried in set_key_data, as well as a block of code such as this:
Code: (Select All)
d = keyboard
keybind_default(up_key, d) = 329
keybind_default(down_key, d) = 337
keybind_default(left_key, d) = 332
keybind_default(right_key, d) = 334
keybind_default(weapon_key, d) = 46 ' x
keybind_default(shield_key, d) = 47 ' c
keybind_default(engine_key, d) = 45 ' z
keybind_default(energy_key, d) = 30 ' LCTRL
keybind_default(wpn_sel_right, d) = 34 ' f
keybind_default(wpn_sel_left, d) = 33 ' d
keybind_default(ok_key, d) = 46 ' x
keybind_default(cancel_key, d) = 47 ' c
keybind_default(pause_key, d) = 31 ' a
keybind_default(enter_key, d) = 29
keybind_default(esc_key, d) = 2
Once you have this data set, you can call set_default_keybinds and all the assignments above are made. You would have to code your own front-end keybinding menu, I can share what I've done for my game with that, but it may be very project-specific.
So to sum up, you just use these routines, set the defaults as you see fit, call update_inputs in your loops, and check new_press() using the input constants at the top, to detect if that key was freshly pressed. You could also check press() and hold() together to see if it's being held down or released during a given loop.
Here's a sample loop, a stripped-down version of my game's title screen routine, I've omitted most of the stuff that you don't need to wade through, like text draws:
Code: (Select All)
do
limit 60
display_screen
update_inputs
if new_press(up_key) = true and new_press(down_key) = false then cursor = wrap(cursor - 1, 0, 2)
if new_press(down_key) = true and new_press(up_key) = false then cursor = wrap(cursor + 1, 0, 2)
if new_press(enter_key) = false and new_press(ok_key) = false then continue
if cursor = 1 then option_menu: c = 0: continue ' Options
if cursor = 2 then exit sub ' Quit
' Start game, etc etc
loop
Let me know if you need me to clarify anything, hopefully this was a little clearer than mud!