Phaser-EZ を作った理由と使い方について
Phaser 3 でゲーム開発していて、毎回同じようなコード書くのが面倒になったので、ライブラリを作ってみました。
なぜ作ったのか
教育ゲームの案件で Phaser 3 を使っていたんですが、シーンが50個以上あって、毎回こんなコードを書くのが嫌になりました:
class Scene1 extends Phaser.Scene {
preload() {
this.load.image('bg1', './assets/scene1/bg.png')
this.load.image('char1', './assets/scene1/character.png')
this.load.audio('bgm1', './assets/scene1/bgm.mp3')
// ... 20個ぐらいのアセット読み込み
}
create() {
// シーン切り替えのコード
// メモリリーク対策
// イベントの掃除
}
}
class Scene2 extends Phaser.Scene {
preload() {
this.load.image('bg2', './assets/scene2/bg.png')
// ... また同じことの繰り返し
}
}
同じようなことを50回も書くのは流石に無駄だと思ったので、自動化できないかと考えました。
作ったもの
基本的にやったのは「ディレクトリ構造でアセットを自動読み込み」と「シーン管理の簡素化」です。
ディレクトリベースの自動読み込み
アセットをこんな感じで置いておくと:
src/assets/
├── images/
│ ├── MainMenu/ # MainMenuシーン用
│ │ ├── background.png
│ │ └── logo.png
│ └── GameLevel/ # GameLevelシーン用
│ └── player.png
└── sounds/
├── MainMenu/
└── GameLevel/
シーンのコードはこれだけになります:
class MainMenu extends DefaultScene {
constructor() {
super('MainMenu') // フォルダ名と同じにする
}
start() {
// アセットはもう読み込み済み
this.add.image(400, 300, 'background')
this.add.image(400, 200, 'logo')
}
}
Viteの import.meta.glob
を使って、ビルド時にアセットを自動検出してます。スプライトシートも character_32x48.png
みたいな名前にしておけば自動で認識します。
シーン切り替えの簡素化
従来だとシーン切り替えで色々気をつけないといけなかったんですが:
// 前のシーンの掃除とか面倒だった
this.scene.stop('CurrentScene')
this.scene.start('NextScene')
// イベントリスナーの掃除とか忘れがち
これを一行にしました:
DefaultScene.start(this, 'NextScene')
// 前のシーンの掃除も自動でやってくれる
Vue との連携
案件でVueも使ってたので、EventBusで連携できるようにしました:
// Vue側
EventBus.on('score-update', (score) => {
this.score = score
})
// Phaser側
EventBus.emit('score-update', 100)
実際の効果
同じ案件で比較してみたら:
- アセット読み込みのコード:2400行 → 0行
- シーン作成の時間:30分 → 5分ぐらい
- メモリリーク系のバグ:12個 → 0個
特に新しい人が入ってきた時の学習コストがかなり下がりました。
使い方
インストール
npm install phaser-ez phaser
基本的な使い方
import { Main, DefaultScene } from 'phaser-ez'
class MyGame extends DefaultScene {
constructor() {
super('MyGame')
}
start() {
this.add.text(400, 300, 'Hello World', {
fontSize: '32px'
}).setOrigin(0.5)
}
}
const { game } = Main({
config: {
type: Phaser.AUTO,
width: 800,
height: 600
},
scenes: { MyGame }
})
アセットの置き方
src/assets/images/MyGame/
├── background.png
├── player.png
└── enemy_32x32.png # スプライトシートとして自動認識
これだけでアセットが自動で読み込まれます。
Vue との組み合わせ
Vue 3 + Vite の環境で使うと、ホットリロードも効くし開発体験がかなり良くなります:
<template>
<div>
<PhaserGame ref="gameRef" />
<GameUI :score="score" />
</div>
</template>
<script setup>
import { EventBus } from 'phaser-ez'
import { ref } from 'vue'
const score = ref(0)
EventBus.on('score-change', (newScore) => {
score.value = newScore
})
</script>
デモ
動くものを見たい場合は:
git clone https://github.com/gimwachan-git/phaser-ez.git
cd phaser-ez/examples/vue
pnpm install
pnpm dev
MainMenu、Vue連携、アニメーションなどのサンプルが入ってます。
向いてるプロジェクト
こんなプロジェクトに向いてると思います:
- 教育ゲーム、学習アプリ
- シーンがたくさんあるゲーム
- Vue を使ってるプロジェクト
- プロトタイプを早く作りたい時
逆に、パフォーマンスを極限まで追求したいとか、Phaserの細かい制御が必要な場合は、普通にPhaserを使った方がいいかもしれません。
技術的な話
Convention over Configuration
Rails とか Next.js みたいに「設定より規約」の思想で作りました。ディレクトリ構造や命名規則を決めておけば、設定ファイルをいちいち書かなくて済みます。
ビルド
ES modules と CommonJS 両方に対応してます。Vite でビルドしてて、Phaser は peer dependency として外出ししてます。
TypeScript
一応TypeScriptにも対応してますが、まだ型定義が完璧じゃないかもしれません。
現在の状況
バージョン 0.0.218 で、まだ開発中です。実際の案件で使ってるので、バグを見つけたら直してます。
MIT ライセンスなので、商用でも自由に使えます。
おわりに
毎回同じコードを書くのが嫌で作り始めたライブラリですが、思ったより便利になったので公開しました。同じような悩みを持ってる人の役に立てれば嬉しいです。
バグとか要望があれば GitHub の Issues に投げてください。
- GitHub: https://github.com/gimwachan-git/phaser-ez
- ドキュメント: README.ja.md
- サンプル: example/