Steps

Keyboard Navigation


This section will create the ability to move the character on the screen by accessing properties of the Phaser class. We will need to incorporate details within several classes to create the overarching functionality.

Step 1 — Create a Property to Connect to Phaser’s “Keyboard Navigation”

In the Hero.ts file, create a property called keys that is required (!) — set the value of this property to Phaser’s Cursor Keys.

  • Note: The ! is a non-null assertion operator that informs the compiler of the application that this cannot be null or undefined (an “empty” value).

Phaser.Types.Input.Keyboard.CursorKeys is a type established by the Phaser framework designed to dictate keyboard inputs within a game. Specifically, this gives access to the four cursor keys (up, down, left, right) along with the spacebar and shift keys.

  • Instead of accessing these inputs on our own, we can use the predefined package and check if those keys are pressed by checking the state of these properties via Phaser.
  • For example, in our update() loop for the game, we can simply call this property and check if keys.left.isDown from the active user to determine what should happen as a result.
// Hero.ts
export class Hero extends Phaser.Physics.Arcade.Sprite {
keys!: Phaser.Types.Input.Keyboard.CursorKeys;

// constructor...

}

Step 2 — Create a Method for Initializing the Keyboard Navigation

In the Hero.ts file, create a private method that takes a “scene” parameter and assigns the keys property to create a function.

Then, call that method in the constructor.

The Hero.ts file should now look like this:

// Hero.ts
import { Play } from './Play';

export class Hero extends Phaser.Physics.Arcade.Sprite {
keys!: Phaser.Types.Input.Keyboard.CursorKeys;

constructor(scene: Play, x, y) {
super(scene, x, y, 'hero');
this.setOrigin(0.5, 0.5);
this.initKeys(scene);
}

private initKeys(scene) {
this.keys = scene.input.keyboard.createCursorKeys();
}
}
  • When a new instance of the Hero class is created, it will call the Phaser.Physics.Arcade.Sprite class that is being extended by Hero and use the values of scene, x, and y to set the origin of the “sprite” (in this case, our Hero character) at the specified location.
  • This is the role of the constructor where super extends “construction” to the larger class that Hero is a part of (which, in this case, is Phaser).
  • The this.setOrigin(0.5, 0.5) gives the arguments for the scene — starting the hero at this particular location on the screen (which will put them slightly to the right of the screen’s end).
  • The initKeys(scene) initializes the keys property through the private method we created.
    • This method uses createCursorKeys to return an object with properties for the cursor keys that we now have access to so that we can check if those keys are being pressed. We can now establish what should happen when those keys are pressed.

Step 3 — Create Movement Actions for the Hero

In the Hero class, create a method called update(). Using conditional logic (if/else), set the actions for the left and right cursor keys to move left or right and to not move if neither left or right are pressed by the user.

We are defining the behavior of the character in response to keyboard inputs that we have set up using Phaser’s framework.

// Hero.ts
export class Hero extends Phaser.Physics.Arcade.Sprite {
//...

update() {
if (this.keys.left.isDown) {
this.runLeft();
} else if (this.keys.right.isDown) {
this.runRight();
} else {
this.halt();
}
}

//...
}
  • The update() method is continuously called as part of the game’s Lifecycle (typically once per frame in the game loop). It will constantly check the state of the keys property we’ve created which is set to the current state of the keyboard input.
  • During that continuous check, if a key we’ve establish is pressed, that corresponding method will be called and halt() will be called if neither of those keys are pressed.
  • We are currently establishing the movement on the horizontal plane of the game and we’ve limited it to left, right, or neither.

Now, create methods for each part of the update() method that set the direction of the Hero’s movement on the screen:

  • runRight()
  • runLeft()
  • halt()
// Hero.ts
export class Hero extends Phaser.Physics.Arcade.Sprite {
//...

runRight() {
this.setVelocityX(160);
this.flipX = false;
}

runLeft() {
this.setVelocityX(-160);
this.flipX = true;
}

halt() {
this.setVelocityX(0);
}

//...
}
  • runRight() — sets the horizontal velocity to 160, making the character move positively along the x axis (to the right).
  • runLeft() — sets the horizontal velocity to -160, making the character move negatively along the x axis (to the left).
  • halt() — sets the horizontal velocity to 0 which stops any movement.
  • this.setVelocity() — is a method predefined in the library of the Phaser physics engine.
    • Velocity is measured in pixels per second
    • 160 would mean that the character moves 160 pixels per second while the cursor key is being pressed.
  • this.flipX — this controls the orientation of the character image. When this is set to false the character image appears in its original orientation. When it is set to true, it will flip the image on the X axis to make it seem like the character is now looking the other direction. In this case, running left will also make the character look to the left (the direction the character is moving).

Hero Class: Current Code

The code in our Hero.ts file should now look like this:

// Hero.ts

import { Play } from './Play';

export class Hero extends Phaser.Physics.Arcade.Sprite {
keys!: Phaser.Types.Input.Keyboard.CursorKeys;

constructor(scene: Play, x, y) {
super(scene, x, y, 'hero');
this.setOrigin(0.5, 0.5);
this.initKeys(scene);
}

update() {
if (this.keys.left.isDown) {
this.runLeft();
} else if (this.keys.right.isDown) {
this.runRight();
} else {
this.halt();
}
}

runRight() {
this.setVelocityX(160);
this.flipX = false;
}

runLeft() {
this.setVelocityX(-160);
this.flipX = true;
}

halt() {
this.setVelocityX(0);
}

private initKeys(scene) {
this.keys = scene.input.keyboard.createCursorKeys();
}
}

Step 4 — Incorporate Keyboard Navigation in Play.ts

First, create an instance of the Hero character in the Play class. This is the scene responsible for gameplay within the game’s lifecycle.

  • This should include a non-null assertion.
  • Make sure that you import the Hero class correctly.

This allows us to store and manipulate the state of the character and the properties of Hero (such as movement).

// Play.ts
import { Hero } from './Hero';

export class Play extends Phaser.Scene {
hero!: Hero;
// ...

Second, create an instance of groups connected to Phaser’s Group (also with a non-null assertion) just like in the Level class.

  • This includes an index signature which means that groups is an object that can have multiple properties. The keys of these properties will be strings.
  • The value of each property will be an instance of Phaser.Physics.Arcade.Group
    • This is a class provided by the Phaser framework to represent a group of game objects so that you can call methods on the group as a whole to store or manipulate their state as a whole.
// Play.ts
export class Play extends Phaser.Scene {
hero!: Hero;
// ...

groups!: { [key: string]: Phaser.Physics.Arcade.Group };

// ...

Third, create a private method called mapProps() that will store an array of props using the data we’ve created for our hero’s movement and “map” those properties from the level object to the current instance of the class.

  • Using a forEach() loop, iterate over each property name in the props array and assign the value of that property from the level object to the same property on this (which means “the current instance of this class from the level class”).
// Play.ts
export class Play extends Phaser.Scene {
// ...

// ...
private mapProps() {
const props = [
'hero',
'groups',
];

props.forEach((prop) => (this[prop] = this.level[prop]));
}
  • This method is copying the 'hero' and 'groups' properties from this.level to this so that we can get the current state of the game’s level (from this.level) and use it in Play when a level starts or changes.

Fourth, call the mapProps() method in the initLevel() method — so that this process will be included when a game level is initiated.

  • Now, the properties from the level (hero and groups) will be included in the Play class when a new Level is created.
// Play.ts
export class Play extends Phaser.Scene {
// ...

initLevel() {
this.level = new Level(this);
this.gotoLevel(this.currentLevel);
**this.mapProps();**
}

// ...

}

Finally, add this information to the update() method we created earlier.

// Play.ts
export class Play extends Phaser.Scene {
// ...

update() {
this.hero.update();
}

}

// ...
  • This method will be called once per frame in the game loop and will now update the hero object (from the Hero class) based on the current game state.
  • This checks the state of the keys property and calls different methods based on which keys are pressed. What we’ve set up for the hero now takes effect in the game and in the level we’re on.

The Play.ts file should look like this:

import { Hero } from './Hero';
import { Level } from './Level';

export class Play extends Phaser.Scene {
hero!: Hero;
level!: Level;
currentLevel: integer = 2;

groups!: { [key: string]: Phaser.Physics.Arcade.Group };

constructor() {
super('Play');
}

create() {
console.log('Play.create()');
this.initLevel();
}

update() {
this.hero.update();
}

initLevel() {
this.level = new Level(this);
this.gotoLevel(this.currentLevel);
this.mapProps();
}

private mapProps() {
const props = [
'hero',
'groups',
];

props.forEach((prop) => (this[prop] = this.level[prop]));
}

private gotoLevel(level) {
this.level.loadLevel(this.cache.json.get(**level:${level}**));
}
}

Checkpoint

  1. Character starts slightly to the right.
  2. Character can move left and right (and doesn’t move when no keys are pressed)
< Back Home