2
\$\begingroup\$

I'm attempting to implement a really simple game state system, this is my first game - trying to make a Tetris clone. I'd consider myself a novice programmer at best. I've been testing it out by drawing different textures to the screen depending on the current state.

The 'Not Playing' state seems to work fine, I press Space and it changes to 'Playing', but when I press 'P' to pause or resume the game nothing happens. I tried checking current and previous keyboard states thinking it was happening to fast for me to see, but again nothing seemed to happen. If I change either the pause or resume, so they're both different, it works as intended.

I'm clearly missing something obvious, or completely lacking some know-how in regards to how update and/or the keyboard states work.

Here's what I have in my Update method at the moment:

    protected override void Update(GameTime gameTime)
    {
        KeyboardState CurrentKeyboardState = Keyboard.GetState();

        // Allows the game to exit
        if (CurrentKeyboardState.IsKeyDown(Keys.Escape))
            this.Exit();

        // TODO: Add your update logic here

        if (CurrentGameState == GameStates.NotPlaying)
        {
            if (CurrentKeyboardState.IsKeyDown(Keys.Space))
                CurrentGameState = GameStates.Playing;
        }

        if (CurrentGameState == GameStates.Playing)
        {
            if (CurrentKeyboardState.IsKeyDown(Keys.P))
                CurrentGameState = GameStates.Paused;
        }

        if (CurrentGameState == GameStates.Paused)
        {
            if (CurrentKeyboardState.IsKeyDown(Keys.P))
                CurrentGameState = GameStates.Playing;
        }

        base.Update(gameTime);
    }
\$\endgroup\$

5 Answers 5

4
\$\begingroup\$

When playing and you hit the P key your if(CurrentGameState == GameStates.Playing) block is executed, changing the state to paused. Then the if(CurrentGameState == GameStates.Paused) block that follows also runs (because you just changed the state to paused above and the P key state hasn't changed), changing the game state back to playing. This is why you never experience the paused state.

At the very least, I'd make the code for each state exclusive within the update method, either by using a switch statement or the following:

if (CurrentGameState == GameStates.NotPlaying)
{
  // do stuff
}
else if (CurrentGameState == GameStates.Playing)
{
  // do stuff
}
else if (CurrentGameState == GameStates.Paused)
{
  // do stuff
}
\$\endgroup\$
1
  • \$\begingroup\$ The else if statement made the difference. I had to add in a previous keyboard state to keep it from updating every frame...I was almost there on my own. I knew it would be something obvious. \$\endgroup\$
    – user13095
    Commented Mar 28, 2012 at 3:03
3
\$\begingroup\$

As others have mentioned: Immediately after you set the gamestate to paused, it enters the following if-block and sets the state back to playing.

Sure, the simple fix is to include an else between the two states, but that will cause yet an other problem because IsKeyDown will return true in sequential updates, and since updates happen 40+ times per second, you will temporarily unpause for one frame, then pause the very next, and back and forth until the key is released.

The real solution is to detect the moment when the key is released (or pressed if you want to do it backwards). This can be done by storing the previous keyboard state, and comparing that to the current keyboard state. Something like:

if (LastKeyboardState.IsKeyDown(Keys.P) && !CurrentKeyboardState.IsKeyDown(Keys.P))
{
    // The 'P' key has just been released
    // This will only fire ONCE when the 'P' key is released
}

I would recommend making a wrapper class for the keyboard that handles this for you, but that is up to you.

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

Since there are many ways to skin a cat, Here's another:

bool gamePaused = false;
KeyboardState currentKB, previousKB;
protected override void Update(GameTime gameTime)
{
    previousKB = currentKB;
    currentKB = Keyboard.GetState();

    if (currentKB.IsKeyDown(Keys.Escape)) Exit();
    if (currentKB.IsKeyUp(Keys.P) && previousKB.IsKeyDown(Keys.P)) gamePaused = !gamePaused;

    if (gamePaused) return;

    //do update stuff here

    base.Update(gameTime);
}
\$\endgroup\$
2
\$\begingroup\$

There is a simple explanation here.

\$\endgroup\$
1
\$\begingroup\$

Look closely at the logic you have to pause and unpause:

if (CurrentGameState == GameStates.Playing)
{
    if (CurrentKeyboardState.IsKeyDown(Keys.P))
        CurrentGameState = GameStates.Paused;
}

if (CurrentGameState == GameStates.Paused)
{
    if (CurrentKeyboardState.IsKeyDown(Keys.P))
        CurrentGameState = GameStates.Playing;
}

If your game is playing and you have pressed P this frame, the first set if statements' conditions will be true. This will change CurrentGameState to Paused which will then make the second set of conditions true, chaning it back to Playing.

You'll probably just need to set up a series of conditionals that exclusively check the game state at the beginning of the frame. Either by using a switch statement instead of a series of ifs or at least putting subsequent ifs within else blocks:

if (CurrentGameState == GameStates.Playing)
{
    if (CurrentKeyboardState.IsKeyDown(Keys.P))
        CurrentGameState = GameStates.Paused;
}
else if (CurrentGameState == GameStates.Paused)
{
    if (CurrentKeyboardState.IsKeyDown(Keys.P))
        CurrentGameState = GameStates.Playing;
}
\$\endgroup\$
1
  • \$\begingroup\$ Yep, else if saved the day. Too bad I can't give you both credit...and I can't even up vote due to lack of rep. \$\endgroup\$
    – user13095
    Commented Mar 28, 2012 at 3:05

You must log in to answer this question.