Episode 20: Controls

While I initially set up some simple keyboard controls to select menu options and move the player’s ship, I realized early on that things were gonna get a little complicated. This was for two reasons: first, that I was going to need different controls for each of the game’s states, and second, that the game was going to need mobile-friendly controls.

The Control Factory

Like most other systems, I created a factory to manage the game’s controls. This factory does a few things:

  • Loads the controls based on the game state and device type
  • Checks for input based on the current controls
  • Executes appropriate code based on input
  • Draws and updates the joystick and buttons
  • Enables or disables input

Loading Controls

I probably could have written one function that accepted the state key as a parameter, or just checked the current state, but I broke it up in to different functions to make it more readable. This is the function that loads the controls for the play state:

Checking Input

We then check controls by calling checkPlayControls from the update function in the play sate. This function is really long and complicated, but it basically does the following:

  • If input is disabled, return
  • Update the joystick and check its position
  • If the joystick is being dragged, move the player’s ship
    • Otherwise slow/stop the player’s ship
  • If we’re using a desktop, check if the keys or buttons are being pressed
    • If they are, move the player’s ship, fire weapons, etc.
  • If we’re using mobile, just check if the buttons are being pressed
    • If they are, move the player’s ship, fire weapons, etc.

These steps are executed in a complex series of conditional statements. This was a little tricky because the player could be entering multiple inputs, so we want the right things to happen in the right order.

For example, if the player is pressing the up arrow key, we want to move the player’s ship up and slow it’s lateral movement. But if the player is pressing the up arrow and left arrow keys, we want to move the player’s ship up and left. On top of that, we want to animate the joystick and buttons based on this input. This is why I used the isDown flag on my buttons rather than have them manipulate the game directly.

The Buttons & Joystick

Buttons are a built into Phaser and work in a pretty simple way. You basically create it the same way as animated sprite, except the different frames are drawn based on the button’s state (hovered over, not hovered over, pressed, or unpressed). Here’s how the code looks:

Once the button is created, you bind some callback function to the button, which executes when you press it. This is what we did earlier with the setButtonEvents function.

One decision I made was to have the buttons and joystick animate when the player entered keyboard input. I basically just felt like this would make the game more interesting to look at. Plus I had to draw those things anyway, so why not make them do something?

I was originally using four directional buttons for movement, but I thought it would be better to use a joystick. I looked around at some examples and eventually decided to make my own. I did this for two reasons: first, because I wanted to learn how a joystick would work, and second, I needed the joystick to get both analog and digital input.

The analog input is used to let the player move in multiple directions at once and to accelerate at different rates depending on how far the joystick was dragged from its center position. So if the joystick moved all the way to the left and slightly up, then the player’s ship would accelerate left the maximum amount and accelerate forward slightly.

The digital input is used to navigate the menu and also to reflect keyboard input, which uses the arrow keys, so it can’t be analog. The joystick functions more like a set of four directional buttons in this scenario, because we want to register four different behaviors (move up, move down, move left, move right). Basically it works like this:

If the player drags the joystick or clicks in the joystick area, the joystick will move to the nearest directional area (top, bottom, left, or right) and execute the appropriate function. If the player presses an arrow key, the joystick will move to the corresponding directional area.

One other note: pressing multiple keys at the same time will cause the joystick to move to a location representing both inputs. For example, if the player presses the up arrow key, the joystick will move to the top-center area and fire the moveUp function (or whatever function it’s set to execute). If, while still holding the up arrow key, the player then presses the left arrow key, the joystick will move to the top-left area and fire the moveLeft function (or whatever).

The joystick ended up being a little complicated to build. Here were a few other things I had to deal with:

  • What happens when the player drags the cursor outside of the joystick area?
  • What happens when the player presses opposing directional keys at the same time?
  • What happens if the player holds down two buttons at the same time?
  • What happens if the player holds down the joystick or a directional key in the menu?
  • What happens if the player holds down the joystick and a directional key?

Pausing

One more thing I had to deal with was pausing the game. Phaser has a built-in pause feature, but there’s just one problem: Phaser’s buttons and keys don’t work when the game is paused! The left me with two options: pause the game myself or find another way to check for input. I ended up going with the second option.

The reason I didn’t try to figure out how to pause the game without pausing it is that I didn’t feel like I understand the physics, rendering, timers, and other systems well enough to mess around with them. Also, I had a much better understanding of getting user input in JavaScript. Anyway, here’s what I ended up having to do:

  • Add input events to check for tapping or clicking
    • If a tap or click event occurs, check if a button was pressed or if the joystick was moved, and if so, execute the callback function
  • Use setInterval to check for joystick input every 60th of a second (because update functions aren’t called when the game is paused)
  • Check for keyboard input by checking event.keyCode
    • If a key was pressed, execute the callback function

Unfortunately audio and animations are also disabled when the game is paused, but the only time I actually pause the game is to bring up a menu that lets the player abandon a mission, so it’s not a huge deal.

Leave a Reply