🏃

コードを書いて横スクロールで移動する画面を作る【Phaser3】

2022/12/18に公開

コードを書いて横スクロールで移動する画面を作る

この記事は、CoderDojo Advent Calendar 2022 - Adventar の17日目の投稿であり、昨年2021年4月に公開した記事、コードを書いて画面内のキャラクターを動かす の精神的続編です。

著:CoderDojo紫雲 増岡秀樹

概要

これは、Webブラウザに表示されているキャラクターをキーボードで操作し、画面内のステージを横スクロールさせるプログラムを書く、練習用のサンプルコードです。

Phaser3 Phaser - A fast, fun and free open source HTML5 game framework というWebゲームフレームワークを使用してJavaScriptでオブジェクトを操作するコードを書きます。

内容は、主に各地のCoderDojoに参加しているNinja向けとなっており、時間内でチュートリアルを終え、独自の応用にチャレンジできる程度の分量としています。

また、一部の説明を簡略化するために、プロパティ名などに漢字を含む日本語を使用しています。

目標

これを...

変更前

こうして...

編集中

こうじゃ!

完了

準備

CodeSandboxにあるサンプルコードを、Webブラウザで開いて編集します。

【サンプルコード】focused-water-1y4xor - CodeSandbox

CodeSandbox

Game.jsファイルが表示されていることを確認します。

今回は、このGame.jsファイルに書かれているcreate()関数の内容を編集します。

create() {
      // 画面の幅と丈を取得しています
      const 画面の幅 = this.scale.width;
      const 画面の丈 = this.scale.height;

      /*
       * 今回の任務は...
       *
       * Step.1 メインカメラがプレイヤー・キャラクターを追いかけるようにする
       * Step.2 ステージの境界を設定する
       * Step.3 空の背景を追加する
       * Step.4 木の背景を追加して奥行きを表現する
       *
       * Mission Start!
       *
       */

      /*
       * Step.3
       * 空の背景を追加する
       */

      /*
       * Step.3-a
       * 空の背景画像に渡す情報を定義する
       */

      /*
       * Step.3-b
       * 画面(this)に
       * 空の背景画像を追加(add.image)する
       *
       * Step.3-c
       * 背景の移動量を調整".setScrollFactor"して
       * 空の背景画像が動かないようにする
       */

      /*
       * Step.4
       * 木の背景を追加して奥行きを表現する
       */

      /*
       * Step.4-a
       * 木の背景に渡す情報を定義する
       */

      /*
       * Step.4-b
       * 画面"this"に
       * 木の背景画像を追加"add.image"する
       *
       * Step.4-c
       * 背景の表示位置を調整".setOrigin"する
       *
       * Step.4-e
       * 背景の移動量を調整".setScrollFactor"する
       */

      /*
       * Step.4-d
       * 木の背景画像を2枚追加して、
       * 水平に並べる"木の背景.X座標 * 1"
       *
       * Step.4-e
       * 背景の移動量を調整".setScrollFactor"する
       */

      // ここで既存の機能を呼び出しています
      this.前回までのあらすじ();


      /*
       * Step.1
       * メインカメラ"this.cameras.main"が
       * プレイヤー・キャラクター"this.player"を
       * 追いかけるように".startFollow"する
       */

      /*
       * Step.2
       * ステージの境界を設定する
       */

      /*
       * Step.2-a
       * このステージに渡す情報を定義する
       */

      /*
       * Step.2-b
       * メインカメラ"this.cameras.main"の
       * 範囲を設定".setBounds"する
       */

      /*
       * Step.2-c
       * このステージ"this.physics.world"の
       * 境界を設定".setBounds"する
       */

      /*
       * Mission Complete?
       */

  }

編集したファイルを保存するには、コントロールキーを押しながらSキーを押す【Ctrl+S】か、メニューバーから【File】を選択して【Save】をクリックします。

File Save

このサンプルコードは自由に編集しても大丈夫です。ページを再読込すると、元の内容に戻ります。

今回の任務は...

  • Step.1 メインカメラがプレイヤー・キャラクターを追いかけるようにする
  • Step.2 ステージの境界を設定する
  • Step.3 空の背景を追加する
  • Step.4 木の背景を追加して奥行きを表現する
  • Step.5 改造タイム

Step.1 メインカメラがプレイヤー・キャラクターを追いかけるようにする

Step.1

  /*
   * Step.1
   * メインカメラ"this.cameras.main"が
   * プレイヤー・キャラクター"this.player"を
   * 追いかけるように".startFollow"する
   */
   this.cameras.main.startFollow(this.player);

観察タイム

コードを書いて変化を観察します。

Step.1実行結果

今回使用した機能は...

startFollow

ゲームオブジェクトを追跡するようにカメラを設定します。

Phaser 3 API Documentation - Class: Camera

Step.2 ステージの境界を設定する

Step.1 でカメラがプレイヤーを追いかけるようになりました。
しかし、見えない何かに遮られて途中から先に進めません。
Step.2 では、ステージに境界を設定することで、プレイヤーがその境界内を動き回れるようにします。

Step.2

Step.2-a このステージに渡す情報を定義する

/*
* Step.2-a
* このステージに渡す情報を定義する
*/
const このステージ = {
    "X座標": 0,
    "Y座標": 0,
    "幅": 画面の幅 * 3,
    "丈": 画面の丈
};

Step.2-b メインカメラの範囲を設定する

/*
* Step.2-b
* メインカメラ"this.cameras.main"の
* 範囲を設定".setBounds"する
*/
this.cameras.main.setBounds(
    このステージ.X座標,
    このステージ.Y座標,
    このステージ.,
    このステージ.
);

Step.2-c このステージの境界を設定する

/*
* Step.2-c
* このステージ"this.physics.world"の
* 境界を設定".setBounds"する
*/
this.physics.world.setBounds(
    このステージ.X座標,
    このステージ.Y座標,
    このステージ.,
    このステージ.
);

観察タイム

変化を観察します

Step.2 実行結果

今回使用した機能は...

Camera.setBounds

カメラの境界を設定します。境界は軸に合わせた長方形です。

Phaser 3 API Documentation - Class: Camera

World.setBounds

指定されたワールド ピクセル寸法に一致するように物理ワールドの境界を設定します。

Phaser 3 API Documentation - Class: World

Step.3 空の背景を追加する

Step.2 ではステージに境界を設定し、プレイヤーがその中を動き回れるようにしました。
このStep.3 では、ステージに背景を設定し疾走感を表現します。

Step.3-a 空の背景画像に渡す情報を定義する

/*
* Step.3-a
* 空の背景画像に渡す情報を定義する
*/
const 空の背景 = {
    "X座標": 画面の幅 * 0.5,
    "Y座標": 画面の丈 * 0.5,
    "画像名": 'bg-sky',
    "移動量": 0
};

Step.3-b 画面に空の背景画像を追加する

/*
* Step.3-b
* 画面"this"に
* 空の背景画像を追加"add.image"する
*
* Step.3-c
* 背景の移動量を調整".setScrollFactor"して
* 空の背景画像が動かないようにする
*/
this.add.image(空の背景.X座標, 空の背景.Y座標, 空の背景.画像名);

Step.3-c 背景の移動量を調整して空の背景画像が動かないようにする

/*
* Step.3-b
* 画面"this"に
* 空の背景画像を追加"add.image"する
*
* Step.3-c
* 背景の移動量を調整".setScrollFactor"して
* 空の背景画像が動かないようにする
*/
this.add.image(空の背景.X座標, 空の背景.Y座標, 空の背景.画像名)
    .setScrollFactor(空の背景.移動量);

コードを追記していくときには、セミコロンやピリオドの位置に注意して記述しましょう。

観察タイム

変化を観察します

Step.3 実行結果

今回使用した機能は...

Scene.add.image

新しいイメージ ゲーム オブジェクトを作成し、シーンに追加します。

Phaser 3 API Documentation - Class: GameObjectFactory

Image.setScrollFactor

このゲーム オブジェクトのスクロール係数を設定します。 スクロール係数は、このゲーム オブジェクトに対するカメラの動きの影響を制御します。

Phaser 3 API Documentation - Class: Image

Step.4 木の背景を追加して奥行きを表現する

Step.3 ではステージに背景を追加し、スクロール時の疾走感を表現しました。
このStep.4では、空の背景とプレイヤーの間に木の背景を追加し、移動量を調整することでステージの奥行きを表現します。

Step.4-a 木の背景に渡す情報を定義する

/*
* Step.4-a
* 木の背景に渡す情報を定義する
*/
const 木の背景 = {
    "X座標": 800,
    "Y座標": 画面の丈,
    "画像名": 'bg-wood',
    "X位置調整": 0,
    "Y位置調整": 0.75,
    "移動量": 0.25
};

Step.4-b 木の背景を追加する

/*
* Step.4-b
* 画面"this"に
* 木の背景画像を追加"add.image"する
*
* Step.4-c
* 背景の表示位置を調整".setOrigin"する
*
* Step.4-e
* 背景の移動量を調整".setScrollFactor"する
*/
this.add.image(木の背景.X座標 * 0, 木の背景.Y座標, 木の背景.画像名);

Step.4-c 木の背景の表示位置を調整する

/*
* Step.4-b
* 画面"this"に
* 木の背景画像を追加"add.image"する
*
* Step.4-c
* 背景の表示位置を調整".setOrigin"する
*
* Step.4-e
* 背景の移動量を調整".setScrollFactor"する
*/
this.add.image(木の背景.X座標 * 0, 木の背景.Y座標, 木の背景.画像名)
    .setOrigin(木の背景.X位置調整, 木の背景.Y位置調整);

Step.4-d 新たに木の背景を追加して水平に並べる

/*
* Step.4-d
* 木の背景画像を2枚追加して、
* 水平に並べる"木の背景.X座標 * 1"
*
* Step.4-e
* 背景の移動量を調整".setScrollFactor"する
*/
this.add.image(木の背景.X座標 * 1, 木の背景.Y座標, 木の背景.画像名)
    .setOrigin(木の背景.X位置調整, 木の背景.Y位置調整);

this.add.image(木の背景.X座標 * 2, 木の背景.Y座標, 木の背景.画像名)
    .setOrigin(木の背景.X位置調整, 木の背景.Y位置調整);

Step.4-e 追加した全ての木の背景で移動量の調整を行う

/*
* Step.4-b
* 画面"this"に
* 木の背景画像を追加"add.image"する
*
* Step.4-c
* 背景の表示位置を調整".setOrigin"する
*
* Step.4-e
* 背景の移動量を調整".setScrollFactor"する
*/
this.add.image(木の背景.X座標 * 0, 木の背景.Y座標, 木の背景.画像名)
    .setOrigin(木の背景.X位置調整, 木の背景.Y位置調整)
    .setScrollFactor(木の背景.移動量);

/*
* Step.4-d
* 木の背景画像を2枚追加して、
* 水平に並べる"木の背景.X座標 * 1"
*
* Step.4-e
* 背景の移動量を調整".setScrollFactor"する
*/
this.add.image(木の背景.X座標 * 1, 木の背景.Y座標, 木の背景.画像名)
    .setOrigin(木の背景.X位置調整, 木の背景.Y位置調整)
    .setScrollFactor(木の背景.移動量);

this.add.image(木の背景.X座標 * 2, 木の背景.Y座標, 木の背景.画像名)
    .setOrigin(木の背景.X位置調整, 木の背景.Y位置調整)
    .setScrollFactor(木の背景.移動量);

観察タイム

変化を観察します

Step.4 実行結果

今回使用した機能は...

Image.setOrigin

このゲーム オブジェクトの原点を設定します。 値は 0 から 1 の範囲で与えられます。

Phaser 3 API Documentation - Class: Image

木の背景.X座標 * 0

ここでは、背景を水平に並べるために、X座標を0倍、1倍、2倍としました。

Step.5 改造タイム

今回書いたコードをおさらいします。

    /*
     * 今回の任務は...
     *
     * Step.1 メインカメラがプレイヤー・キャラクターを追いかけるようにする
     * Step.2 ステージの境界を設定する
     * Step.3 空の背景を追加する
     * Step.4 木の背景を追加して奥行きを表現する
     *
     * Mission Start!
     *
     */

    /*
     * Step.3
     * 空の背景を追加する
     */

    /*
     * Step.3-a
     * 空の背景画像に渡す情報を定義する
     */
    const 空の背景 = {
      X座標: 画面の幅 * 0.5,
      Y座標: 画面の丈 * 0.5,
      画像名: "bg-sky",
      移動量: 0
    };

    /*
     * Step.3-b
     * 画面(this)に
     * 空の背景画像を追加(add.image)する
     *
     * Step.3-c
     * 背景の移動量を調整".setScrollFactor"して
     * 空の背景画像が動かないようにする
     */
    this.add
      .image(空の背景.X座標, 空の背景.Y座標, 空の背景.画像名)
      .setScrollFactor(空の背景.移動量);

    /*
     * Step.4
     * 木の背景を追加して奥行きを表現する
     */

    /*
     * Step.4-a
     * 木の背景に渡す情報を定義する
     */
    const 木の背景 = {
      X座標: 800,
      Y座標: 画面の丈,
      画像名: "bg-wood",
      X位置調整: 0,
      Y位置調整: 0.75,
      移動量: 0.25
    };

    /*
     * Step.4-b
     * 画面"this"に
     * 木の背景画像を追加"add.image"する
     *
     * Step.4-c
     * 背景の表示位置を調整".setOrigin"する
     *
     * Step.4-e
     * 背景の移動量を調整".setScrollFactor"する
     */
    this.add
      .image(木の背景.X座標 * 0, 木の背景.Y座標, 木の背景.画像名)
      .setOrigin(木の背景.X位置調整, 木の背景.Y位置調整)
      .setScrollFactor(木の背景.移動量);

    /*
     * Step.4-d
     * 木の背景画像を2枚追加して、
     * 水平に並べる"木の背景.X座標 * 1"
     *
     * Step.4-e
     * 背景の移動量を調整".setScrollFactor"する
     */
    this.add
      .image(木の背景.X座標 * 1, 木の背景.Y座標, 木の背景.画像名)
      .setOrigin(木の背景.X位置調整, 木の背景.Y位置調整)
      .setScrollFactor(木の背景.移動量);

    this.add
      .image(木の背景.X座標 * 2, 木の背景.Y座標, 木の背景.画像名)
      .setOrigin(木の背景.X位置調整, 木の背景.Y位置調整)
      .setScrollFactor(木の背景.移動量);

    // ここで既存の機能を呼び出しています
    this.前回までのあらすじ();

    /*
     * Step.1
     * メインカメラ"this.cameras.main"が
     * プレイヤー・キャラクター"this.player"を
     * 追いかけるように".startFollow"する
     */
    this.cameras.main.startFollow(this.player);

    /*
     * Step.2
     * ステージの境界を設定する
     */

    /*
     * Step.2-a
     * このステージに渡す情報を定義する
     */
    const このステージ = {
      X座標: 0,
      Y座標: 0,
      : 画面の幅 * 3,
      : 画面の丈
    };

    /*
     * Step.2-b
     * メインカメラ"this.cameras.main"の
     * 範囲を設定".setBounds"する
     */
    this.cameras.main.setBounds(
      このステージ.X座標,
      このステージ.Y座標,
      このステージ.,
      このステージ.
    );

    /*
     * Step.2-c
     * このステージ"this.physics.world"の
     * 境界を設定".setBounds"する
     */
    this.physics.world.setBounds(
      このステージ.X座標,
      このステージ.Y座標,
      このステージ.,
      このステージ.
    );

    /*
     * Mission Complete?
     */

ここまでの4つのStepで、ステージの領域を任意の大きさで拡張し、プレイヤーがその中を自由に動き回れるようになりました。

今回の任務はこれで完了です。

思いつく限り、自由に改造してみてください。

実演動画

https://youtu.be/93eZcx2O4wc

What next?

参考文献

明日(12月18日)のアドベントカレンダーは...

CoderDojo Advent Calendar 2022 - Adventar

Discussion