PixiJSでブラウザゲーム 03エフェクト

PixiJSでブラウザゲーム 02シーン切替ではゲームシーンの切替処理を実装してみました。 今回は、キャラアイコンにエフェクトをかけてみます。 サンプルでは、キャラアイコンの「フェードイン」、「フェードアウト」、「バイブレーション」、「点滅」、「回転」を実現しています。

目次:

前提

  • PixiJS v5.2.1

サンプルコード

03-character-effect.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>キャラアイコンのエフェクト</title>
</head>

<body>
  キャラアイコンのエフェクト
  <div>
    <button onclick="effectFadeOut(event)">フェードアウト</button>
    <button onclick="effectFadeIn(event)">フェードイン</button>
    <button onclick="effectVibe(event)">バイブレーション</button>
    <button onclick="effectFlash(event)">点滅</button>
    <button onclick="effectRotate(event)">回転</button>
  </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 anim1; // キャラアイコンインスタンス

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

  //load an image and run the `setup` function when it's done
  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) {
    //
    // ゲームシーンを作成
    //
    const gameScene = new PIXI.Container();
    app.stage.addChild(gameScene);

    // ダンジョン作成(ここで画面の大枠サイズを確定)
    const dungeon = new PIXI.Sprite(PIXI.utils.TextureCache["dungeon"]);
    gameScene.addChild(dungeon);
    // アニメーションを作成してシーンに追加
    anim1 = createAnim(["chara-1", "chara-2", "chara-3", ]);
    anim1.x = 30;
    anim1.y = (gameScene.height / 2) - (anim1.height / 2);
    anim1.vx = 0;
    anim1.vy = 0;
    anim1.play();
    gameScene.addChild(anim1);
  }
  /**
   * アニメーションスプライトオブジェクトを作成
   * @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 effectFadeOut(e) {
    const item = anim1;
    _effect(item.alpha - 0.1, () => {
      console.log("effectFadeOut end.")
    });

    function _effect(alpha, cb) {
      item.alpha = alpha;
      if (alpha <= 0) return cb && cb(null);
      setTimeout(() => {
        _effect(alpha - 0.1, cb);
      }, 100);
    }
  }
  /**
   * キャラのフェードイン
   */
  function effectFadeIn(e) {
    const item = anim1;
    _effect(item.alpha + 0.1, () => {
      console.log("effectFadeIn end.")
    });

    function _effect(alpha, cb) {
      item.alpha = alpha;
      if (alpha >= 1) return cb && cb(null);
      setTimeout(() => {
        _effect(alpha + 0.1, cb);
      }, 100);
    }
  }
  /**
   * キャラの回転
   */
  function effectRotate(e) {
    const item = anim1;
    let rotation = 0; // 現在の回転度数
    const maxCount = 3; // 上限回転数
    const maxTick = 57 * maxCount; // 57 = 1 radians.
    const rotationPerTick = 10; // 1tickのrotation
    const defaultX = item.x;
    const defaultY = item.y;
    // アイコンの中心位置を取得しセット
    const offsetX = item.width / 2;
    const offsetY = item.height / 2;
    item.x += offsetX;
    item.y += offsetY;
    item.anchor.set(0.5, 0.5);
    // item.pivot.set(item.width/2, item.height/2);
    _effect(() => {
      console.log("effectRotate end");
      // アイコンの中心位置を戻す
      item.x -= offsetX;
      item.y -= offsetY;
      item.anchor.set(0, 0);
      item.rotation = 0;
    });

    function _effect(cb) {
      item.rotation += rotationPerTick;
      rotation += rotationPerTick;
      if (rotation >= maxTick) return cb && cb(null);
      setTimeout(() => {
        _effect(cb);
      }, 100);
    }
  }
  /**
   * キャラのバイブ
   */
  function effectVibe(e) {
    const item = anim1;
    let count = 0; // 現在の回数
    const maxCount = 10; // 上限回転数
    const maxRotation = 57 * maxCount; // 57 = 1 radians.
    const rotationPerTick = 0.3; // 1tickのrotation
    const defaultX = item.x;
    const defaultY = item.y;
    const offsetX = item.width / 2;
    const offsetY = item.height / 2;
    item.x += offsetX;
    item.y += offsetY;
    item.anchor.set(0.5, 0.5);
    // item.pivot.set(item.width/2, item.height/2);
    _effect(true, () => {
      console.log("effectVibe end");
      // 位置を戻す
      item.x -= offsetX;
      item.y -= offsetY;
      item.anchor.set(0, 0);
      item.rotation = 0;
    });

    function _effect(leanRight, cb) {
      item.rotation = (leanRight) ? rotationPerTick : -rotationPerTick;
      count += 1;
      if (count > maxCount) return cb && cb(null);
      setTimeout(() => {
        _effect(!leanRight, cb);
      }, 100);
    }
  }
  /**
   * キャラの点滅
   */
  function effectFlash(e) {
    const item = anim1;
    let count = 0;
    const flashCount = 3; // 点滅回数
    const maxCount = flashCount * 2;
    _effect(false, () => {
      console.log("effectFlash end");
      item.alpha = 1;
    });

    function _effect(appear, cb) {
      item.alpha = (appear) ? 1 : 0;
      count += 1;
      if (count > maxCount) return cb && cb(null);
      setTimeout(() => {
        _effect(!appear, cb);
      }, 100);
    }
  }
  </script>
</body>

</html>

実行

ひとこと

キャラアイコンのエフェクトが実現できました。 次回はクリック(&タップ)イベントのハンドリングを実現してみたいと思います。

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

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