頑張らないために頑張る

ゆるく頑張ります

Phaser3でフェードアウトしながらシーンを変える方法

Posted at — Mar 14, 2021

概要

Phaser3でゲームを作るときに、フェードアウトとフェードインを活用してシーンを変える方法があります。しかも割と簡単。今回はその話です。

Phaser3においてシーン遷移する場合、なんの設定もしないといきなりシーンがバシッと変わってしまって、余韻もへったくれもありません。とはいえ、マスクを使ってシーンを変えるほどでもないしなーそういうのは他でやりたいなー、という場合に便利なのがフェードアウトとフェードインです。じわーっと画面がだんだん消えていって、消えきったなーと思ったら別の画面がじわーと現れる、この一連の動きを実装してみます。具体的には、遷移前のシーンをフェードアウトさせて、遷移後のシーンをフェードインさせるわけですね。

コード

全体像

Phaser3でシーンを遷移させる場合はscene.start('nextScene')を使うわけですが、これが実行されるとシーンが遷移します。ということは、これが実行される前にフェードアウトさせて、次のシーン側でフェードインさせるわけです。

今回はFirstSceneからSecondSceneに遷移するケースを考えてみます。

export class FirstScene extends Phaser.Scene {
  constructor() {
    super('firstScene');
  }

  create() {

    const { width, height } = this.game.canvas;

    this.add.text(width / 2, height / 2, 'First Scene').setOrigin(0.5);

    const zone = this.add.zone(width / 2, height / 2, width, height);

    zone.setInteractive({
      useHandCursor: true
    });

    zone.on('pointerdown', () => {
      zone.removeInteractive();
      this.cameras.main.fadeOut(1200, 0, 0, 0);
      // このシーンが完全にフェードアウトしてから次のシーンをstartする
      this.cameras.main.once(Phaser.Cameras.Scene2D.Events.FADE_OUT_COMPLETE, () => {
        this.scene.start('secondScene');
      });
    });

  }
}

export class SecondScene extends Phaser.Scene {
  constructor() {
    super('secondScene');
  }

  create() {

    this.cameras.main.fadeIn(1000, 0, 0, 0);

    const { width, height } = this.game.canvas;

    this.add.text(width / 2, height / 2, 'Second Scene').setOrigin(0.5);

  }
}

const config = {
  type: Phaser.AUTO,
  width: 800,
  height: 600,
  scene: [FirstScene, SecondScene]
};

new Phaser.Game(config);

シーンは前述のとおりFirstSceneSecondSceneの2つで、これらをクラスで準備します。それぞれ、FirstSceneにはフェードアウトするコードを、SecondSceneにはフェードインするコードを実装しています。

フェードアウト

this.cameras.main.fadeOut(1200, 0, 0, 0);
// このシーンが完全にフェードアウトしてから次のシーンをstartする
this.cameras.main.once(Phaser.Cameras.Scene2D.Events.FADE_OUT_COMPLETE, () => {
  this.scene.start('secondScene');
});

上記の部分が、FirstSceneをフェードアウトさせているコードです。scene.cameras.main.fadeOut()の第1引数にフェードアウトする時間、第2引数から第4引数まではそれぞれRGBの値をセットします。RGBの値はフェードアウトする色を指定できます。上記では真っ黒にフェードアウトしますが、「青色にフェードアウトしてほしい」という場合はfadeOut(1000, 0, 0, 255)のように記述すればいいわけです。もちろん、100, 40, 180のような値も設定可能なので、シーンの雰囲気によってフェードアウトする色を指定できます。

ちなみに、第5引数にはコールバック関数を指定できます。コールバック関数は、フェードアウトしている間ずっとフレームごとに呼び出されます。フェードアウトしてから呼び出されるものではないので注意。

フェードアウトし終わってから処理を行いたい場合は、scene.cameras.main.once()の第1引数に「フェードアウトが終わったぜー」という意味のPhaser.Cameras.Scene2D.Events.FADE_OUT_COMPLETEを渡してやります。すると、フェードアウトが完了したかどうかを勝手に判断してくれて、完了したら第2引数に指定されたコールバック関数を実行します。ここではSecondSceneに遷移しろよーと書いてあります。お察しのとおり、フェードアウトの完了以外にもイベントは存在していて、FADE_OUT_STARTという「フェードアウトが始まったとき」というようにイベント開始をトリガーにできたりします。また、フェードアウト以外にもSHAKE_STARTというシェイクに関するイベントも実装されています。

  1. FADE_OUT_STARTでフェードアウトの開始を検知して、BGMを変更する。
  2. フェードアウト中はコールバック関数を使って、ランダムなタイミングで効果音を鳴らす。
  3. FADE_OUT_COMPLETEでフェードアウトの完了を検知して、シーンを変更する。

上記のような一連の処理が、これらのイベントを組み合わせることで実装可能になるわけです。

なお、前後しますがフェードアウト処理の直前にあるzone.removeInteractive();は、ゲームオブジェクトにsetInteractive()で設定した「クリック可能」という状態を解除します。なんでこれが必要かというと、フェードアウトし終わる前に再度クリックされた場合、再びフェードアウト処理が実行されてしまうからです。つまりクリックを連打することで、後続処理へと遷移しなくなってしまいます。その事態を避けるため、一度クリックされたらゲームオブジェクトに対して設定されている「クリック可能」という状態を解除し、複数回クリックされないようにしてフェードアウト処理が実行されるのを1回きりに制限しています。

フェードイン

this.cameras.main.fadeIn(1000, 0, 0, 0);

上記の部分が、SecondSceneをフェードインさせているコードです。scene.cameras.main.fadeIn()の引数構成はscene.cameras.main.fadeOut()と同一です。こちらもRGBでフェードインする色を指定できるので、フェードアウトする色と同じ色を指定することでシームレスなシーン遷移が実装できます。

第5引数のコールバック関数に関しても、やはり同様です。フェードインしている間ずっとフレームごとに呼び出されます。フェードインしてから呼び出されるものではないので注意。そして、フェードインが完了したときに処理を行いたければ、FADE_IN_COMPLETEイベントを検知すればいいわけです。こちらもやはり同様ですね。

サンプル

See the Pen Change scene with fade in Phaser3 by ysko909 (@ysko909) on CodePen.

実際にCodepen上に実装してみました。クリックするとフェードアウトが発動して、フェードアウト完了時にシーンの遷移が行われます。遷移先のシーンではフェードインが発動して、遷移先のシーンがじわじわと表示される挙動を確認できると思います。

まとめ

今回はPhaser3のシーン遷移について、フェードアウトとフェードインを導入して実装してみました。

フェードアウトもさることながら、フェードインに関しても「こんなに簡単に実装できるの?」というくらい全然記述してないですね。ホントにPhaser3様様です。

参考

Phaser.Cameras.Scene2D. Camera

Phaser.Cameras.Scene2D. Events

Scene Transition with Fade Out in Phaser 3

Creating Spelunky-style level transitions in Phaser

Phaser.GameObjects. GameObject

comments powered by Disqus