Skip to Content
Course content

154: Handling Input and Game State

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Up until now, we've looked at how to capture raw input, but in a real game, you rarely just map a key press to a function. If you do, you'll quickly find yourself in "if-statement hell," where you're checking 50 different conditions just to figure out if the player is allowed to jump. The secret is to decouple the input (the key pressed) from the game state (what the character is actually doing).

Defining the Player's Mental Model

I like to start by defining exactly what "modes" my character can be in. For this example, we're building a basic combat controller. A player shouldn't be able to walk while they're mid-swing in a heavy attack, and they certainly shouldn't be able to attack while they're already attacking. I'll use an enum class for this because it keeps the states scoped and prevents naming collisions.

enum class PlayerState {
    Idle,
    Moving,
    Attacking
};

class Player {
    PlayerState currentState = PlayerState::Idle;
    float attackTimer = 0.0f;

public:
    void handleInput(char input) {
        // Logic goes here
    }

    void update(float deltaTime) {
        // State-based updates
    }
};


Mapping Input to State Transitions

Now, here is where we translate a key press into a state change. I'm going to map 'W' to movement and 'Space' to attacking. Notice how I'm not just moving the character; I'm changing the state. This allows the rest of the game engine to know exactly what the player is doing without checking the keyboard again.

void Player::handleInput(char input) {
    if (input == 'w') {
        currentState = PlayerState::Moving;
    } else if (input == ' ') {
        currentState = PlayerState::Attacking;
        attackTimer = 0.5f; // The attack lasts half a second
    } else {
        currentState = PlayerState::Idle;
    }
}


The "Animation Reset" Bug

I just ran this code in my head, and I realized I made a classic mistake. Look at the handleInput logic above. If the player holds down the Space bar, currentState is set to Attacking and attackTimer is reset to 0.5f every single frame. In a real game, this would cause the attack animation to restart constantly, leaving the player stuck on frame one of the animation. It's a jittery mess.

The fix is to implement "state guards." We only transition to a state if the current state allows it. I need to make sure that if we're already attacking, the input is ignored until the action finishes.

Locking States for Fluidity

Here is the corrected version. I've added checks to ensure that we don't interrupt an attack and that we only enter the Moving state if we aren't currently locked in an action. I'm also adding the update method to handle the timer that eventually pushes us back to Idle.

void Player::handleInput(char input) {
    // If we are attacking, ignore all other inputs until the timer hits zero
    if (currentState == PlayerState::Attacking) {
        return; 
    }

    if (input == 'w') {
        currentState = PlayerState::Moving;
    } else if (input == ' ') {
        currentState = PlayerState::Attacking;
        attackTimer = 0.5f;
    } else {
        currentState = PlayerState::Idle;
    }
}

void Player::update(float deltaTime) {
    if (currentState == PlayerState::Attacking) {
        attackTimer -= deltaTime;
        if (attackTimer <= 0) {
            currentState = PlayerState::Idle;
        }
    }
}


By shifting the logic this way, the handleInput function becomes a "request" to change state, and the state machine decides if that request is valid. This is the foundation of almost every professional action game; whether it's a simple 2D platformer or a massive open-world RPG, they're all just managing transitions between states like this.




📋 Practical Task

Implementing a Timed Dodge-Roll State

Expand the Player class from the lesson to include a "Dodge-Roll" mechanic. Your implementation must meet the following criteria:

  • Add DodgeRolling to the PlayerState enum.
  • Map the 'Shift' key (or a character of your choice) to trigger the dodge roll.
  • The dodge roll should last for 0.3 seconds.
  • While DodgeRolling, the player should be unable to Attack or Move.
  • Once the dodge timer expires, the player should automatically return to the Idle state.

Write the updated handleInput and update methods to manage this new state and its transition logic.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.