godotengine / godot-docs

Godot Engine official documentation
https://docs.godotengine.org
Other
3.64k stars 3k forks source link

Docs on new method to get Input is factually wrong and misleading #5378

Open golddotasksquestions opened 2 years ago

golddotasksquestions commented 2 years ago

Your Godot version: 3.4, 3.3.4

Issue description:

https://docs.godotengine.org/en/stable/tutorials/inputs/controllers_gamepads_joysticks.html#which-input-singleton-method-should-i-use

There are so many things that are factually not right here and will lead Godot users and players of their games into worse development and gameplay experience.

There are 3 ways to get input in an analog-aware way

No there are not just 3. There are many more, with different usecases each (what kind of deadzone you want, how much deadzone you want, if you want deadzone at all ...

When you have two axes (such as joystick or WASD movement) and want both axes to behave as a single input, use Input.get_vector():

This is really bad advice because it will lead to very inconsistent behaviour. with WASD the user will be able to walk in a straight direction, but when they use analog stick they suddenly can't walk straight any more.

This is because unlike Input.get_action_strength() and Input.get_axis(), Input.get_vector does not have a deadzone along both axis. Input.get_vector() only has a central deadzone.

Most games however will require the player to move straight more often than not. Also intuitively, the Player will "just want to go forward", not drift left or right when they hold their analog stick straight forward.

Take as an example countless 3D thrid person games that ask you to walk over a beam or narrow bridge. If the Godot user develops their game with Input.get_vector(), these passages will be incredibly frustrating. And overall the controlls will feel less tight because they can't "just move left" or "just move right" anymore without also driving slightly somewhere else.

In stealth games when you would try to gradually and slowly accelerate your character, it becomes impossible to do so in a straight direct using Input.get_vector().

There are countless more examples, which is why most games use an axis deadzone besides the central deadzone.

Godot now allows you to now more easily disable the axis deadzones, which was already possible, but is now more convenient. That's great! However only having the central radial deadzone by no mean ideal for most games or usecases. The opposite is the case!

The current Documentation is for some reason dismissive of the of way to get analog Input, without mentioning how Input.get_axis() can is now a much shorter, much more convenient way to get the same result:

var velocity = Vector2(Input.get_axis("left", "right"), Input.get_axis("up", "down"))

This is the same as

var velocity = Vector2(Input.get_action_strength("move_right") - Input.get_action_strength("move_left"),
            Input.get_action_strength("move_back") - Input.get_action_strength("move_forward"))

This means you also get the axis deadzones with this approach.

Input.get_vector() should have a warning that you will get driving issues and it should only be used in cases when the player would not ever want to get a straight Input direction. (honestly I have a hard time to even think of practical usecase examples).

This handles deadzone in a correct way for most use cases.

The line below is similar to get_vector(), except that it handles the deadzone in a less optimal way.

These statements are definitively and factually wrong and very misleading. I can already see countless people confused and frustrated in the community channels why their character won't move straight. People who do community support will have to answer the same thing again and again. Let's please fix this as soon as possible.

URL to the documentation page: https://docs.godotengine.org/en/stable/tutorials/inputs/controllers_gamepads_joysticks.html#which-input-singleton-method-should-i-use

aaronfranke commented 2 years ago

If you want to avoid lateral motion when moving "forward only", then var velocity = Vector2(Input.get_axis("left", "right"), Input.get_axis("up", "down")) works for that. It doesn't make this documentation "factually wrong" in any way. As @groud put it, a cross-shaped partial deadzone is "far less commonly desired behavior".

See the discussions in https://github.com/godotengine/godot/pull/42976 and https://github.com/godotengine/godot-docs/pull/4651

golddotasksquestions commented 2 years ago

Calling a Input method without axis deadzone "better" is factually wrong. It's not better it's worse in pretty much all game development usecases.

var velocity = Vector2(Input.get_axis("left", "right"), Input.get_axis("up", "down"))

Needs to be advertised as the "default" way to get Input if we want intuitive Input and less issues with Godot users and Players of Godot games.

The new Input.get_vector is a great addition, and certainly useful for specific cases, but it a very bad idea for general use.

If for no other reason than for the fact that is has and a completely different behaviour when WASD or D-pad is used compared to when analog sticks are used.

var velocity = Vector2(Input.get_axis("left", "right"), Input.get_axis("up", "down")) is as consistent as possible while also giving the user control along the axis and allows to move freely.

If you want to move straight forward, with this, you can!

You can't move straight forward with Input.get_vector(), you simply can't!

Personally I would prefer if the Godot user has the option to set, enable and disable axis deadzone and likewise set the shape of the central deadzone and enable default clamping. But I can't code C++.

What I can do is telling you that this documentation is misleading and needlessly will cause a problems.

wareya commented 2 years ago

As @groud put it, a cross-shaped partial deadzone is "far less commonly desired behavior".

Joystick noise does not apply to the center of the joystick's position. It applies to the center of each axis. If you do not have axial deadzones at all then you are going to be reading noise on the x axis whenever the player is pointing straight up or down. It's just stupid. If you want to make an input system that can angle joysticks in any direction, you don't want to get rid of the axial deadzone, you want to use an axial deadzone and then also do deadzone compensation.

wareya commented 2 years ago

Here's an image for demonstration. Excuse my language. I have a coordination disorder and get irrationally angry when people grandstand about bad, stupid control-related things like radial deadzones being good even though they're actually bad and make me subconsciously do things with my hands that cause physical pain.

image

Calinou commented 2 years ago

Does this mean we need two separate methods in Input (one that returns a radial deadzone, one that returns a cross deadzone)? It seems there's no "one size fits all" solution here.

aaronfranke commented 2 years ago

@Calinou No, because var velocity = Vector2(Input.get_axis("left", "right"), Input.get_axis("up", "down")) already has the behavior of a cross-shaped partial deadzone, there isn't much of a reason to add another method for this.

The OP's concern is that the documentation should be updated to include this information, but I'm not sure if the cross-shaped deadzone behavior is actually preferable. When I made my PR that added Input.get_vector I originally had it as just a simple wrapper with the cross-shaped deadzone behavior, but then we changed it to the current behavior because we decided it was better to have a circular deadzone (see groud's comment here).

aaronfranke commented 2 years ago

@groud Can you explain why the behavior you suggested of a circular deadzone is better?

groud commented 2 years ago

I can't think of a single game where you would want this kind of "snapping" behavior on the 4 coordinates directions. I guess in somes situations where the camera is fixed it could make sense, but otherwise, circular shaped deadzone always seem to be preferred over other ones.

Calinou commented 2 years ago

I can't think of a single game where you would want this kind of "snapping" behavior on the 4 coordinates directions. I guess in somes situations where the camera is fixed it could make sense, but otherwise, circular shaped deadzone always seem to be preferred over other ones.

You could make the analog input snap to orthogonal corners using a "virtual notch" system (basically emulating what GameCube controllers did at a hardware level), but this can be done in your own game code.

wareya commented 2 years ago

I can't think of a single game where you would want this kind of "snapping" behavior on the 4 coordinates directions.

Because all controller analog sticks are noisy (reminder: this is why deadzones exist), this is desirable in virtually any game that has walking and a camera, even with a non-fixed camera. You really do not want to be unable to walk in a straight line. It's tedious and frustrating.

There is a problem with simple axial deadzones in that they make it so that there's a range of nearly-straight angles you can't access. This is fixed by adding deadzone compensation, not by switching to a radial deadzone.

If the deadzone feels like snapping instead of like "oh, I can walk in a straight line, cool", that means your deadzone is too big. Also, deadzones always need to be configurable by the actual player of the game, because their purpose is to eliminate axis-center noise, which is different from controller to controller (and gets worse as controllers age).

This issue is not so difficult to think through that you can really come to the wrong conclusion so easily after considering all of the above facts. Please actually think through what is being done to the axis data here and why it's being done, not just what it looks like on a chart.

wareya commented 2 years ago

@Calinou No, because var velocity = Vector2(Input.get_axis("left", "right"), Input.get_axis("up", "down")) already has the behavior of a cross-shaped partial deadzone, there isn't much of a reason to add another method for this.

This doesn't handle radial mapping.

You could make the analog input snap to orthogonal corners using a "virtual notch" system (basically emulating what GameCube controllers did at a hardware level), but this can be done in your own game code.

Deadzones can be done in game code in general. Should they? Probably not.

groud commented 2 years ago

I mean, you make it look like 1) controllers are so noisy you would see the difference between a snapped version and a not-snapped one and 2) players need to go in a perfectly straight line as they would not notice anyway.

I am not convinced that any of these happen in real life situation.

But aside from what I think, several users complained about the fact we did not have a circular deadzone. That's why we made it the default.

But if you think that's wrong, feel free to open a proposal and get feedback on it.

wareya commented 2 years ago

I am not convinced that any of these happen in real life situation.

Uh huh, sure.

1) controllers are so noisy you would see the difference between a snapped version and a not-snapped one

https://user-images.githubusercontent.com/585488/141206522-a457c303-5ac0-4b24-8746-b43dce61eeb7.mp4

This is a Dualshock 4.

2) players need to go in a perfectly straight line as they would not notice anyway.

Literally any open world game.

aaronfranke commented 2 years ago

several users complained about the fact we did not have a circular deadzone

Which users? I don't remember this conversation.

wareya commented 2 years ago

several users complained about the fact we did not have a circular deadzone

Which users? I don't remember this conversation.

Also: Were they actually complaining about not having a circular deadzone, or was it the fact that if you use a normal deadzone you lose access to certain angles? The "inaccessible angles" problem can be fixed without using a circular deadzone.

groud commented 2 years ago

Uh huh, sure.

Well, you video proves nothing. My point was that it would not be noticeable in most games. If your goal is to move in a straight line, the behavior shown there would be perfectly fine.

Which users? I don't remember this conversation.

There was a reddit thread about it IIRC, but cross-shaped deadzones are usually considered as an issue from what I read over the internet.

But well, I won't bikeshed forever about it. Make a proposal, link it on social networks and we'll see which solution users prefer as a default one.

wareya commented 2 years ago

My point was that it would not be noticeable in most games. If your goal is to move in a straight line, the behavior shown there would be perfectly fine.

This is simply not true in practice and seeing you assert such makes me think that you haven't played many over-the-shoulder or first-person 3d games with broken deadzone handling. This is a real, experienced problem, not theoretical.

groud commented 2 years ago

This is simply not true in practice and seeing you assert such makes me think that you haven't played many over-the-shoulder or first-person 3d games with broken deadzone handling. This is a real, experienced problem, not theoretical.

If you say so... Seems the other way around though... no one seem to say the cross-shaped deadzone is the best one:

wareya commented 2 years ago

If you say so... Seems the other way around though...

Here is a game that uses Input.get_vector(): https://github.com/wareya/StillWaiting

Try to walk in a straight line. Unless your controller itself has an aggressive baked-in deadzone (some do), you can't. This is because the camera automatically turns whenever you're not walking in a straight line, which is something that most over-the-shoulder games do.

Seems the other way around though... no one seem to say the cross-shaped deadzone is the best one:

All of these links are discussing overly large cross-shaped deadzones that don't have deadzone compensation.

Dark souls 2 mod

Dark Souls 2 has "deadzone memory". It does not have a normal cross-shaped deadzone.

UE4 Marketplace thing

This person is shilling something they made that costs money.

https://www.gamedeveloper.com/disciplines/doing-thumbstick-dead-zones-right

If you actually read it instead of just picking it up off of google, this article specifically explains that they encourage the use of both techniques simultaneously, depending on the genre. In particular, Twin Stick Shooters, what they're talking about the most, have fixed cameras and third-person perspectives, which minimizes the impact of being unable to point the analog stick in a straight line.

https://github.com/Minimuino/thumbstick-deadzones

This is just a personal essay advocating for another essay/article from somewhere else. This reminds me of the hundreds of personal essays similarly advocating blindly for "Fix Your Timestep", despite the fact that "Fix Your Timestep" is incomplete and broken on its own (you must always have a way for the simulation to fall back to delta time or slowdown, or else you end up with a performance death spiral like OpenMW used to have). This is basically Cargo Cult Best Practices.

https://forums.unrealengine.com/t/thumbstick-dead-zones/45050

They are specifically complaining about the snapping that a simple axial deadzone has (the fact that you can't access certain angles). Using deadzone compensation fixes this.

https://www.reddit.com/r/Rainbow6/comments/74qn8g/gamepad_advanced_options_and_deadzones/

which can makes aim with an analogue stick feel particularly clunky and is similar to angle snapping with a mouse on pc.

This person is again complaining about some angles being inaccessible which can be fixed without making it impossible to walk in straight lines.

BTW, they cite the same article that the github essay cites, which really makes me think this is another case of Cargo Cult Best Practices again (like Fix Your Timestep was).

golddotasksquestions commented 2 years ago

@groud

I really don't understand you.

The new Input.get_vector is great! Cicular deadzones are great! That's all out of discussion.

The point of my issue here and what @wareya is saying, is that it makes no sense to recommend a method that only has circular deazone but no axial deadzone as the general "better" way to handle analog Input.

This only will lead to Godot games with bad Controls.

Please do me the favour and try both these methods for your self. Please make a couple of actual character controllers in a settings when the player needs to walk straight or sideways along any of the axis, when the player needs to aim straight that need to feel "tight" and compare. Make a top down pixelart 2D character controller and try to walk straight up down or left. Make a 3D platformer character controller and try to have your character walk straight forward over a beam or fallen tree trunk. Make an FPS controller with upwards gun recoil and try to keep aim.

The need to be able to have the analog input snap to the axis is so universal, there hardly is any example where you would NOT want this to happen. The player will expect they can use the analog stick to incrementally go straight up down or left, without drifting sideways.

Please. Please try it!

Even when aiming in like a twin stick or FPS shooter game players will want there to be at least a tiny bit of snap to the axis. If you can give us example when an Input method with ONLY a circular deadzone would be favourble, I would really love to hear it. Maybe a drawing app you control with analog sticks? I can only think of situational or really rare special usecases. 99.9% of all games I see make in Godot need the axis deadzone.

@Calinou Yes it would be preferable to be able to define center and axis deadzones seperately within the Input class and turn them on and off at will anytime during runtime. But this is not what this issue here is about. This issue is about the current documentation page and how it's current wording is not helping Godot and it's users, but actually the opposite: These recommendations are factually wrong and will lead to Godot games that feel worse than they need to, just because the Godot user did what was recommended in the docs (who would blame them?)

madmiraal commented 2 years ago

I created a proposal about creating configurable axis dead zones before coming across and reading this issue: https://github.com/godotengine/godot-proposals/issues/3709

Please feel free to add your suggestions to what needs to be done there.

moonraymurray commented 1 year ago

I am having this issue, I have WASD and a PS4 Controller, left stick axis, controlling the same inputs, and because there is no deadzone cut off, but only deadzone to activate and make true, using WASD with even a PS4 controller plugged in leads too movement stopping and starting non stop, until the PS4 Axis is moved, then for some bizarre reason now the issue is gone, and there is no way to change this using GDscript.

Calinou commented 1 year ago

I am having this issue, I have WASD and a PS4 Controller, left stick axis, controlling the same inputs, and because there is no deadzone cut off, but only deadzone to activate and make true, using WASD with even a PS4 controller plugged in leads too movement stopping and starting non stop, until the PS4 Axis is moved, then for some bizarre reason now the issue is gone, and there is no way to change this using GDscript.

This is an unrelated issue: https://github.com/godotengine/godot/issues/45628

akien-mga commented 1 year ago

Reopening as the upstream change was reverted due to regressions: https://github.com/godotengine/godot/pull/73608

aaronfranke commented 1 year ago

Originally, I had Input.get_vector() using Input.get_action_strength(). This was suggested to be changed to Input.get_action_raw_strength(), which results in undesired behavior as this issue notes.

Then https://github.com/godotengine/godot/pull/69028 changed Input.get_vector() back to Input.get_action_strength(), which I think was the right move.

Then a user reported issues with Input.get_action_strength(), so the "fix" in https://github.com/godotengine/godot/pull/73608 was to change Input.get_vector() back to Input.get_action_raw_strength(). This makes sense as a hotfix for 4.0, but it's really not the right fix, because even after changing Input.get_vector(), the bugs the user reported about Input.get_action_strength() are still relevant and need fixing.

If we have two methods, I would prefer the raw one be called Input.get_raw_vector() to mimic Input.get_action_raw_strength(). But I suspect we don't need two, because the only reason we aren't using Input.get_action_strength() in Input.get_vector() is because of a bug with Input.get_action_strength().

diegopau commented 1 year ago

I just did the implementation for the camera movement using the gamepad right stick for my Godot 4 3D game.

The documentation led me to try first with get_vector() but as @golddotasksquestions explained, this makes it very hard to snap to specific directiions which is what you usually want: move camera to the right and having it go only to the right, not 100% to the right and 3% up or down (as an example). Using get_axis() solves this since it treats the deadzone for each axis separated. I would argue that for moving the character (forward, backward, left, right) with the left analog stick it might also be better to use get_axis()

In this image I try to make it easier to visualize the advantage of using get_axis() axis

This would affect most of all to this bit of the documentation: When you have two axes (such as joystick or WASD movement) and want both axes to behave as a single input, use Input.get_vector()

aaronfranke commented 11 months ago

For some more data, here are the deadzone behaviors in various popular games: https://imgur.com/a/xSfcP

Most games have a cross-shaped partial deadzone to allow for direct forward movement, but some of the games have no cross-shaped partial deadzone, only the center deadzone.