WEB制作とゲーム開発のブログ

INWAN'S LABO

【JavaScript】キー入力でキャラクターを操作する

キー入力で操作する

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

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

 

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

 

というかほとんどサンプルプログラムの解説なのでファイルをダウンロードしてプログラムを見ながら読んでください。

サンプルプログラム

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

 

ファイルをダウンロード

 

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

 

あとゲーム部分はPixiJSをベースに作った簡易ゲームエンジン「p94e.js」を使っています。

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

 

キー入力の基本

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

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

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

 

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

 

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

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

 

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

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

 

ボタンの入力操作を作る

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

 

状態管理用定数を作る

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

 

なのでその定数を作っておきます(main.jsの168行目あたり)。

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

 

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

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

なので一つのボタンにつき2つフラグを用意します(main.jsの175行目くらい)。

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()を使って押された、もしくは離したキーのフラグを変えます(main.jsの200行目くらい)。

サンプルでは操作キーを簡単に変えられるように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()で行います(main.jsの273行目)。

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

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方向だけ定数を作っておきます(main.jsの158行目あたり)。

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

 

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

checkDirection()は押されている方向キーの数字を全部足して返します(main.jsの256行目あたり)。

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文で分岐して処理します(main.jsの71行目あたり)。

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

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の値を変えれば好きなボタンでプレイできます。

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

 

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

 

関連記事