Open fritzw opened 9 months ago
Last time I checked, XInput actually worked, when I pressed some button on the gamepad before starting Clonk (or restarting it afterwards), or something similar. The same was also true for other old games like Need for Speed Underground 2.
It might still have been in Windows 7, but I would hope that Microsoft didn’t drop that compatibility in newer versions.
All the button and axis names are definitely not helpful at all. I wonder how much better it would be when using SDLs Gamepad support.
Splitting menu actions from the normal controls is an interesting idea. There is already some special handling for the mouse, which could be helpful. Fortunately, in this case, the menu handling functionality for scripts is quite limited. So there should not be scripting issues, I think.
Well, at first I also thought that it kinda works, because I could control the menu in Clonk with my XBox 360 controller, even without Xidi. But it turned out that it was only because Steam was running in the background and was translating controller inputs to keyboard inputs like the arrow keys. It's called "Desktop Layout" in the Steam controller settings, and after I set that to the "Disabled" layout, the controller did nothing in Clonk without Xidi. Even playing was possible (to a degree) when selecting the "Keyboard 4" controls for my player, but more customizations would be necessary in Steam to make it work really.
Oh, in case anyone is interested, here's my current Xidi.ini
which maps the A button to "Up" on the left stick:
[Mapper]
Type = JumpOnA
[CustomMapper:JumpOnA]
Template = DigitalGamepad
ButtonA = Axis(Y, -)
No, it was definitely not Steam, and I was using the Gamepad 1 option in Clonk.
Which Windows version are you using?
This is on Windows 11.
I've added a checklist to the first comment, with a few more details what an implementer would have to look out for. Will extend with more details if necessary.
I just tried on Windows 10 and it works.
I don't have a Windows 10 PC ready to cross-check, but I guess they changed something in Windows 11 then. When I disable Xidi and close Steam, my controller does not interact with Clonk at all, neither in the main menu, nor in the controller configuration menu, nor in-game.
Still, the other more important points remain.
I just fully realized that you want to split Jump from Up for Clonk controls. That seems indeed very difficult, also due to scripts.
Oh, it's still there in Windows 11, but was not active by default for me. In the legacy control panel, there is an entry called "Set up USB game controllers", where you can "Select the device you want to use with older programs" after clicking "Advanced":
I just fully realized that you want to split Jump from Up for Clonk controls. That seems indeed very difficult, also due to scripts.
Ah. Well, yes that's what I feared.
I guess that separating the menu controls from the Clonk controls is the more important part from a usability standpoint, though. Having "Jump" and "Up" on the same key is occasionally annoying, but not being able to "Confirm" menu entries using "A" is a major problem in my opinion. That works against years of trained muscle memory.
I just fully realized that you want to split Jump from Up for Clonk controls. That seems indeed very difficult, also due to scripts.
Although, I thought about it more and it could actually work out decently for most packs. Jump is only the default action for Up. So it should be easily possible to inhibit that or trigger it separately.
I guess that separating the menu controls from the Clonk controls is the more important part from a usability standpoint, though. Having "Jump" and "Up" on the same key is occasionally annoying, but not being able to "Confirm" menu entries using "A" is a major problem in my opinion. That works against years of trained muscle memory.
Would it help to map Throw to two buttons? The one you use for controlling the clonk and also “A”. This is already possible with Extra.c4g/KeyConfig.txt:
[Keys]
Joy1Btn4=button1,button2
1 is for Gamepad 1 and 4 should be Throw (these + 1). button1 and button2 are parsed like this, which should be the same as displayed in the gamepad options.
You can also map to all the other controls found here.
Would it help to map Throw to two buttons? The one you use for controlling the clonk and also “A”.
Not really, because the conventional mapping for button A in most games is "Jump". If I understand you correctly, this would not be possible with this suggestion, right? The minimal solution to my "problem" would be to map A to Thrown only while controlling a menu, and not a Clonk or other object.
Oh, right. What about Dig? Do you also have already a mapping for “B”?
I have "Dig" mapped on B, which is fine because "Dig" also means "Cancel" in menus, and it is also the upper right key, just as in the default Clonk keyboard controls (for the same reason, I have "Throw" on X).
I mean, there are probably people out there who would map "Dig" to a different key but keep "Cancel" on B. So that option would be nice to have, but not as important as the other issue with A and Jump/Confirm.
Okay, progress report: I've got a crude proof of concept working, that just changes the existing C4GamePad
class to call the Xinput API instead of the WinMM joystick API. But this is actually worse than the previous workaround using Xidi, because Xidi allows you to map two buttons to the same action (e.g. having "Up/Jump" on both "A" and "Left stick up").
In order to make this viable, we would need a different input configuration mode, where you can select the controller buttons and then assign them actions. Possibly two different actions for "Menu Mode" and "Game Mode".
I guess a better approach is to first tackle the problem of having possibly different buttons for menu control and clonk/game control.
Okay, here are some possible avenues of attack. Writing them up, just to help myself think about which one might be feasible.
C4Menu::ConvertCom
converts normal commands like "Dig" to menu commands like "Close".
player
argument, so that it can look up the player's controls from the config, and adjust the conversion process accordingly.C4ConfigGamepad
would have to be expanded to store the Command-to-MenuCommand mappings, unless menu control is to be hardcoded for Xinput controllers to the conventional standard A=confirm, B=cancel.
C4Menu::DrawElement
would have to use the same mapping as C4Menu::ConvertCom
in reverse to pass the correct key to DrawCommandKey
.C4Game::InitKeyboard
the input keys for all 12 (C4MaxKey
) possible commands for each input method are registered.
C4MaxKey
to add additional key definitions like CON_MenuEnter
seems prone to unexpected bugs because the value of C4MaxKey
is used in multiple places, and is sometimes implicitly assumed to be 12.C4Game::LocalPlayerControl
would still somehow know to avoid calling ConvertCom
on the COM_Up
command, so the same mapping logic from option 1 would still be needed in addition to this.C4Game::InitKeyboard
, keys are registered with a scope in which they function, like KEYSCOPE_Control
for in-game player inputs.
KEYSCOPE_ControlMenu
to register the same gamepad button twice with these two different scopes, and then filter which one to use based on whether the player has a menu open.C4KeyboardInput::DoInput
where this information is not available, and the scope is not passed down to the event handlers.So, I've got a basic prototype of option 1, including the forward and reverse mapping.
However, increasing C4MaxKey
by at least 3 (for the 3 menu actions) is probably inevitable, because otherwise it is very messy to map menu actions to a button which is not one of the 12 default buttons. Because only those generate player control commands that can then be converted by C4Menu::ConvertCom
, so additional buttons would have to be registered separately in C4Game::InitKeyboard
. And in C4Game::LocalControlKey
we would need separate CON_Menu*
constants to idenfiy these keys.
Suddenly, Option 2 (registering buttons twice and throwing away the normal action while in a menu) looks like it might actually be the cleaner option.
Also, none of this currently handles the case where two buttons can trigger the same action like having both "A" and "Left Stick Up" for "Jump".
Sooo.... I've got a working prototype that does everything I need, but likely still has some undiscovered bugs. There's a PR at #118 in case someone wants to have a look. Here's how it looks right now:
Object controls:
Menu controls:
Note: Swimming with a joystick feels weird, because the swim direction depends on the time for which a direction key is held, which makes sense for keyboard controls and allows for fine grained direction control with fast key taps.
With a joystick however, you'd expect the swim direction to instantly reflect the direction you move the stick. Even if it was quantized to 8 directions it would feel more natural than the current behavior, where you have to quickly yank the stick around to get fine grained direction control. But I'm not sure how easy this would be to change.
Also, stick inputs should not be processed per-axis, but as a pair of axes, by interpreting the angle and deflection of the sick. The current implementation leads to a square activation window for the stick directions, which makes diagonal controls more difficult in my opinion. Also, some form of hysteresis would probably be nice to avoid accidental "double activations" when a stick is close to a diagonal direction and the value jiggles around the threshold.
The current gamepad user experience is somewhat suboptimal, at least for players who played any recent platformer with a gamepad.
Alternative: Make "Jump" the same as "Menu Confirm" for gamepad controls, if the above is too complicated.Firstly, Clonk does not support XInput, so no support for "modern" controllers like the various XBox types. This can be fixed using Xidi. This works, but you see no actual button names in the UI, only button numbers (e.g. on the action indicators at the bottom right of the screen). Actual XInput support would be nice, and it "should not be that hard" (haha). Just call
XInputGetState
instead ofjoyGetPosEx
(but of course the devil is in the details).But second, and more importantly: Nearly all modern games follow the same conventions for gamepad control for good reasons:
But in Clonk, this setup is currently not possible. You could configure the A button as the "Up" key in Clonk, allowing you to jump with A. But "Confirm" in menus is always the same as "Throw", so you can't have "Confirm" and "Jump" both on A. Also, you would have to press A to climb up or navigate upwards in menus. You can use Xidi to map both A and Up to the clonk "Up" key to fix that issue and still have Jump on A, but then you still have the problem that you can get accidental jumps when you walk around, by pressing the analog stick slightly in the wrong direction.
The 3rd point is not so much of a problem, because the "Cancel" button in menus is the same as "Dig". Mapping that to the B button works fine, although it would still be nice to have the option to configure "Dig" independently of "Cancel".
The ideal solution in my opinion would be to have "Up" and "Jump" (and possibly even "Menu Cancel") as separately configurable inputs (which could be configured to the same input of course). A quick look at the code tells me that this is probably not so easy, because some code depends on the notion that "Up" is the same as "Jump", for example
ObjectComUp
directly callsPlayerObjectCommand(cObj->Owner, C4CMD_Jump)
. I expect there to be more such places, but I didn't look further than that, so I might be mistaken. Some problematic cases might even be in some c4script code somewhere...If someone changed the code anyways, it might also be desirable to configure "Menu Cancel" and "Dig" as separate keys.