JavaScriptでゲーム作ろう

スマホやパソコンのブラウザで遊べるゲームはマウスのクリックやタッチによる操作だけではありません。

キーボードでの操作も使うことができれば幅広くいろんなジャンルが作れます。

 

ということで今回のこの記事ではJavaScriptでキーボードを使った、特に十字キーなどで操作するタイプのゲームでのキー入力の扱い方について紹介します。

(※ちなみにこの記事は3部作となっていて、「バーチャルパッド」と「ゲームパッド」もこのキー入力のプログラムをベースに追加していきます。)

 

出来上がりはこんな感じです。

サンプルプログラムをプレイ

サンプルプログラムでは方向キーがwasd、Aボタンがn、Bボタンがm、スタートボタンがEnterになっています。

 

こちらからサンプルプログラムをダウンロードできます。

ファイルをダウンロード

 

キー操作部分は素のJavaScriptだけで作っていますがゲーム部分はPixiJSをベースに作った簡易ゲームエンジン「p94e.js」を使っています。

気になる方はp94e.jsの記事も一緒にお読みください。

 

キー操作のプログラム全文

操作入力用のInputManagerクラスはこんな感じになっています。

class InputManager {
  constructor() {
    //方向入力チェック用定数
    this.keyDirections = {
      UP: 1,
      UP_RIGHT: 3,
      RIGHT: 2,
      DOWN_RIGHT: 6,
      DOWN: 4,
      DOWN_LEFT: 12,
      LEFT: 8,
      UP_LEFT: 9,
    };
    //キーの状態管理定数
    this.keyStatus = {
      HOLD: 2,
      DOWN: 1,
      UNDOWN: 0,
      RELEASE: -1,
    };
    //キーの状態管理用変数
    this.input = {
      //入力されたキーのチェック用
      keys: {
        Up: false,
        Right: false,
        Down: false,
        Left: false,
        A: false,
        B: false,
        Start: false
      },
      //一つ前のキーの状態管理用
      keysPrev: {
        Up: false,
        Right: false,
        Down: false,
        Left: false,
        A: false,
        B: false,
        Start: false
      },
    };

    //キーを押した時
    document.addEventListener('keydown', (e) => {
      switch(e.key){
        case Config.Keys.Up:
          this.input.keys.Up = true;
          break;
        case Config.Keys.Down:
          this.input.keys.Down = true;
          break;
        case Config.Keys.Right:
          this.input.keys.Right = true;
          break;
        case Config.Keys.Left:
          this.input.keys.Left = true;
          break;
        case Config.Keys.A:
          this.input.keys.A = true;
          break;
        case Config.Keys.B:
          this.input.keys.B = true;
          break;
        case Config.Keys.Start:
          this.input.keys.Start = true;
          break;
      }
    });

    //キーを離したとき
    document.addEventListener('keyup', (e) => {
      switch(e.key){
        case Config.Keys.Up:
          this.input.keys.Up = false;
          break;
        case Config.Keys.Down:
          this.input.keys.Down = false;
          break;
        case Config.Keys.Right:
          this.input.keys.Right = false;
          break;
        case Config.Keys.Left:
          this.input.keys.Left = false;
          break;
        case Config.Keys.A:
          this.input.keys.A = false;
          break;
        case Config.Keys.B:
          this.input.keys.B = false;
          break;
        case Config.Keys.Start:
          this.input.keys.Start = false;
          break;
      }
    });
  }

  //方向キー入力チェック
  checkDirection() {
    let direction = 0;//初期化
    if(this.input.keys.Up){
        direction += this.keyDirections.UP;
    }
    if(this.input.keys.Right){
      direction += this.keyDirections.RIGHT;
    }
    if(this.input.keys.Down){
      direction += this.keyDirections.DOWN;
    }
    if(this.input.keys.Left){
      direction += this.keyDirections.LEFT;
    }
    return direction;
  }

  //ボタンの入力状態をチェックして返す
  checkButton(key) {
    if(this.input.keys[key]){
      if(this.input.keysPrev[key] == false){
        this.input.keysPrev[key] = true;
        return this.keyStatus.DOWN;//押されたとき
      }
      return this.keyStatus.HOLD;//押しっぱなし
    }else{
      if(this.input.keysPrev[key] == true){
        this.input.keysPrev[key] = false;
        return this.keyStatus.RELEASE;//ボタンを離した時
      }
      return this.keyStatus.UNDOWN;//押されていない
    }
  }
}

 

JavaScriptのキー入力

キーボードでの入力もマウスやタッチと同じイベントで処理します。

document.addEventListener('keydown', (e) => {
  console.log(e.key);
});

上記のように記述すると押したキーがコンソールに表示されます。

 

押したときは「keydown」、離したときは「keyup」です。

 

e.keyの値は押したボタンが文字で入っているのでconsole.log()で表示させて確認できます。

アルファベットは大文字・小文字で違うのでCapsLockには要注意です。

 

単純な押した離したの判定なら上のように書けばいいんですが、ゲームで使うとなるともうちょっと工夫した方が便利だと思います。

ということで次からゲーム向けに作っていきます。

 

ボタンの入力操作を作る

まずはキー入力をコントローラーのボタンのように使えるように作ります。

 

状態管理用定数を作る

キーボードやゲームパッドのボタンは単純に押した離しただけではなくて、正確にゲームでコントロールしようと思うと「今押した」「押しっぱなし」「今離した」「押してない」という4つの状態が発生します。

 

なのでその定数を作っておきます。

this.keyStatus = {
  HOLD: 2,
  DOWN: 1,
  UNDOWN: 0,
  RELEASE: -1,
};

 

で、この4つの状態を確認するには「今のボタンの状態の一つ前」の状態が必要になります。

「今の状態」と「一つ前」の状態を比較して違っていれば「今状態が変わった」ということです。

なので一つのボタンにつき2つフラグを用意します。

this.input = {
  //入力されたキーのチェック用
  keys: {
    Up: false,
    Right: false,
    Down: false,
    Left: false,
    A: false,
    B: false,
    Start: false
  },
  //一つ前のキーの状態管理用
  keysPrev: {
    Up: false,
    Right: false,
    Down: false,
    Left: false,
    A: false,
    B: false,
    Start: false
  },
};

 

キー入力イベントを作る

document.addEventListener()を使って押された、もしくは離したキーのフラグを変えます。

サンプルでは操作キーを簡単に変えられるようにConfigにキー用の定数を作っています。

キーを変更したい場合はConfigの所だけ変えればいいのでとっても楽ちんです。

document.addEventListener('keydown', (e) => {
  switch(e.key){
    case Config.Keys.Up:
      this.input.keys.Up = true;
      break;
    case Config.Keys.Down:
      this.input.keys.Down = true;
      break;
    case Config.Keys.Right:
      this.input.keys.Right = true;
      break;
    case Config.Keys.Left:
      this.input.keys.Left = true;
      break;
    case Config.Keys.A:
      this.input.keys.A = true;
      break;
    case Config.Keys.B:
      this.input.keys.B = true;
      break;
    case Config.Keys.Start:
      this.input.keys.Start = true;
      break;
  }
});

//キーを離したとき
document.addEventListener('keyup', (e) => {
  switch(e.key){
    case Config.Keys.Up:
      this.input.keys.Up = false;
      break;
    case Config.Keys.Down:
      this.input.keys.Down = false;
      break;
    case Config.Keys.Right:
      this.input.keys.Right = false;
      break;
    case Config.Keys.Left:
      this.input.keys.Left = false;
      break;
    case Config.Keys.A:
      this.input.keys.A = false;
      break;
    case Config.Keys.B:
      this.input.keys.B = false;
      break;
    case Config.Keys.Start:
      this.input.keys.Start = false;
      break;
  }
});

 

ボタンのチェック

ボタンがどうなっているかのチェックはcheckButton()で行います。

ここで今の状態と一つ前の状態を比較してその結果を返しています。

checkButton(key) {
  if(this.input.keys[key]){
    if(this.input.keysPrev[key] == false){
      this.input.keysPrev[key] = true;
      return this.keyStatus.DOWN;//押されたとき
    }
    return this.keyStatus.HOLD;//押しっぱなし
  }else{
    if(this.input.keysPrev[key] == true){
      this.input.keysPrev[key] = false;
      return this.keyStatus.RELEASE;//ボタンを離した時
    }
    return this.keyStatus.UNDOWN;//押されていない
  }
}

 

これをループ内で

if(inputManager.checkButton("A") == inputManager.keyStatus.DOWN){
  //ボタン押したときの処理
}

のように使うと、上の例ではボタンを押したときだけ処理ができます。

 

ちなみにcheckButton()はループ内で使うのは1回にしてください。

使った時点で前の状態を書き換えているので2回使うとおかしくなります。

複数回使いたい場合は戻り値を保存して使ってください。

 

十字キー入力を作る

ファミコンのコントローラーからゲーム機のコントローラーには十字になった操作ボタンがついています。

この十字キーのおかげで私たちはキャラクターを上下左右に動かすことができます。

 

2進数で考える

この4方向の状態は4桁の2進数で再現できます。

 

0000が何も押されていない状態です。で

  • 0001 上
  • 0010 右
  • 0100 下
  • 1000 左

と考えてください。

 

そうすると上と右が押されると3になります。

左上が押されると9になります(2進数を10進数に直してね)。

 

こうすることで複数ボタンを押されている状態を一つの数字で表すことができます。

しかし、実際使えるのは8方向だけなので、その8方向だけ定数を作っておきます。

this.keyDirections = {
  UP: 1,
  UP_RIGHT: 3,
  RIGHT: 2,
  DOWN_RIGHT: 6,
  DOWN: 4,
  DOWN_LEFT: 12,
  LEFT: 8,
  UP_LEFT: 9,
};

 

入力方向を算出して操作する

checkDirection()は押されている方向キーの数字を全部足して返します。

checkDirection() {
  let direction = 0;//初期化
  if(this.input.keys.Up){
      direction += this.keyDirections.UP;
  }
  if(this.input.keys.Right){
    direction += this.keyDirections.RIGHT;
  }
  if(this.input.keys.Down){
    direction += this.keyDirections.DOWN;
  }
  if(this.input.keys.Left){
    direction += this.keyDirections.LEFT;
  }
  return direction;
}

 

返ってきた値でswitch文で分岐して処理します。

作ってある定数以外の数字は無視します。

const oblique = 1 / Math.sqrt(2);//ななめ移動の値
switch(inputManager.checkDirection()){
  case inputManager.keyDirections.UP:
    player.y--;
    break;
  case inputManager.keyDirections.UP_RIGHT:
    player.x += oblique;
    player.y -= oblique;
    break;
  case inputManager.keyDirections.RIGHT:
    player.x++;
    break;
  case inputManager.keyDirections.DOWN_RIGHT:
    player.x += oblique;
    player.y += oblique;
    break;
  case inputManager.keyDirections.DOWN:
    player.y++;
    break;
  case inputManager.keyDirections.DOWN_LEFT:
    player.x -= oblique;
    player.y += oblique;
    break;
  case inputManager.keyDirections.LEFT:
    player.x--;
    break;
  case inputManager.keyDirections.UP_LEFT:
    player.x -= oblique;
    player.y -= oblique;
    break;
  default:
    break;
}

これなら斜め移動もちゃんと作れます。

 

まとめ

ちょっとややこしい感じですがこれでゲームの操作はばっちりできると思います。

 

Config.keyの値を変えれば好きなボタンでプレイできます。

設定画面とか作って変更できるようにするとかなりプロっぽくてかっこいいと思います。

 

このプログラムをベースにバーチャルパッドとゲームパッドも追加していきます。