Last time we found that although we could stop the wavering of our character when we released our controls, we also introduced a lot of weird feeling Kinaesthetic Artifacts as a result of deadzoning our axes independent of one another.
These “square” deadzones do have their uses (in console FPS games where players mean to sweep their aim perfectly across the horizon, but, when using a “purer” signal, would actually find their aim wavering up and down as they tried. This is one of the subtler forms of aim assistance going on in most console FPS games*) but nothing about the visibly circular restrictive outer limit of the XBox controller screams “make me stick to cardinal directions!”.
It stands to reason that a circular input wants to result in a circular output: the affordances of the interface should be matched by the expression space inside the game itself. In our example, square dead zones didn’t work in our favour, so let’s try Circular Dead Zones instead.
Now we have a deadzone which doesn’t clamp and deform the direction of the stick. We’re still rid of the “wandering” so notable when there is no DeadZone at all.
Compare and contrast the Square and the Circular deadzones. Note how, instead of a blank cross shape, we have a circular black shape in the center of the output. When we push in a direction, we get that direction in game, rather than it snapping to north, south, east, and west. Great!
Instead of independently DeadZoning the stick axes, we’re DeadZoning the Vector Magnitude – deflection regardless of direction. And as it turns out, the code is a lot more elegant and easy to read than deadzoning the axes separately (in Unity’s API, anyway).
float deadZoneMagnitude = 0.5f;//Yes, still humourously large for the purposes of explanation. //the ".normalized" version of a vector in unity gives that vector's direction, but not its length... in effect, the length is "1", in the direction the vector was already pointing. StickInput = StickInput.normalized * (StickInput.magnitude < deadZoneMagnitude ? 0.0f : StickInput.magnitude);//Set the stick magnitude to zero if it's less than the deadzone value. Otherwise, maintain direction and magnitude.
There’s a bit of a problem here, though: Notice how the Magnitude read-out sometimes exceeds 1.0. This is because a deflection reaching into the upper right hand corner (1,1) has a magnitude of root 2 ( Pythagorus! Sqrt(1^2 + 1^2) = 1.41421), which, if you believe in maths, is actually greater than 1.
We don’t get all the way to 1.41421: as mentioned in Part Zero, the stick is physically clamped, and won’t even give us the corner input, though it does give us irritating values which just exceed what we, as players, expect from the interface.
One speculates that this was a hard decision made in producing this interface en mass: it would probably be extortionately expensive to create something perfectly tuned, whilst maintaining any amount of durability.
The least worst compromise seems to have been made: you get a bit more throw in the corners than you expect, just not all the way up to Root 2. And that’s more or less enough to get 90% of the way to a proper representation of the input a player is making.
As users, we believe, due to the design of the controller housing, that we’ve been given the freedom of a 1 unit radius circle, and no more. We actually don’t want more than is implied. It just doesn’t intuitively make sense that the controller would read more than it’s implying it reads. That breaks the implied contract between us and the games we play at a fundamental level. It’s not by a lot, but we’re so subconsciously sensitive to these little irks that it does matter.
In our game’s example, I’m simply multiplying displacement per frame (speed) by the magnitude of the input (which is typical of any analogue based game). This means that at some angles, our character might actually exceed their top speed.
That may be desirable in some designs: it proved to be one of the component parts of strafe jumping in Quake 2 & 3)**, but in our current character controller example, it seems like an arbitrary and bothersome skill to ask of someone to aim their camera at 45 degrees to their heading to gain optimal speed.
There’s a couple of ways to “clamp” this magnitude, thus funneling the implied stick inputs back to to the range they’re supposed to be in. Both approaches have some small issues. You’d think it’d be straightforward, but this nuance deserves another article! Next time, I welcome you to peruse some of my proudest failures in kinaesthetics.
Previous Part | Next Part (Soon!)
*I’d also argue that, considering Part Zero‘s point, that we’re all still fairly inaccurate machines, ourselves, and lean heavily on the guidance of the hardware… so why not allow software to gently steer us in the right direction, too? Not to the point of playing the game for us, but for the sake of meeting our intent half way. More on that much further down the road.
**In the case of first person shooters, the diagonal speed boost makes some amount of sense from a pure dexterity proving point of view – building speed in this way implicitly requires your aiming skill to compensate, since you’ll rarely be moving toward what you’re aiming at if you desire the benefits of increase speed. And that’s fine and dandy on a keyboard where we can accurately read the 4 diagonals. With these haphazardly clamped inputs on controllers, however, we’re not kidding anyone: The benefit of the extra diagonal deflection is so minuscule (about 1mm of “extra” signal vs. the cardinals) that it doesn’t really feel like a fully honed, intentional mechanic could come out of it: all that’s left is an unexpected Kinaesthetic Artifact. As such, that’s the sort of thing that we probably want to clean up at the lowest level of our input engine architecture, lest it permeate unexpectedly into other systems which assume that input magnitude resides between 0 and 1.