0
\$\begingroup\$

Using Unity 5, I want to use have one keyboard key ("f") increase a counter when pressed. It means, using Input.GetKeyDown("f") function in a way that: if "f" is pressed when the counter=1, then the counter is set to be equal 2. If counter was equal 2 when "f" is pressed, then counter is set to 3.

However, it does not work properly: it seems that Unity detects the key being pressed many times, no matter if it was only pressed once. See the code that illustrates the situation:

using UnityEngine;
using System.Collections;

public class TestingKeyGetRecursion : MonoBehaviour {

int counter = 1;

void Update () {

    if(counter == 1 && Input.GetKeyDown("f"))
    {
        counter = 2;
        Debug.Log(counter);
    }
    if(counter == 2 && Input.GetKeyDown("f"))
    {
        counter = 3;
        Debug.Log(counter);
    }
    if(counter == 3 && Input.GetKeyDown("f"))
    {
        counter = 1;
        Debug.Log(counter);
    }
}

void OnGUI()
{
    GUI.Label(new Rect(30, 10, 250, 20),  "counter: " + counter.ToString());
}
}

As you can see in the console if you run the above example, the values are properly set into the counter variable and printed in the console. However, it happens several times in the same key-hit and, in the end, the counter ends up equal as it started, i.e. equal 1.

Can anyone please explain:

1) why does the Engine behave like that;

2) is there a way to solve such situation? I cannot substitute the counter with a bool variable and just do "counter != counter" when "f" is pressed.

\$\endgroup\$
5
\$\begingroup\$

It is due to your flow is confusing for engine as it will try to execute your every check in every frame. Try to implement with else or with single statement like,

using UnityEngine;
using System.Collections;

public class TestingKeyGetRecursion : MonoBehaviour {

    int counter = 1;

    void Update () 
    {
        if (Input.GetKeyDown(KeyCode.F))
        {
            if (++counter >= 4)
                counter = 1;
            Debug.Log(counter);
        }

        // ======== OR ======= //
        // On DMGregory's advise

        if (Input.GetKeyDown(KeyCode.F))
        {
            counter = (counter % 3) + 1;
            Debug.Log(counter);
        }
    }

    void OnGUI()
    {
        GUI.Label(new Rect(30, 10, 250, 20),  "counter: " + counter.ToString());
    }
}
\$\endgroup\$
  • 3
    \$\begingroup\$ Agreed except for "engine may confuse with..." — the engine isn't confused here, it's behaving very predictably; the only confusion is in OP"s understanding of control flow. ;) To shorten that code snippet even further, we could even do counter = (counter % 3) + 1; instead of the extra if. \$\endgroup\$ – DMGregory Jan 26 '16 at 13:56
  • \$\begingroup\$ You are the man @DMGregory :). You could edit my answer :) You can in fact :P \$\endgroup\$ – Hamza Hasan Jan 26 '16 at 14:38
6
\$\begingroup\$

To answer your first Question "why does the Engine behave like this" I would add the following:

The Engine does not actually detect the key being pressed multiple times. What you need to keep in mind here is that the "was pressed" state of the key does not reset until the next frame, and your code will be executed in a single frame. Furthermore, every if statement will be evaluated. So successively going through your update method what happens is

  • We start with counter = 1, and we assume key was pressed in this frame
  • Now we check if key was pressed (in this frame) and counter is 1 => yes, so we set counter to 2
  • Now we check if key was pressed (in this frame) and counter is 2 => yes it is, since we just set it to 2! So it gets set to 3
  • Now we check if key was pressed (in this frame) and counter is 3 => again, we just set it to 3, so we set it to 1 again

Your second Question "is there a way to solve such situation" has been answered by others already, so I won't write any code here, but for completeness sake: To solve this, you can simply make sure that not every if statement is being evaluated during the same frame by using else if on all but the first one. That way, as soon as it jumps into one of the (else) if statements, it will not bother checking the others in this frame anymore. See RianF2's answer for the else if code or Hamza Hasan's answer for a nice and short code snippet that does the same.

\$\endgroup\$
2
\$\begingroup\$

Try adding an "else-if" instead of a normal "if". So your update method should look like this:

void Update () {

    if(counter == 1 && Input.GetKeyDown("f"))
    {
        counter = 2;
        Debug.Log(counter);
    }
    else if(counter == 2 && Input.GetKeyDown("f"))
    {
        counter = 3;
        Debug.Log(counter);
    }
    else if(counter == 3 && Input.GetKeyDown("f"))
    {
        counter = 1;
        Debug.Log(counter);
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.