# Game State Management with Key Events

**URL:** <https://discourse.processing.org/t/game-state-management-with-key-events/19492>\
**Category:** Coding Questions\
**Created:** [April 7, 2020, 4:46am UTC](https://discourse.processing.org/t/game-state-management-with-key-events/19492 "2020-04-07T04:46:31Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![graysonh](https://avatars.discourse-cdn.com/v4/letter/g/a8b319/32.png) [@graysonh](https://discourse.processing.org/u/graysonh)\
**Post date:** [April 7, 2020, 4:46am UTC](https://discourse.processing.org/t/game-state-management-with-key-events/19492/1 "2020-04-07T04:46:32Z")

</div>

Hello all,

I’m working on a Roguelike dungeon crawler and I am learning new game development/coding techniques along the way. I am having trouble creating a finite state machine that switches between states with user input as seen in the diagram below:

 ![Gameplay_ Finite State Machine](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/d/d2bdc9318c8becbdb7dbf1eb48f8948cf8e14126.png)

The issue I’m having is with using the same key for entering and leaving a state. (Eg. Normal Gameplay --\> Full Map View --\> Normal Gameplay). The method I am currently using just rapidly switches between states while the key is pressed.

I’m really struggling to come up with a logical way of keeping track of the user input for this.

I’m currently using a booean[256] array inside keyPressed() and keyReleased() to set each character either pressed (true) or not pressed (false).

Here is the relevant code:

```auto

// start, saveExit, death, win;
// 1, 2, 3, 4

int MAXKEYS = 256;
boolean[] keys = new boolean[MAXKEYS];

GameStates states;
void setup() {
  states = GameStates.TitleScreen;
  println(states.toString());
}

String prevString;
void draw() {
  prevString = states.toString();
  states = states.nextState(keys, keys['1'], keys['2'], keys['3'], keys['4']);
  if (!prevString.equals(states.toString())) {
    println(states.toString());
  }
}

void keyPressed() {
  updateKeys(true);
  if (key == ESC) {
    key = 0;
  }
}

void keyReleased() {
  updateKeys(false);
  if (key == ESC) {
    key = 0;
  }
}

void updateKeys(boolean b) {
  if (key < MAXKEYS && keys[key] != b) {
    keys[key] = b;
  }
}

public enum GameStates {

  TitleScreen {
    @Override
      GameStates nextState(boolean[] input, boolean start, boolean saveExit, boolean death, boolean win) {
      if (start) {
        return NormalGameplay;
      } else {
        return TitleScreen;
      }
    }
  }
  , 
    NormalGameplay {
    @Override
      GameStates nextState(boolean[] input, boolean start, boolean saveExit, boolean death, boolean win) {
      if (death) {
        return DeathScreen;
      } else if (win) {
        return EndGame;
      } else if (input[ESC]) {
        return MenuScreen;
      } else if (input['m']) {
        return FullMapView;
      } else if (input['c']) {
        return CharacterInformationView;
      } else {
        return NormalGameplay;
      }
    }
  }
  , 
    MenuScreen {
    @Override
      GameStates nextState(boolean[] input, boolean start, boolean saveExit, boolean death, boolean win) {
      if (saveExit) {
        return TitleScreen;
      } else if (input[ESC]) {
        return NormalGameplay;
      } else {
        return MenuScreen;
      }
    }
  }
  , 
    DeathScreen {
    @Override
      GameStates nextState(boolean[] input, boolean start, boolean saveExit, boolean death, boolean win) {
      int todo; // stats
      return TitleScreen;
    }
  }
  , 
    FullMapView {
    @Override
      GameStates nextState(boolean[] input, boolean start, boolean saveExit, boolean death, boolean win) {
      if (input['m']) {
        return NormalGameplay;
      } else if (input['c']) {
        return CharacterInformationView;
      } else {
        return FullMapView;
      }
    }
  }
  , 
    CharacterInformationView {
    @Override
      GameStates nextState(boolean[] input, boolean start, boolean saveExit, boolean death, boolean win) {
      if (input['c']) {
        return NormalGameplay;
      } else if (input['m']) {
        return FullMapView;
      } else {
        return CharacterInformationView;
      }
    }
  }
  , 
    EndGame {
    @Override
      GameStates nextState(boolean[] input, boolean start, boolean saveExit, boolean death, boolean win) {
      int todo; // credits
      return TitleScreen;
    }
  }; 

  abstract GameStates nextState(boolean[] input, boolean start, boolean saveExit, boolean death, boolean win);
}

```

---

<div class="post-metadata">

**Author:** ![graysonh](https://avatars.discourse-cdn.com/v4/letter/g/a8b319/32.png) [@graysonh](https://discourse.processing.org/u/graysonh)\
**Post date:** [April 8, 2020, 2:41am UTC](https://discourse.processing.org/t/game-state-management-with-key-events/19492/2 "2020-04-08T02:41:36Z")

</div>

For anyone with the same issue in the future, this post is how I solved it. I’m not 100% happy with how I had to do it, but at least it’s working the way I originally intended it to now!

I’m now keeping track of both keyPressed() and keyReleased() with the _keys[][]_ boolean array. The first column keeps track of keyPressed() and the second column keeps track of keyReleased().

I modified the game state switching so that it runs off of keyReleased() events rather than keyPressed() events as keyReleased only occurs once on most operating systems.

```auto

// start, saveExit, death, win;
// 1, 2, 3, 4

int MAXKEYS = 256;
boolean[][] keys = new boolean[MAXKEYS][2];

GameStates states;
void setup() {
  states = GameStates.TitleScreen;
  println(states.toString());
}

String prevString;
void draw() {
  prevString = states.toString();
  states = states.nextState(keys, keys['1'][0], keys['2'][0], keys['3'][0], keys['4'][0]);
  if (!prevString.equals(states.toString())) {
    println(states.toString());
  }
}

void keyPressed() {
  updateKeys(key, true, false);
  if (key == ESC) {
    key = 0;
  }
}

void keyReleased() {
  updateKeys(key, false, true);
  if (key == ESC) {
    key = 0;
  }
}

// keys[pressed][first press]
void updateKeys(int k, boolean p, boolean r) {
  if (k < MAXKEYS) {
    keys[k][0] = p;
    keys[k][1] = r;
  }

  // arrow keys etc...
}

// keep track of pressed and released as boolean variables
// reset released boolean after variable is used.

public enum GameStates {

  TitleScreen {
    @Override
      GameStates nextState(boolean[][] input, boolean start, boolean saveExit, boolean death, boolean win) {
      if (start) {
        return NormalGameplay;
      } else {
        return TitleScreen;
      }
    }
  }
  , 
    NormalGameplay {
    @Override
      GameStates nextState(boolean[][] input, boolean start, boolean saveExit, boolean death, boolean win) {
      if (death) {
        return DeathScreen;
      } else if (win) {
        return EndGame;
      } else if (input[ESC][1]) {
        input[ESC][1] = false;
        return MenuScreen;
      } else if (input['m'][1]) {
        input['m'][1] = false;
        return FullMapView;
      } else if (input['c'][1]) {
        input['c'][1] = false;
        return CharacterInformationView;
      } else {
        return NormalGameplay;
      }
    }
  }
  , 
    MenuScreen {
    @Override
      GameStates nextState(boolean[][] input, boolean start, boolean saveExit, boolean death, boolean win) {
      if (saveExit) {
        return TitleScreen;
      } else if (input[ESC][1]) {
        input[ESC][1] = false;
        return NormalGameplay;
      } else {
        return MenuScreen;
      }
    }
  }
  , 
    DeathScreen {
    @Override
      GameStates nextState(boolean[][] input, boolean start, boolean saveExit, boolean death, boolean win) {
      int todo; // stats
      return TitleScreen;
    }
  }
  , 
    FullMapView {
    @Override
      GameStates nextState(boolean[][] input, boolean start, boolean saveExit, boolean death, boolean win) {
      if (input['m'][1]) {
        input['m'][1] = false;
        return NormalGameplay;
      } else if (input['c'][1]) {
        input['c'][1] = false;
        return CharacterInformationView;
      } else {
        return FullMapView;
      }
    }
  }
  , 
    CharacterInformationView {
    @Override
      GameStates nextState(boolean[][] input, boolean start, boolean saveExit, boolean death, boolean win) {
      if (input['c'][1]) {
        input['c'][1] = false;
        return NormalGameplay;
      } else if (input['m'][1]) {
        input['m'][1] = false;
        return FullMapView;
      } else {
        return CharacterInformationView;
      }
    }
  }
  , 
    EndGame {
    @Override
      GameStates nextState(boolean[][] input, boolean start, boolean saveExit, boolean death, boolean win) {
      int todo; // credits
      return TitleScreen;
    }
  }; 

  abstract GameStates nextState(boolean[][] input, boolean start, boolean saveExit, boolean death, boolean win);
}

```

---

<div class="post-metadata">

**Author:** ![jeremydouglass](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/jeremydouglass/32/20_2.png) [@jeremydouglass](https://discourse.processing.org/u/jeremydouglass)\
**Post date:** [May 16, 2020, 9:20pm UTC](https://discourse.processing.org/t/game-state-management-with-key-events/19492/3 "2020-05-16T21:20:08Z")

</div>

Thank you for sharing this approach!

You might also be interested in some simple state machine Java libraries. They all use the builder pattern.

- Stateful - very simple [https://github.com/zevada/stateful](https://github.com/zevada/stateful)
- Easy-states - a bit more verbose [https://github.com/j-easy/easy-states](https://github.com/j-easy/easy-states)
- Squirrel - a more industrial-strength fsm solution [https://github.com/hekailiang/squirrel](https://github.com/hekailiang/squirrel)
