PixiJSでブラウザゲーム 05方向キーで操作

PixiJSでブラウザゲーム 04クリックイベントではクリックイベントの実装方法を紹介しました。 今回は、矢印方向キーでキャラを操作する処理を実装してみたいと思います。 サンプルでは、矢印方向キーでキャラアイコンを動かすことができます。さらに障害物の猫を設置しその上を通過することは出来なくしてあります。

目次:

前提

  • PixiJS v5.2.1

サンプルコード

05-move-character.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>方向キーでキャラを操作</title>
</head>

<body>
  <div>
    方向キーでキャラを動かせます。
    障害物(猫)の上は通過できなくしてあります。
  </div>
  <div id="game"></div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.1.3/pixi.min.js"></script>
  <script>
  // Create a Pixi Application
  const app = new PIXI.Application({
    width: 512,
    height: 512,
    antialiasing: true,
    transparent: false,
    resolution: 1
  });

  let gameScene;
  let player;
  const collisions = []; // 障害物(猫)一覧

  // #game要素にpixiApp.view追加
  document.querySelector("#game").appendChild(app.view);

  // ロード
  PIXI.Loader.shared
    // リソースを登録
    // 第一引数は任意のキー、第二引数は実体パス
    .add("chara-1", "https://i.imgur.com/JNGbC6F.png")
    .add("chara-2", "https://i.imgur.com/H9smyaD.png")
    .add("chara-3", "https://i.imgur.com/xT6FStW.png")
    .add("explorer", "https://i.imgur.com/rWus6RK.png")
    .add("cat", "https://i.imgur.com/Fk2JkI5.png")
    .add("dungeon", "https://i.imgur.com/EzpxVBZ.png")
    .load(setup);

  //This `setup` function will run when the image has loaded
  function setup(loader, res) {
    // ゲームシーンを作成
    gameScene = new PIXI.Container();
    app.stage.addChild(gameScene);
    // ダンジョン作成(ここで画面の大枠サイズを確定)
    const dungeon = new PIXI.Sprite(PIXI.utils.TextureCache["dungeon"]);
    gameScene.addChild(dungeon);
    // アニメーションを作成してシーンに追加
    player = createAnim(["chara-1", "chara-2", "chara-3", ]);
    player.x = 68;
    player.y = gameScene.height / 2 - player.height / 2;
    player.vx = 0;
    player.vy = 0;
    player.play();
    player.anchor.set(0.5, 0.5); // NOTE: キャラアイコンの重心をアイコンの中心にセット
    gameScene.addChild(player);

    //
    // 障害物(猫)をテキトーに配置
    //
    // 障害物1(猫)を作成してシーンに追加
    const cat = new PIXI.Sprite(PIXI.utils.TextureCache["cat"]);
    cat.x = 200;
    cat.y = gameScene.height / 2 - cat.height / 2;
    gameScene.addChild(cat);
    collisions.push(cat); // 障害物として登録
    // 障害物2(猫)を作成してシーンに追加
    const cat2 = new PIXI.Sprite(PIXI.utils.TextureCache["cat"]);
    cat2.x = 100;
    cat2.y = 400;
    gameScene.addChild(cat2);
    collisions.push(cat2); // 障害物として登録
    // 障害物3(猫)を作成してシーンに追加
    const cat3 = new PIXI.Sprite(PIXI.utils.TextureCache["cat"]);
    cat3.x = 400;
    cat3.y = 200;
    gameScene.addChild(cat3);
    collisions.push(cat3); // 障害物として登録

    // 方向キーイベントを初期化
    initArrowEvent();

    // Start the game loop 
    app.ticker.add((delta) => {
      // プレイヤーの移動方向取得
      const playerDirection = getPlayerDirection();
      // 障害物と衝突していないかチェック
      for (let len = collisions.length, i = 0; i < len; i++) {
        const collision = collisions[i];
        const hitDirection = getCollisionDirection(collision);
        if (hitDirection) return;
      }
      player.x += player.vx;
      player.y += player.vy;
    });

  }

  /**
   * アニメーションスプライトオブジェクトを作成
   * @param  {Array<String>} imgs - imageパスの配列
   * @param  {Object} opts - [OPTIONAL] オプション @see PIXI.AnimatedSprite
   * @return {AnimatedSprite}
   */
  function createAnim(imgs, opts) {
    const textureArray = [];
    for (let i = 0; i < imgs.length; i++) {
      let texture = PIXI.Texture.from(imgs[i]);
      textureArray.push(texture);
    };
    const animatedSprite = new PIXI.AnimatedSprite(textureArray);
    animatedSprite.animationSpeed = (opts && opts.animationSpeed) ? opts.animationSpeed : 0.1;
    return animatedSprite;
  }

  /** プレイヤーの移動方向を取得 */
  function getPlayerDirection() {
    if (player.vx > 0) return "right";
    if (player.vx < 0) return "left";
    if (player.vy > 0) return "bottom";
    if (player.vy < 0) return "top";
    return null;
  }

  /** プレイヤーが衝突した障害物の方向を取得 */
  function getCollisionDirection(collision) {
    const { x, y, vx, vy } = player;
    let direction = null;
    const nextX = x + vx;
    const nextY = y + vy;
    const targetRangeX = [collision.x, collision.x + collision.width];
    const targetRangeY = [collision.y, collision.y + collision.height];
    if ((nextX >= targetRangeX[0] && nextX <= targetRangeX[1]) &&
      (nextY >= targetRangeY[0] && nextY <= targetRangeY[1])) {
      if (vx > 0) direction = "right";
      else if (vx < 0) direction = "left";
      else if (vy > 0) direction = "bottom";
      else if (vy < 0) direction = "top";
    }
    return direction;
  }

  /**
   * 方向キーイベントを初期化
   */
  function initArrowEvent() {
    // Capture the keyboard arrow keys
    const left = keyboard(37),
      up = keyboard(38),
      right = keyboard(39),
      down = keyboard(40);
    //Left arrow key `press` method
    left.press = function() {
      //Change the player's velocity when the key is pressed
      player.vx = -5;
      player.vy = 0;
    };
    //Left arrow key `release` method
    left.release = function() {
      //If the left arrow has been released, and the right arrow isn't down,
      //and the player isn't moving vertically:
      //Stop the player
      if (!right.isDown && player.vy === 0) {
        player.vx = 0;
      }
    };
    //Up
    up.press = function() {
      player.vy = -5;
      player.vx = 0;
    };
    up.release = function() {
      if (!down.isDown && player.vx === 0) {
        player.vy = 0;
      }
    };
    //Right
    right.press = function() {
      player.vx = 5;
      player.vy = 0;
    };
    right.release = function() {
      if (!left.isDown && player.vy === 0) {
        player.vx = 0;
      }
    };
    //Down
    down.press = function() {
      player.vy = 5;
      player.vx = 0;
    };
    down.release = function() {
      if (!up.isDown && player.vx === 0) {
        player.vy = 0;
      }
    };

    // The `keyboard` helper function
    function keyboard(keyCode) {
      var key = {};
      key.code = keyCode;
      key.isDown = false;
      key.isUp = true;
      key.press = undefined;
      key.release = undefined;
      //The `downHandler`
      key.downHandler = function(event) {
        if (event.keyCode === key.code) {
          if (key.isUp && key.press) key.press();
          key.isDown = true;
          key.isUp = false;
        }
        event.preventDefault();
      };

      //The `upHandler`
      key.upHandler = function(event) {
        if (event.keyCode === key.code) {
          if (key.isDown && key.release) key.release();
          key.isDown = false;
          key.isUp = true;
        }
        event.preventDefault();
      };

      //Attach event listeners
      window.addEventListener(
        "keydown", key.downHandler.bind(key), false
      );
      window.addEventListener(
        "keyup", key.upHandler.bind(key), false
      );
      return key;
    }
  }
  </script>
</body>

</html>

実行

以下のiframeをクリックしカーソルを当ててから方向キーを操作してみてください。

ひとこと

以上、方向キーでキャラを動かす方法が分かりましたね。 次回はドラクエ風の流れるメッセージをゲームシーン上に表示してみましょう。

宣伝: PixiJSを使ったブラウザゲーム開発なら田中ソフトウェアラボにご相談ください!

オススメ:Zattoyomiで時事ネタチェックの時間節約!
芽萌丸プログラミング部 @programming
プログラミング関連アカウント。Web標準技術を中心に書いていきます。フロントエンドからサーバサイドまで JavaScript だけで済ませたい人たちの集いです。記事は主に @TanakaSoftwareLab が担当。