恐竜本舗

エンジニアをしている恐竜の徒然日記です。

PhaserJSでパンを撃って敵を討つゲームを作った

Summary

  • パンを撃って敵を討つシューティングゲームを作ってみた
  • いつもお世話になっているPdMや社内に向けたネタ作だが、割とちゃんと作った
  • Googleログインでランキング機能を入れている

あそぶ

こちらから遊べます。キーボード操作が必要なため、PCブラウザでのみ遊べます。 推奨環境はChrome

  • URL

pan-shoot.fly.dev

github.com

Tech Stack

モチベーション

Viteの学習も兼ねて、ブラウザゲームを作ってViteでBuild した結果を、サーバから静的配信したい、というモチベーションでネタを探していた。

ちょこちょこ小ネタのゲームを作るので、今回も小ネタとして作ってみた。

感想もろもろ

Fly.io

個人開発の置き場を Heroku からどこに変えるかを悩んでいて、今回は Fly.io に置いてみた。

  • 無料枠内で DBサーバとして PostgreSQL も使える
  • flyctl でアプリの構築〜デプロイ〜管理すべてコマンドラインベースで勝手が良い
  • Dockerfileに記述しておき、Fly.io 上でDockerイメージを立ち上げてくれる

構成管理が toml なのが慣れないともやもやするが、簡単なサービスなら今後はこれで行こうかなと思った。

Stable Diffusion

今回のメインビジュアルは背景を Stable Diffusion で作ってみた。

ローカルやGoogle Colaboratory に置いて試すのが一般的だが、 mage.space というサービスで今回は作成した。

画像生成系モデルのAIについては全然詳しくないのだが、 Example も多いのでそのプロンプトを見つつ作ってみた。

いい感じのアラビアンな画像ができたと思う。

Google ログイン

もともとはViteでブラウザゲームをビルドしたいだけだったので Firebase Hosting あたりに置くつもりだった。

しかし、やっぱり展開するならランキング機能欲しいなという欲が出てきて、 Google ログインを突っ込むことにした。

Auth0や Firebase Authorication も検討したものの、アカウント情報を基本的には保持したくなかったため、IDと名前以外は持たないように内製した。

スコア保持

ゲームはすべてCanvas 上で動いている。

普段の仕事だとSPAのWEBアプリを作っているのだが、Canvas上での画面をまたぐ際の情報保持がイマイチ分からず苦戦した。

結局、CryptoJS を用いて暗号化して、SessionStorageに保持 → 画面遷移後に認証、スコアを複合→DB保存後、破棄 という手順を踏んだ。

https://github.com/daitasu/pan-shoot/blob/main/frontend/src/scenes/mypage.ts#L32-L46

もっといい方法がありそうだが、そんなに大事な情報でもないのでフロント側で管理することにした。

Static Server 配信のための src リプレイス

今回のVite側の静的ファイルのパス解決とGolangサーバ側のパス解決を合わせるのに中々詰まった。

今回のフロントエンドをビルドした結果は下記のようになる。

dist
└ assets
   └ main-xxxxxxxxx.js
└ images
   └ xxx.png # 各種の画像
└ index.html

このとき、

  • Golang側で静的ファイルサーバを / 直下には置かず、 /static 配下にしたかった
  • index.html から js を <script type="module" src="/static/assets/main-xxx.js"></script> のように取得したい
  • 画像群もまた、 href="/static/images/chocopan.ico" のようにして取得したい

という条件があるのだが、Golang側で /static/ を静的ファイルサーバとして配信すると、画像ファイルは正常に上記のパスになる。

一方、Viteでビルドした時点で index.html の script タグは src="/assets/main-xxx.js" となっており、ここにずれがあった。

prefix をつけると画像側のパスが今度はずれてしまうため、ここだけ個別書き換えるようにした。

pan-shoot/buildPlugin.ts at main · daitasu/pan-shoot · GitHub

これを vite.config.ts 上でプラグインとして入れ込むことで解决した。

import updateIndexHtml from "./src/scripts/buildPlugin";

export default defineConfig({
  // ...
  build: {
    // ...
    rollupOptions: {
      input: {
        main: resolve(root, "index.html"),
      },
    },
  },
  plugins: [updateIndexHtml()],
  server: {
    port: 9000,
  },
});