RYO620
DESIGN & DEVELOPMENT
Pixi.js でゲームを作ってみる vol.2
Ryosuke

Pixi.js でゲームを作ってみる vol.2

この記事では Pixi.js を使ってブラウザで動く簡単な2Dゲームを作ってみたいと思います。 爆弾を設置して敵を倒す、某爆弾男ゲームのようなシステムを作ってみようかと。 あくまで簡易的な。

Pixi.js でゲームを作ってみる vol.1 の続きです。


OUTLINE

  • キーボードで操作する
    • キャラクターの方向転換
    • キャラクターの移動
  • キャラクターの移動を制限する

キーボードで操作する

キャラクターの方向転換

addEventListenerkeydown のイベントを取得し、対応した方向へテクスチャの向きを変更させてみました。 PIXI.extras.AnimatedSprite のテクスチャは、 インスタンス変数 textures で変更できます。

index.js

// ・ // ・ // ・ /* * jsonのパス、読み込み完了の関数を与える */ loader .add('sprite', '../_assets/img/sprite.json') .once('complete', function(){ var /** * テクスチャの配列(Left: 0, Back: 1, Right: 2, Front: 3) */ ttCharacter = [], /** * キャラクター要素 */ elmCharacter, i, j, k; for (i = 0; i < 4; i++) { // 4方向 ttCharacter[i] = []; for (j = 0; j < 4; j++) { // 4フレーム var frame = j === 0 ? 0: j === 1 ? 1: j === 2 ? 0: j === 3 ? 2: 0; ttCharacter[i].push(PIXI.Texture.fromFrame('character-' + i + '-' + frame)); } } /** * キャラクターの生成(Left: 0, Back: 1, Right: 2, Front: 3) */ elmCharacter = new PIXI.extras.AnimatedSprite(ttCharacter[3]); elmCharacter.play(); elmCharacter.animationSpeed = 0.1; /** * キャラクターの基準点、位置を設定 */ elmCharacter.anchor.set(0.5); elmCharacter.position.set(640 / 2, 360 / 2); stage.addChild(elmCharacter); /* * キーボードが押されたイベント */ document.addEventListener('keydown', handleKeyDown); function handleKeyDown(e){ var key = e.key; /** * キーボードの矢印キーに対応したテクスチャに変更 */ switch (key) { case 'ArrowLeft': console.log('left'); elmCharacter.textures = ttCharacter[0]; break; case 'ArrowUp': console.log('up'); elmCharacter.textures = ttCharacter[1]; break; case 'ArrowRight': console.log('right'); elmCharacter.textures = ttCharacter[2]; break; case 'ArrowDown': console.log('down'); elmCharacter.textures = ttCharacter[3]; break; default: break; } } }); /* * 読み込む */ loader.load();

動くんだけど、テクスチャの切り替えがやたら遅い……。 textures で別のテクスチャを指定してから アニメーションの play() が1周終わるまで、テクスチャ切り替えはお預けのよう……。

なので別の案を。 新たに PIXI.Container を作って、4種類のアニメーションを全部ぶっこんでおく。 方向切り替えのタイミングで該当するアニメーションのみ visible = true で表示、 その他は false で非表示にしておく。

index.js

// ・ // ・ // ・ /* * jsonのパス、読み込み完了の関数を与える */ loader .add('sprite', '../_assets/img/sprite.json') .once('complete', function(){ var /** * テクスチャの配列(Left: 0, Back: 1, Right: 2, Front: 3) */ ttCharacter = [], /** * キャラクターアニメーションの配列 */ elmAnimationCharacter = [], /** * キャラクター要素 */ elmCharacter = new PIXI.Container(), i, j, k; for (i = 0; i < 4; i++) { // 4方向 ttCharacter[i] = []; for (j = 0; j < 4; j++) { // 4フレーム var frame = j === 0 ? 0: j === 1 ? 1: j === 2 ? 0: j === 3 ? 2: 0; ttCharacter[i].push(PIXI.Texture.fromFrame('character-' + i + '-' + frame)); } } for (k = 0; k < 4; k++) { elmAnimationCharacter.push(new PIXI.extras.AnimatedSprite(ttCharacter[k])); elmAnimationCharacter[k].play(); elmAnimationCharacter[k].animationSpeed = 0.1; elmAnimationCharacter[k].anchor.set(0.5); elmAnimationCharacter[k].visible = false; elmCharacter.addChild(elmAnimationCharacter[k]); } /** * 正面(Front: 3)を表示する */ elmAnimationCharacter[3].visible = true; elmCharacter.position.set(640 / 3, 360 / 2); stage.addChild(elmCharacter); /** * キーボードが押されたイベント */ document.addEventListener('keydown', handleKeyDown); function handleKeyDown(e){ var key = e.key, i; /** * 全てのアニメーションを非表示 */ for (i = 0; i < 4; i++) { elmAnimationCharacter[i].visible = false; } /** * 矢印キーの方向のアニメーションのみ表示 */ switch (key) { case 'ArrowLeft': console.log('left'); elmAnimationCharacter[0].visible = true; break; case 'ArrowUp': console.log('up'); elmAnimationCharacter[1].visible = true; break; case 'ArrowRight': console.log('right'); elmAnimationCharacter[2].visible = true; break; case 'ArrowDown': console.log('down'); elmAnimationCharacter[3].visible = true; break; default: break; } } }); /* * 読み込む */ loader.load();

これでスムーズに切り替わるようになりました!


キャラクターの移動

単純に押された方向にキャラクターを移動させるだけなら下記のように書けます。 押された方向に PIXI.Container の position をズラすだけです。

index.js

// ・ // ・ // ・ /** * 矢印キーの方向に移動 * 矢印キーの方向のアニメーションのみ表示 */ switch (key) { case 'ArrowLeft': console.log('left'); elmAnimationCharacter[0].visible = true; elmCharacter.position.x -= 16; // 左に16pxズラす break; case 'ArrowUp': console.log('up'); elmAnimationCharacter[1].visible = true; elmCharacter.position.y -= 16; // 上に16pxズラす break; case 'ArrowRight': console.log('right'); elmAnimationCharacter[2].visible = true; elmCharacter.position.x += 16; // 右に16pxズラす break; case 'ArrowDown': console.log('down'); elmAnimationCharacter[3].visible = true; elmCharacter.position.y += 16; // 下に16pxズラす break; default: break; } // ・ // ・ // ・

が、キーボードを押しっぱなしにした場合に、 イベント keydown が起こるタイミングがゲームとして致命的な感じです。 テキストエディタでカーソルを移動させるときのように、ピッ……ピッピッピッピッ……という感じで、初回の入力後に若干の間が生じた後、ようやく連続っぽい動作をします。 そこで、以下のブログを参考にしました。

初心者がモチベーション上げながらプログラミングをしてシューティング(っぽい)ゲームを1本作る! - Λlisue's blog

keydownkeyup のイベントのタイミングでキー入力状態を一旦保存。 常時その入力状態をチェックするループ関数で移動を行う感じです。

index.js

// ・ // ・ // ・ /** * キーの状態を保存(true: 押されている, false: 押されていない) */ var keyStatus = [], i; for (i = 0; i < 4; i++) { keyStatus[i] = false; } /** * キーボードが押されたイベント */ document.addEventListener('keydown', function (e){ keyStatus[e.keyCode - 37] = true; }, false); document.addEventListener('keyup', function (e){ keyStatus[e.keyCode - 37] = false; }, false); /* * checkInput 関数を定義 */ var checkInput = function () { var i, KEY_LEFT = 0, KEY_UP = 1, KEY_RIGHT = 2, KEY_DOWN = 3, /** * 全てのアニメーションを非表示 */ hideAnimation = function () { for (i = 0; i < 4; i++) { elmAnimationCharacter[i].visible = false; } }; /** * 矢印キーの方向に移動 * 矢印キーの方向のアニメーションのみ表示 */ if (keyStatus[KEY_LEFT]) { hideAnimation(); elmAnimationCharacter[0].visible = true; elmCharacter.position.x -= 2; } if (keyStatus[KEY_UP]) { hideAnimation(); elmAnimationCharacter[1].visible = true; elmCharacter.position.y -= 2; } if (keyStatus[KEY_RIGHT]) { hideAnimation(); elmAnimationCharacter[2].visible = true; elmCharacter.position.x += 2; } if (keyStatus[KEY_DOWN]) { hideAnimation(); elmAnimationCharacter[3].visible = true; elmCharacter.position.y += 2; } requestAnimationFrame(checkInput); }; checkInput(); // ・ // ・ // ・

遅延等なく動きもスムーズ! また上記のような方法だと、 同時押しも検出できるため斜め移動も可能です。 斜め方向だと移動速度が速く見えてしまいますが、今回は目をつむります。 スーファミでもよくあるしね……。

DEMO vol.2


キャラクターの移動を制限する

まだゲームステージもできてないので、とりあえず画面外には行けないように制限をかけます。

index.js

// ・ // ・ // ・ /* * checkInput 関数を定義 */ var checkInput = function () { var i, KEY_LEFT = 0, KEY_UP = 1, KEY_RIGHT = 2, KEY_DOWN = 3, /** * 全てのアニメーションを非表示 */ hideAnimation = function () { for (i = 0; i < 4; i++) { elmAnimationCharacter[i].visible = false; } }, /** * 移動を制限 */ restrictMovement = function () { if (elmCharacter.x - 40 <= 0) { elmCharacter.x = 40; } else if (elmCharacter.x + 40 >= 640 * 2) { elmCharacter.x = 640 * 2 - 40; } if (elmCharacter.y - 80 <= 0) { elmCharacter.y = 80; } else if (elmCharacter.y >= 360 * 2) { elmCharacter.y = 360 * 2; } }; /** * 矢印キーの方向に移動 * 矢印キーの方向のアニメーションのみ表示 */ if (keyStatus[KEY_LEFT]) { hideAnimation(); elmAnimationCharacter[0].visible = true; elmCharacter.position.x -= 4; restrictMovement(); } if (keyStatus[KEY_UP]) { hideAnimation(); elmAnimationCharacter[1].visible = true; elmCharacter.position.y -= 4; restrictMovement(); } if (keyStatus[KEY_RIGHT]) { hideAnimation(); elmAnimationCharacter[2].visible = true; elmCharacter.position.x += 4; restrictMovement(); } if (keyStatus[KEY_DOWN]) { hideAnimation(); elmAnimationCharacter[3].visible = true; elmCharacter.position.y += 4; restrictMovement(); } requestAnimationFrame(checkInput); }; // ・ // ・ // ・

ゲームステージを作る段階でグリッドを導入するので、その時にもうちょっとちゃんと制限かけようと思います。
あとモジュール分けたり、リファクタリングしないと汚くなってきた……。

vol.2はここまで。 続きは Pixi.js でゲームを作ってみる vol.3 から。