【TypeScript/Phaser.js】記号だけで戦うタイピングゲーム作った

[クソアプリ Advent Calendar 2021](https://qiita.com/advent-calendar/2021/kuso-app) 10日目、初参加です。よろしくお願いします！

# 経緯



**「すいません、この記号ってどうやって打つんですかぁ？」**って質問をたまにもらうので、そんな駆け出しエンジニアさんを **地獄に叩き落とす** クリスマスプレゼントを作る事にしました。


(ちょうど↓の記事で「Phazer.js」と言う2Dゲームフレームワークを知ったので、遊んでみたかったんです。)

https://dev.classmethod.jp/articles/phaser-js-typescript-vite/

# 完成品


![kigo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849081/879b9d40-3fd6-d8b8-107c-4f43ffc74769.gif)

ゲームは[こちら](https://umaxyon.github.io/MaiuBlog/services/kigoujigoku/)。
(僕はEasyの9840点が最高記録。1万点超えたい。)


ソースは[こちら(GitHub)](https://github.com/umaxyon/KigoShoot)に置きました。

戦艦は「[いらすとや](https://www.irasutoya.com/)」さん、岩は「[ぬれよん](https://nureyon.com/rock-1?pattern=4)」さんからお借りしました。
背景と弾は[Phazer.jsのチュートリアル](https://phaser.io/tutorials/making-your-first-phaser-3-game/part1)から流用。


# 所感

### Vite速ぇ

上記 [DevelopersIO](https://dev.classmethod.jp/articles/phaser-js-typescript-vite/) の記事に従って、Vite も初使用だったんですが、まずこれが便利で驚きました。

[Vite](https://dev.classmethod.jp/articles/phaser-js-typescript-vite/) は Webpack のようなフロントエンドのビルドツールですが、めちゃくちゃ速い！最初のプロジェクト作成も Vue や React、Typescript のインストール含めて一発で準備してくれるし、すごい快適でした。

ただ、今回の Phazer.js に Vite 使う場合は注意があって、Viteは 4k以下の小さな画像をbase64でインライン化する最適化を勝手に行うので、これを止めないと Phazer.js が WebGL や Canvas に取り込めず、開発時は見えていた画像が build するとリンク切れを起こす問題が発生します。

vite.config.ts に以下を設定して解決します。

~~~typescript
export default defineConfig({
  build: {
    assetsInlineLimit: 0
  }
})
~~~

Webpackと細かい違いは色々ありそうですが、Viteは今後使って行きたいです。


### Phazer.js についてざっくり

日本語ドキュメントが無いのが残念ですが、チュートリアルとExampleが充実していて、ソース追いかけるだけでも何となく理解できる程度に親切な感じなので、ゲーム以外でも Web上で何かアニメーションが必要な時とか、Canvasをゴリゴリいじる勉強するより楽だし、色々使えると思います。


具体的なところを簡単に書くと、

~~~typescript
const game = new Phaser.Game({   // 1.ゲームオブジェクトに色々設定して、
   type: Phaser.AUTO,
   parent: "phaser-game",    // 表示させるHTML上のdivとかのid
   width: 800, height: 600,  // 画面サイズ
   physics: {
     default: "arcade",
     arcade: {
          gravity: { y: 0 },  // 重力
     },
   },
   scene: [TitleScene, MainScene],  // 画面を表すシーン(自分で作る)
});
game.scene.start('titlescene')  // 2. 起動したいシーンのキーを指定してstart
~~~

Secneというクラスが画面や場面を表し、そのライフサイクルメソッドの中に処理を書いていきます。AndroidのActivity とかが近いのかな。

~~~typescript
class TitleScene extends Phaser.Scene {
    constructor() {
        super({ key: 'titlescene' })   // このシーンのキー名。この文字でGame内に登録される。
    }

    preload() {   // 画面ロード前
       this.load.image('sky', "画像パス")    // "sky"というキー名で画像をロード
       this.load.image('btn', "画像パス")    // ボタン画像
    }

    create() {    // 画面表示直前
        this.add.image(400, 300, "sky")     // "sky"で登録した画像を(400, 300)に配置
        this.add.image(280, 500, 'btn')
          .setInteractive()                 // イベント付ける場合はこれを呼ぶ
          .once('pointerup', () => {        // マウスアップ
             this.scene.start('mainscene')  // 3. ボタン押下でメインシーンを起動
          })
    }

    update(time: number, delta: number) {  // アニメーションフレームのループはここ。
    }
}
~~~

触りだけ説明ですが、こんな感じです。面倒な当たり判定処理とか、スプライトシートのアニメーションとかも簡単に出来るようになってるので、Example 見ながら色々調べてみると楽しいですよ。


### 今回のアプリについて

WinとMacのChrome、Edge、Safari で動作確認しています。IEは非対応。

最初、Winだけで動作確認していたんですが、公開直前にMacで確認すると効かないキーがある事が発覚。HTMLのイベントから取得出来るkeyCodeプロパティって、そういや非推奨だったのをすっかり忘れてました。

特にMacは「=」と「+」が同じコード返して来たり、「¥」が0を返して来たり、バグバグな状態が今でも互換のために残されてるんですね。公開直前にちょっと絶望しましたよ、、

今は keyCode プロパティではなく key プロパティを使うのが良いみたいです。


# 最後に

ショボいなりに遊べるので、クソアプリの主旨に合ってないと怒られないか少し不安もありつつですが、、まぁ記号が降ってくるだけのクソゲーってことで。

やり過ぎると指が死ぬので、鍛えたい人にもいいかもしれません。


それでは、ありがとうございました！