Handling Input

Introduction

A game probably wouldn’t be that fun if you couldn’t interact with it. Adding user input handling to our game is relatively straightforward with cerlib.

It handles the input state management for us and provides a number of functions ready for use.

We first need to add some variables to our game that we can manipulate with our input:

class MyGame : public cer::Game
{
public:
  // ...
  
  void draw(const cer::Window& window) override
  {
    cer::draw_sprite(cer::Sprite {
      .image    = image,
      .dst_rect = { sprite_position, sprite_size },
      .origin   = image.size() / 2,
    });
  }
  
private:
  // ...
  cer::Vector2 sprite_position = {100, 100};
  float sprite_size = 1.0f;
};

Keyboard

First up is the keyboard. We want to move the sprite in the direction the player is pressing. The best place to check the player’s input is in the Update method, since it runs once per frame and just before the game is drawn.

It’s as simple as:

bool update(const cer::GameTime& time) override
{
  const float dt       = float(time.elapsed_time);
  const float movement = dt * 200.0f;
 
  if (cer::is_key_down(cer::Key::Up))
    sprite_position.y -= movement;
 
  if (cer::is_key_down(cer::Key::Down))
    sprite_position.y += movement;
 
  if (cer::is_key_down(cer::Key::Left))
    sprite_position.x -= movement;
 
  if (cer::is_key_down(cer::Key::Right))
    sprite_position.x += movement;
}

Now we should be able to move the sprite around using the arrow keys. A movement variable is used to amplify the amount of movement. A value of 200 multiplied by dt indicates that the sprite is able to move at most 200 units (pixels) per second, since dt is based on fractional seconds, where dt=1 means 1 second.

The opposite of cer::is_key_down is also available, called cer::is_key_up.

cerlib provides an additional function called cer::was_key_just_pressed, which can be used to check a single key press. This is useful for one-time actions, where it’s not unusual that the player presses a key but keeps holding it for a couple of frames longer. If we used cer::is_key_down, we would perform the action across multiple frames, which is probably not what we want.

We only want to know if the player pressed that key, not how long. As an example, we’ll modify our sprite_size variable whenever the U or D key was pressed.

Append the following to our input checks:

if (cer::was_key_just_pressed(cer::Key::U))
  sprite_size += 0.25f;
 
if (cer::was_key_just_pressed(cer::Key::D))
  sprite_size -= 0.25f;

We can now scale the sprite up and down by pressing the respective keys.

The opposite of cer::WasKeyJustPressed is also available, called cer::was_key_just_released. This is true if the player held a key pressed during the previous frames but released it just this frame.

Mouse

Checking for mouse input is very similar to checking the keyboard’s input. The mouse however additionally provides a pointer position as well as a scroll wheel.

Let’s make use of the mouse position first. We use the convenience function cer::mouse_position_delta to obtain how much the mouse moved since the last frame. If the mouse hasn’t moved, it returns a zero vector. If the mouse has moved, then we set the sprite’s position as the current mouse position.

Append the following to our input checks:

const cer::Vector2 mouse_motion = cer::mouse_position_delta();
 
if (!mouse_motion.is_zero())
  spritePosition = cer::mouse_position() * window.pixel_ratio();

The mouse position must be multiplied by the window’s pixel ratio, in case the window’s display is a high DPI display. This is necessary because the mouse position is expressed in logical display units, not pixels. Multiplying it with the pixel ratio converts it to pixel space, which is what cerlib’s rendering uses. If you don’t intent to support high DPI displays in your game, you don’t have to take the pixel ratio into account and can leave the multiplication out.

The sprite should now follow the mouse whenever we move the mouse. Otherwise, it moves based on keyboard input. Let’s also use cer::is_mouse_button_down to move the sprite around. Modify the keyboard checks as follows:

if (cer::is_key_down(cer::Key::Up) || cer::is_mouse_button_down(cer::MouseButton::Left))
  sprite_position.y -= movement;
 
if (cer::is_key_down(cer::Key::Down) || cer::is_mouse_button_down(cer::MouseButton::Right))
  sprite_position.y += movement;

cer::is_mouse_button_down and cer::is_mouse_button_up are the equivalent of cer::is_key_down and cer::is_key_up for mouse buttons.

Next, we also allow resizing the sprite using the mouse wheel. Just modify our keyboard checks:

const cer::Vector2 wheel_delta = cer::mouse_wheel_delta();
 
if (cer::was_key_just_pressed(cer::Key::U) || wheel_delta.y > 0)
  sprite_size += 0.25f;
 
if (cer::was_key_just_pressed(cer::Key::D) || wheel_delta.y < 0)
  sprite_size -= 0.25f;

Gamepad

TODO

Touch

TODO

That’s it so far!

Next: Shading Language