sezero / quakespasm

QuakeSpasm -- A modern, cross-platform Quake game engine based on FitzQuake.
https://sourceforge.net/projects/quakespasm/
GNU General Public License v2.0
244 stars 97 forks source link

Fix joystick movement thresholds and run behavior #51

Closed Macil closed 1 year ago

Macil commented 1 year ago

I've been playing a lot of both Quakespasm(/vkQuake) and the Kex port with a controller. Compared to Kex, I've noticed that fine movement control seems harder, and the always-run setting and the run key barely impact your speed when the joystick is held all the way. I decided to investigate the joystick handling code, and I fixed a few smoking gun issues that were holding things back.

  1. Fixed the issue where holding the movement joystick at a 45 degree angle would make you move at a different angle instead (41 degrees if you have always-run set to "Vanilla", otherwise 60 degrees!). (Yes, this is the same as what happens if you hold the forward and left/right keyboard keys, but it's a bit disorienting when using a joystick where you're supposed to be able to feel the angle and have finer control over it.) Now you move at the same angle as the joystick, just like with the Kex port and most games.
  2. Fixed the issue where holding the movement joystick all the way while not running would make you move at speed 283 instead of 200. This made it very hard to notice the difference between running and walking with a controller. This fix seems to also be consistent with the Kex port.
  3. Fixed the issue where when running you reach the maximum speed while holding the joystick at only 83% (when joy_exponent_move is at its default value of 3, and when joy_exponent_move is 2, 75%, and when joy_exponent_move is 1, 57%). The joystick maxing out so early meant you had a much smaller than necessary window where you could actually adjust your speed.

The above two issues were partly caused by the IN_ApplyMoveEasing function. The function had this comment:

same as IN_ApplyEasing, but scales the output by sqrt(2). this gives diagonal stick inputs coordinates of (+/-1,+/-1). forward/back/left/right will return +/- 1.41; this shouldn't be a problem because you can pull back on the stick to go slower (and the final speed is clamped by sv_maxspeed).

There's two things wrong with this: 1) forward/back/left/right going to 1.41 means that the walking speed will be too high, and the running speed will be clipped creating a large outer deadzone. 2) There's no need to scale things up to make diagonals output (+/-1,+/-1) instead of (+/-0.707,+/-0.707), because the latter already has magnitude 1 and anything with a magnitude higher than that will get scaled down.

  1. Added joy_outerthreshold{look,move} CVARs which create an outer deadzone on the joysticks. Helps the player reach the max speed even if their joystick isn't able to reach its max, and it can be turned up if someone preferred the past behavior. Defaults to 0.02 (2%).

(I picked 2% because 1% and 2% are values I've seen recommended, I wanted to copy the value from a modern popular fast-paced FPS with polished controller support, and 2% is the default value in Apex Legends which happens to have an unusually detailed controller settings menu I was able to learn from.)

  1. Split the joy_deadzone CVAR into two joydeadzone{look,move} CVARs. Mostly done just to be a little more parallel with the joy_outerthreshold{look,move} CVARs, and because the Kex port lets you set the joysticks' deadzones separately.
  2. Changed the default values for joy_exponent and joy_exponent_move from 3 to 2. This part of the PR is kind of just an opinion but I think it's a good one. It feels better to me, it's closer to what the Kex port does by default, and it seems like 2 is a much more commonly recommended value (this specific post by nivix (not OP) was useful to me for learning about modern joystick controls; I'm tempted to implement joystick acceleration/rampup as described there sometime to try to get closer to Kex parity). The old value 3 is maybe a little better for fine slow aiming, but it causes an awkward cliff in the middle of the joystick response curve, making it harder to do things like continuously turning the right amount to keep facing an enemy that you're strafing around.

(I felt the Kex port's joystick response curve was pretty good, so I studied its config, took videos to measure the time it takes to move a given distance or turn around at various joystick values, and charted the results against different Quakespasm joy_exponent settings: moving, turning. I still don't fully understand Kex's response curves, but it's clear to me that joy_exponent{,_move}=2 is much closer to what it does than joy_exponent{,_move}=3.)

sezero commented 1 year ago

@ericwa? (cortroller stuff was your baby..)

Macil commented 1 year ago

I made one last touch to this PR: I've tweaked the final commit which changed joy_exponent (3 to 2) so that it also changes the defaults for joy_sensitivity_yaw (300 to 240) and joy_sensitivity_pitch (150 to 130). These values give max turning speeds that are the same as Kex's defaults, and decreasing these values helps to compensate for the decrease in joy_exponent (which causes mid-range inputs to be scaled up). This config is the one I use all the time in QS/vkQuake.

I didn't originally intend to have much opinionated defaults-changing stuff in this PR, but the joy_exponent_move change (3 to 2) seemed natural to pair with the movement range fixes, the joy_exponent change (3 to 2) seemed natural to pair with that, and after testing out various settings it feels best to me that this change to the sensitivity would be paired with the change to joy_exponent. The final commit isn't exactly a bugfix but I hope it fits well in a PR that's about tuning things to feel better.

sezero commented 1 year ago

This patch is in. Thank you.

ericwa commented 1 year ago

Thanks, these seem good to me. I'll add the new cvars to the controller section of the documentation.

Agreed, regarding the +1/+1 move inputs, it makes more sense to prioritize intuitive controls / matching what other games do, rather than emulating keyboard input as I was doing initially.