Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Is this if statement necessary for movement?

Discussion in 'Scripting' started by KoonyGeez, Jun 13, 2023.

  1. KoonyGeez

    KoonyGeez

    Joined:
    Aug 17, 2019
    Posts:
    3
    I am new to unity and I have watched a tutorial for implementing movement. The code is as follows:

    Code (CSharp):
    1. void Update()
    2.     {
    3.         float axisX = Input.GetAxisRaw("Horizontal");
    4.         float axisZ = Input.GetAxisRaw("Vertical");
    5.      
    6.         Vector3 direction = new Vector3(axisX, 0f, axisZ).normalized;
    7.  
    8.  
    9.         if (direction.magnitude >= 0.1f)
    10.         {
    11.             character.Move(direction * speed * Time.deltaTime);
    12.         }  
    13.  
    14.     }
    I have also read the docs to understand what everything does so the implementation makes sense to me. However, I do not understand why the 'if' statement is used?

    As far as I understand,
    Vector3 direction = new Vector3(axisX, 0f, axisZ).normalized;
    means the magnitude of my 'direction' variable is always 1, since the documentation says 'When normalized, a vector keeps the same direction but its length is 1.0'.

    So if my 'direction' has a magnitude of 1, why is
    if (direction.magnitude >= 0.1f)
    used if the value is always going to be 1 on key press? Is it to ensure the key has been pressed? I removed the 'if' statement but the movement acted the same as with the 'if' statement.

    I don't understand why it is used and its not explained in the tutorial. Is it necessary?
     
  2. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Well I guess the trick is about handling the zero vector, which has a length of exactly 0 and is thus undefined.
    direction
    vector will be zero if all its components are 0, and in that case normalization won't be able to somehow define it.

    Normalization is really just dividing a vector by its length (leading to x^2+y^2+z^2=1), so now you can appreciate why it wouldn't work with a zero vector.

    That's not a very good code, to be honest. You raised a good issue with it, so it's not really self-explanatory. A good code should never prompt too many questions and/or let a programmer wonder why something is the way it is.

    To make this code much better, a proper comment would suffice. I wish tutorials were teaching that as well.
    Code (csharp):
    1. if (direction.magnitude >= 0.1f) { // helps discern between zero and non-zero vectors
    Or the self-evident style that I like better
    Code (csharp):
    1. if(nonZero(direction)) {
     
    Last edited: Jun 13, 2023
    Yoreki and KoonyGeez like this.
  3. KoonyGeez

    KoonyGeez

    Joined:
    Aug 17, 2019
    Posts:
    3
    That makes sense, thanks for the clarification. Regarding the comments - although the tutorial didn't have comments I put my own in but removed them for clarity for the thread. I guess I should keep them in then.
     
  4. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Where nonZero would be
    Code (csharp):
    1. static bool nonZero(Vector3 v) => v != default(Vector3); // or Vector3.zero
    A rare opportunity to actually match a floating-point vector using
    ==
    operator (edit: in fact it's
    !=
    in this case). You can safely do this only with a true 0 or true 1.

    But apart from that this is also much faster than doing
    magnitude
    which needlessly takes a square root to prove that something is not 0.
     
    Yoreki and KoonyGeez like this.
  5. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    And in fact, the worst thing about this
    Code (csharp):
    1. if (direction.magnitude >= 0.1f) {
    is that
    =
    that's such a misleading subtle detail that makes you wonder for ages, as if 0.1 is somehow important by itself. Urgh, horrible! :)
     
  6. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,718
    I would say that the code would make a lot more sense if the normalization operation were inside the if statement. We don't want to try to normalize the zero vector because that's undefined behavior. I'd rewrite it this way:
    Code (CSharp):
    1. void Update()
    2. {
    3.         float axisX = Input.GetAxisRaw("Horizontal");
    4.         float axisZ = Input.GetAxisRaw("Vertical");
    5.         Vector3 direction = new Vector3(axisX, 0f, axisZ);
    6.  
    7.         // Avoid doing the undefined normalization operation as well as the move if the input data is 0.
    8.         // compare with sqrMagnitude for better performance
    9.         if (direction.sqrMagnitude > 0.01f)
    10.         {
    11.             direction.Normalize(); // normalize the vector in place
    12.             character.Move(direction * speed * Time.deltaTime);
    13.         }
    14. }
    Now we avoid both the invalid normalization operation and the move operation if the input is small enough, saving us from some CPU overhead as well as the mathematical embarrassment of trying to normalize the 0 vector.
     
    KoonyGeez and orionsyndrome like this.
  7. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    I agree, but again, the vector has perfect zeroes, even sqrMagnitude is too much, when one can do
    Code (csharp):
    1. if(direction.x > 0f && direction.y > 0f && direction.z > 0f) {
    But I really don't like the sight of this, so
    Code (csharp):
    1. if(nonZero(direction)) {
    would be the best solution. Although I'm just nitpicking at this point.

    Edit:
    It appears that my nitpicking would've saved me from that malformed condition.
    Did anyone else notice it? I will leave it as an example of why this should be avoided.

    There are in fact 2 huge errors! lol

    How a senior dev makes such mistakes? Well the more trivial something is, the more ass it bites, that's the lesson here.

    A proper way to construct this is not to think about, but to stick to the principles.

    1) Write down the basic rule
    Code (csharp):
    1. direction.x == 0f && direction.y == 0f && direction.z == 0f
    2) Negate
    Code (csharp):
    1. !(direction.x == 0f && direction.y == 0f && direction.z == 0f)
    3) Demorganize
    Code (csharp):
    1. direction.x != 0f || direction.y != 0f || direction.z != 0f
    Idiot.
    At least my hunch was right. I would never actually attempt to write this.
    And that's another reason to write nonZero function instead.
     
    Last edited: Jun 14, 2023
    KoonyGeez and PraetorBlue like this.
  8. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,588
    While there is a lot of very good points and performance considerations here, im a bit confused that nobody at least mentioned that you may also simply remove the if statement in the code shown above. Worst case you would call Move() on the zero vector, which results in no movement. Other than wasting a negligible amount of performance.
     
    Chubzdoomer and orionsyndrome like this.
  9. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Well, in my defense, I though it was obvious. We all kind of assumed that he really wanted to know what's the point of it. It's truly pointless no matter how you look at it, but we all got carried away. Two more paragraphs, and there we go, discussing Shannon entropy and heat death of the Universe.

    I agree with you 101%. I need this opinion to overtake my prior opinions in effective priority.
     
  10. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,588
    I think it would have taken less than two additional posts to convince me i was missing something instead :p

    Jokes aside, as i read it OP was asking about why the if-statement was there in the first place. The thread offered reasons for it (and a lot of generally good advice) but never really adressed whether it had to be there. So just in case.. i added the party pooper answer.
     
    KoonyGeez and orionsyndrome like this.
  11. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,495
    Well, it's true that the if statement is kinda unnecessary in some cases, but may be "desired" in others. It's essentially a dead zone for the inputs. This may be relevant when you use a gamepad or joystick as input which may not ever settle on exact 0. So in neutral position it may report a slight non-zero value so your player would slowly drift. By defining a dead zone we can ensure that the player actually stays still at miniscule inputs.

    Most games or emulators which support gamepads usually offer a way to define a dead zone in the settings. So that 0.1 may even be a setting. Just as an example, I still have my old PS1 controller (dualshock) plugged into my PC through a PS1 to USB adapter. It usually has a slight jitter around 0 (infact I remember interfacing the PS1 controller manually through IO pins in the past and the analog sticks actually report values between 0 and 255 and 128 was neutral. Though it could always jitter about 1 or 2 units off).

    So in most cases it's not necessary since when using keyboard inputs, when no key is pressed the value would actually be 0. Though analog input devices can introduce some rest-position-jitter and this if statement would filter it out.
     
    KoonyGeez, Yoreki and orionsyndrome like this.
  12. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    10,977
    We're getting really off-topic, but I think deadzones should be handled before this. You should have a script that processes the input (with both inner and outer deadzones and remapping what is left), and then pass the input to everything else that wants to read it.
     
  13. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    That's actually a very good point. So PraetorBlue's solution wins.
    Man, this has more plot twists than an episode of Game of Thrones.

    Well shoot. I better grab some popcorn.
    I genuinely adore the arguments. Who would've thought?
     
    Yoreki likes this.
  14. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,082
    Wait till someone reminds them that the input system handles dead zones.
     
    orionsyndrome likes this.
  15. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    10,977
    ...badly :)

    At least the old one, not sure about the new one. I have always used GetAxisRaw because whatever the old input system was doing has never worked well for me.
     
  16. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
  17. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,495
    I don't think it's off topic as this is most likely what the author had in mind. However you are right that the code in the original form doesn't make sense / wouldn't work since the "normalized" was done before the check.

    Yes, this would be a necessary change actually. I originally missed that the normalized was before the check... I should have read the OP more carefully.

    I know about the deadzone in the old input system. However one main issue with that was that you could not change the values at runtime. The deadzone setting should be a user setting as it's device specific.
     
  18. KoonyGeez

    KoonyGeez

    Joined:
    Aug 17, 2019
    Posts:
    3
    I did remove the if statement when testing but I just wanted to know what its actual use was since it wasn't explained in the tutorial. Of course now I know. Thank you all.
     
    Yoreki likes this.
  19. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Well I'm heavily biased when it comes to joypads. As a true PC master race specimen I despise the things, that's where my blindness comes from. Of course, this utterly contradicts the professional in me. Now in the hindsight, it's completely obvious this fights against a pretty loose dead zone in analog controllers, and works as a nonzero detector in the keyboard context. This was a good lesson for me as well :)
     
    Yoreki likes this.