roadrunnertwice: Rodney the Second Grade T-Ball Jockey displays helpful infographics. (T-ball / Your Ass (Buttercup Festival))
[personal profile] roadrunnertwice

This is a gamedev post.

Don't do this when converting analog thumbstick input to discrete up/down/left/right (d-pad/arrow-key) values:

Axial (square) deadzone; an off-center diagonal vector remains in the Y deadzone for a while after leaving the X deadzone.

Do this instead:

Radial deadzone with angle snapping cutoffs. An off-center diagonal vector reads as the same angle for its whole length..

Traditionally, shmups only permit eight-directional movement, like with a d-pad or arrow keys or an arcade stick. But modern PCs and consoles commonly have gamepads with analog thumbsticks, and lots of people WILL use the left analog stick for movement. The d-pad is technically more precise, but it’s not always comfortable or reliable for everyone; for example, my bud Isaac can’t d-pad for controlling a character because it fucks up his hypermobile thumb joint (and the analog stick doesn’t).

So, you must support the thumbstick. With that established: how?

ZeroRanger just gives you true analog movement, and it feels pretty great — that’s how I originally played through and beat Despair and Primordial Fighter. So if you don’t have a reason not to, just set a reasonable radial deadzone (or maybe something slightly more sophisticated), perhaps do some scaling to make it feel snappy, and call it a day.

But maybe you want your movement resolution to always be discrete, for whatever reason. Maybe it simplifies serializing inputs for replays; maybe you want to dodge the question of how to combine fundamentally different precision regimes into a single online leaderboard; maybe you’re porting a game that never expected to have to cope with analog; maybe it’s just part of your aesthetic gestalt. That’s fine, but now you need to decide how to map the continuous axis values reported by the gamepad into discrete up-down-left-right signals (henceforth referred to as “cardinal on/offs”).

The first thing that will occur to you is to map an axial deadzone to cardinal on/offs: pick a value d. If x < -d, left = true; if x > +d, right = true. Repeat for y.

Do not do this.

I mean, you’d be in good company, several stellar games do this; off the top of my head, I think Blue Revolver: Double Action, Mushihimesama (PC port), and Touhou 10 Mountain of Faith are all in this car.

The problem is, if you play them with the analog stick, they feel slippy.

The slippiness happens here:

Axial (square) deadzone; an off-center diagonal vector remains in the Y deadzone for a while after leaving the X deadzone.

If you press your analog stick along the sorta-southeast pencil line, it will start registering as due east when you exit the square deadzone, but will switch to registering as southeast once you cross the dotted y deadzone line.

This sucks, because you never changed direction; you only changed your magnitude! In practice, this means a lot of inputs that start wrong or "slip off," and some frustrating deaths.

Anyway, what you should do instead is this:

  • Turn the x and y axis values into a 2D vector.
  • Choose a deadzone radius. If the vector’s length is shorter than that, the input is zero and you skip the rest of this. (Actually, compare len_squared < d * d bc it skips calculating a square root. Same diff tho.)
  • Find the direction of the vector, and snap it to the nearest cardinal or ordinal direction (E, NE, N, etc.).
    • I did this by setting a bunch of constants in radians for the half-ordinal directions like north-northeast, ensuring my angle of input is normalized to the range [-𝛑, 𝛑], and comparing the angle to those constants in increasing value. Start with WSW, which is a bit more than -𝛑: if θ < WSW, it’s west; else if θ < SSW it’s southwest; eventually if θ >= WNW it’s west again.
    • But there are other ways to do it.
  • Convert the eight-directional result to the cardinal on/offs you’d get from the arrow keys. (E = right, NE = right + up, etc.)

The resulting map of the input space looks more like a torus cut into evenly-sized wedges:

Radial deadzone with angle snapping cutoffs. An off-center diagonal vector reads as the same angle for its whole length..

You know... bundt cake.

Once you start pushing in a direction, whatever it resolves to, you stay in that direction regardless of magnitude.

If you happen to have Ketsui: Deathtiny for the PS4, try playing for a bit with the analog stick. (And if you don’t have it, strong recommend.) It honestly feels great; no hint of slippiness. It’s as good as ZeroRanger, even though the game is locked to eight directions. You might want faster response than the throw of the analog stick can yield, but the accuracy is fine. I am completely certain that they’re doing something functionally equivalent to what I just described, resulting in an evenly sliced torus.

So, you should do that too.