Input.GetKeyDown(...) loop glitches

I have a loop that alternates between states for a text-based adventure game:
(Note: This is just example code)

state = 1

void Update()
{
    if (state == 1) { State_1(); }
    if (state == 2) { State_2(); }
}

void State_1()
{
    //Display some text.
    if (Input.GetKeyDown(KeyCode.Alpha1)) { state = 2 }
}

void State_2()
{
    //Display some text.
    if (Input.GetKeyDown(KeyCode.Alpha1)) { state = 1 }
}

This code results in the state switching rapidly into state == 2 and immediately back to state==1.

My understanding of Input.GetKeyDown(…) is that it returns true for the frame that the key is pressed and only that frame, so why is it being registered as true and subsequently firing the State_2() if statement if it should only fire the first then revert to false?

Any suggestions to fix this would be greatly appreciated. Thanks!

With the code you have, this is expected behaviour. You have a small bug in there.

if state == 1, you enter State_1 method and if alpha1 was pressed, you set state = 2 and exit State_1 method.

The next line that is executed is if (state == 2) { State_2(); }. You just set state = 2 in the previous method so you’ll enter it.

Input state changes only between frames as you said. So if alpha1 was pressed in State_1 method, it will always register as pressed in State_2 too during the same Update call.

Since you have no else between the if-statements, whenever you enter State_1() with alpha1 pressed, you will enter State_2 with alpha1 pressed during the same Upfate call.

Should be fixable with just

if (state == 1) { State_1(); }
else if (state == 2) { State_2(); }

Now the swcobd function call is inside the else-block of the first if, and only if or else will run in any given single Upfate() call.

You can use a property instead, like this :

private int _state = 1;
public int State
{
    get { return _state; }
    set
    {
        if (_state != value) // nothing happens if the new value is the same as the old
        {
            _state = value; // update the value
            switch (_state)
            {
                case 1:
                    StateOne ();
                    break;
                case 2:
                    StateTwo ();
                    break;
            }
        }
    }
}

Then in your Update (), you can check that the other key isn’t down.

void Update ()
{
    if (Input.GetKeyDown(KeyCode.Alpha1) &&
        !Input.GetKeyDown(KeyCode.Alpha2))
        State = 1;
    else if (Input.GetKeyDown(KeyCode.Alpha2) &&
        !Input.GetKeyDown(KeyCode.Alpha1))
        State = 2;
}

This isn’t yet totally safe and optimal but it should do the trick.
Check out my Youtube channel for more tips and tricks.
I’m about to release a new course on multi-platform inputs and display, in which I’ll touch on this kind of stuff. You can also check out my website for more info.