Phaser3で作ったゲームを地動説でレスポンシブ対応

## はじめに
これはJavaScript用のゲームライブラリPhaser3を使って、ちょっとしたゲームをWebで公開する人向けの記事です。

## 実装の目的
「こんなゲーム思いついたんだけど、もし良かったら遊んでみて！」なんてときに、Webページとしてゲーム作品が公開できるのは気軽でとても良いのですが、ゲーム画面が横長だとiPhoneなどのスマホ画面では上下の余白が大き過ぎてしまうのが気になりました。

Chromeの開発者ツールで、iPhoneでの表示を試すとこんな感じ。

![スクリーンショット 2024-11-23 18.06.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393314/b793e30b-e726-6e04-8711-570955acdcee.png)

プレイヤーにスマホ画面の自動回転の設定を変えてもらったりしても良いのですが、ひと手間かかってしまいます。

そこで、余計な手間無しで気軽にゲームをプレイしてもらうために、ディスプレイが縦に長いときはゲーム画面を90度回転させてしまうことで画面に収めるというレスポンシブ対応をします！

![ezgif.com-video-to-gif-converter.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393314/62af918b-4b41-3320-0c21-84a62346dade.gif)



## 実装について
### 環境
以下の環境でコーディングと動作確認を行いました。
- 言語 JavaScript
- ライブラリ Phaser3.87.0
- ビルド
    - yarn 1.22
    - Vite 5.4

### レスポンシブ対応をしてないときのコード例

まず、例として背景と2つのキャラクターをPhaserのScene内で表示するコードです。
これを後でレスポンシブ対応します。

```JavaScript
    // 背景画像
    let board = this.add.image(0, 0, 'board')
    board.setPosition(board.width / 2, board.height / 2)

    let cardB = this.add.image(400, 200, 'card_b')
    let cardA = this.add.image(400, 360, 'card_a')
```

これを、下のような変更で90度クルッと回します！

```JavaScript

    // ここで画面回転のためカメラの中心点を設定する

    // 背景画像
    let board = this.add.image(0, 0, 'board')
    board.setPosition(board.width / 2, board.height / 2)

    let cardB = this.add.image(400, 200, 'card_b')
    let cardA = this.add.image(400, 360, 'card_a')

    /*
     以降で画面の向きに合わせてカメラを90度回転させる
     回転させたら、ゲームのキャンバスの縦横比も合わせる（game.scale.resize(x,y)）
    */
```

### 実際のコードについて

実際のコードの全体はGithubリポジトリのmain.jsを参照してください。

https://github.com/kysaeed/pxy

main.js : https://github.com/kysaeed/pxy/blob/main/main.js


#### 個々の処理の説明

最初に回転用のカメラ中心点を設定しています。

```JavaScript
    // 90度回転の中心点を設定
    this.cameras.main.originX = 0.5
    this.cameras.main.originY = (Constants.Screen.Height * 0.5) / Constants.Screen.Width
```

ゲーム画面を回転させる関数です。
`isRotate`が`true`なら横幅(width)と高さ(height)を逆転させる
（`false`の場合は回転の必要がないので、縦横はそのまま）

`this.cameras.main.setRotation(回転角)`で画面が回転します。


```JavaScript
    const setRotateState = (isRotate) => {
      if (isRotate) {
        // [|] 縦長スクリーンに表示
        game.scale.displaySize.setAspectRatio( Constants.Screen.Height/Constants.Screen.Width );
        game.scale.resize(Constants.Screen.Height, Constants.Screen.Width)
        this.cameras.main.setRotation(Math.PI * 0.5)
        game.scale.refresh()
      } else {
        // [--] 横長スクリーンに表示
        game.scale.displaySize.setAspectRatio( Constants.Screen.Width/Constants.Screen.Height );
        game.scale.resize(Constants.Screen.Width, Constants.Screen.Height)
        this.cameras.main.setRotation(0)
        game.scale.refresh()
      }
    }
```


縦画面か横画面かの判定をしています。
画面が横に長く使えさえすればOKなので、厳密なチェックはせずゲーム表示の親要素の縦横どっちが長いかで判定してしまいます。


```JavaScript

    const isVertical = () => {

      const game = window.game

      const w = game.scale.parentSize.width
      const h = game.scale.parentSize.height

      let isVertical = false
      if (w && h) {
        if (w < h) {
          isVertical = true
        }
      }

      return isVertical
    }

```

あとは、100ms以上リサイズされなくなるまで待って回転処理をします。

```JavaScript
    const fit = () => {
      /*
      // PCの場合は、ディレプレイの持ち方を変えないので回転しない
      if (game.device.os.desktop) {
        return
      }
      */
      setRotateState(isVertical())
    }

    let h
    const onResize = () => {

      if (h) {
        clearTimeout(h)
      }
      h = setTimeout(() => {
        fit()
      }, 100)
    }

```

また、初期表示のときも画面回転の向きが合うように、`fit()`を実行します。
```JavaScript
    // 初期化
    fit()
```

### 動作確認してみる

Chromeの開発者ツールで動作確認してみます。
レスポンシブ表示の確認表示で縦長のとき、うまく画面に収まりました:v:

![スクリーンショット 2024-11-23 17.50.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393314/ef4d77dd-75c1-9507-35c9-4252a697b7cc.png)



もともとスクリーンが横長で、回転がいらない場合はそのまま表示されます。


![スクリーンショット 2024-11-23 17.50.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393314/df808d88-3d8d-80c5-71d7-880ab4c65865.png)



### 実機でも動作確認

実機でも確認できるようにDEMOページを公開しました。

https://kysaeed.github.io/pxy/


FIT！
![IMG_0096.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393314/e50e311e-2325-e108-f6fd-20d83c71e27e.jpeg)


## 最後に
この方法は、文字の入力などが不要で画面のタッチ/クリックが多めのゲームに向いていると考えてます。
逆に、ゲーム表示を回転させているだけなのでDOM要素やソフトウェアキーボードを使いたい場合は向きません。
大作ゲームなら、アプリ化したりとかのほうがいいかも。