この記事では Pixi.js を使ってブラウザで動く簡単な2Dゲームを作ってみたいと思います。 爆弾を設置して敵を倒す、某爆弾男ゲームのようなシステムを作ってみようかと。 あくまで簡易的な。
Pixi.js でゲームを作ってみる vol.1 の続きです。
OUTLINE
- キーボードで操作する
- キャラクターの方向転換
- キャラクターの移動
- キャラクターの移動を制限する
キーボードで操作する
キャラクターの方向転換
addEventListener
で keydown
のイベントを取得し、対応した方向へテクスチャの向きを変更させてみました。
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
keydown
、 keyup
のイベントのタイミングでキー入力状態を一旦保存。 常時その入力状態をチェックするループ関数で移動を行う感じです。
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(); // ・ // ・ // ・
遅延等なく動きもスムーズ! また上記のような方法だと、 同時押しも検出できるため斜め移動も可能です。 斜め方向だと移動速度が速く見えてしまいますが、今回は目をつむります。 スーファミでもよくあるしね……。
キャラクターの移動を制限する
まだゲームステージもできてないので、とりあえず画面外には行けないように制限をかけます。
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 から。