Tip: Your input system should not be an opaque abstraction
Posted: Sat Mar 14, 2026 4:42 pm
Almost every engine out there has this sort of high level "input actions" API that they encourage using instead of you polling individual devices such as the keyword and gamepad.
In godot, they literally call it input actions, and every godot game uses it, so it's far from an unpopular idea!
But I did realize something. It's something that bit me in carrot survivors, and couldn't quite fix without a pile of hacks. This time, when encountering the same issue I decided to tackle it properly.
Consider this simple (heh) requirement:
"When using a joystick for movement, the character attacks in the direction of movement, unless they're using the right joystick, in that case the charcter attacks in the direction the right joystick is aiming. Oh, and if they're using a mouse forget all this, the direction of attack is the direction the mouse is pointing at from the player."
Now, you still want to go through some generic input layer for the most part, but you can see how the above requirement throws a wrench into the whole thing. The truth is, if you want your game's user experience to be good, you cannot really "abstract away" input devices. Sure, you can do this for the jump key, spacebar or some gamepad button, but anything more complex than that and you need to start delving into specifics.
The issue I'm dealing with rn is close to the one I described above. None of this is rocket science, it's just hard work and lots of iteration to figure out what people need to play in various ways (remember! the more input styles you support, the more accessible your game will be). But one particular aspect annoyed me: How do I know which input mode I'm in?
As an owner of a steam controller, I know how annoying it is when games use mose movement as an indicator that the player is using keyboard and mouse, so I'm not gonna do that. Furthermore, for Iris Blade, there's two keyboard input styles: One that uses the mouse, with Left and Right buttons to attack, and one that uses only keyboard, with the J and K keys for attacks instead[1]. Using keyboard input as a predictor that the player is playing with their mouse is thus not possible.
Having discarded mouse movement and keyboard input, the only reliable indicator that the player wants to attack with the mouse (and thus, we should use mouse position to direct their attack) is the mouse keys themselves... Again, no shit Sherlock, I could've been a bit less roundabout-y about this huh?
But circling back to the original input actions talk, I have an input action defined for the attack action, which is conveniently bound to Left mouse button, the J keyboard key and the X gamepad button all at once. I have a great API for this:
Problem is, if my code has any sort of special logic that depends on the input device, as I described above, I need to undo that, and... check each input device manually? Now that's bad! I was planning on using the input action system to allow configurable key remappings and now I won't be able to... unless?
Actually the solution was simpler than I originally thought:
By adding this optional output parameter, I can check whether the action was pressed, but still get to peek at the internals of the event that produced the action to do custom logic. Now that was such a simple change that I'm very mad no other engine has it.
[1] I'm planning on making this configurable, but that's a story for another day.
In godot, they literally call it input actions, and every godot game uses it, so it's far from an unpopular idea!
But I did realize something. It's something that bit me in carrot survivors, and couldn't quite fix without a pile of hacks. This time, when encountering the same issue I decided to tackle it properly.
Consider this simple (heh) requirement:
"When using a joystick for movement, the character attacks in the direction of movement, unless they're using the right joystick, in that case the charcter attacks in the direction the right joystick is aiming. Oh, and if they're using a mouse forget all this, the direction of attack is the direction the mouse is pointing at from the player."
Now, you still want to go through some generic input layer for the most part, but you can see how the above requirement throws a wrench into the whole thing. The truth is, if you want your game's user experience to be good, you cannot really "abstract away" input devices. Sure, you can do this for the jump key, spacebar or some gamepad button, but anything more complex than that and you need to start delving into specifics.
The issue I'm dealing with rn is close to the one I described above. None of this is rocket science, it's just hard work and lots of iteration to figure out what people need to play in various ways (remember! the more input styles you support, the more accessible your game will be). But one particular aspect annoyed me: How do I know which input mode I'm in?
As an owner of a steam controller, I know how annoying it is when games use mose movement as an indicator that the player is using keyboard and mouse, so I'm not gonna do that. Furthermore, for Iris Blade, there's two keyboard input styles: One that uses the mouse, with Left and Right buttons to attack, and one that uses only keyboard, with the J and K keys for attacks instead[1]. Using keyboard input as a predictor that the player is playing with their mouse is thus not possible.
Having discarded mouse movement and keyboard input, the only reliable indicator that the player wants to attack with the mouse (and thus, we should use mouse position to direct their attack) is the mouse keys themselves... Again, no shit Sherlock, I could've been a bit less roundabout-y about this huh?
But circling back to the original input actions talk, I have an input action defined for the attack action, which is conveniently bound to Left mouse button, the J keyboard key and the X gamepad button all at once. I have a great API for this:
Code: Select all
if (Input.IsActionJustPressed(InputAction.Attack) {
...
}
Actually the solution was simpler than I originally thought:
Code: Select all
if (Input.IsActionJustPressed(InputAction.Attack, out var actionBinding) {
if (actionBinding.IsMouse()) {
// mouse aim logic
}
else {
// aim where the character is facing based on movement
}
}
[1] I'm planning on making this configurable, but that's a story for another day.