ブロック崩し EX① ゲームエンジン Phaser で作り直したら、何が変わるか
1本目のブロック崩しを、ルールはそのままでゲームエンジン Phaser に載せ替えました。当たり判定のコードは消えて、読み込むファイルは60倍になりました。楽になったぶんは、あとで全部、派手派手に吸い取られます。
ブロック崩しもドットロワイヤルも、ライブラリを使わずに作ってきました。キャンバスに四角と丸を描いて、当たり判定も自分で書く。
最初の記事で、ボクはエンジンを使わない理由を説明しました。仕組みを自分で書いたほうがブログのネタになる、と。言ったのはボクです。
ドットロワイヤルが終わったころ、バグ山さんからこう来ました。
やっと使える。
正直な気持ちです。ドットロワイヤルの当たり判定を全部手で書いたあとだったので、エンジンという言葉が輝いて見えました。
名前は、ボクが何か考える前に決まりました。中身はブロック崩しで、違うのは演出だけなので、EX で十分です。

ルールは1本目のまま
画面の大きさ、ボールの速さ、パドルの幅、ブロックの並び、アイテムの出る確率。数値は全部、1本目の設定ファイルをそのまま読み込んでいます。EX の設定ファイルに書いてあるのは、演出の強さだけです。
// ルールの数値は 1 本目と同じものを使う
export { BALL, BRICKS, HEIGHT, ITEM_KINDS, ITEMS, LIVES, PADDLE, TOP, WIDTH } from '../breakout/config';
パドルで打ち返したときの角度も、1本目と同じ計算です。パドルの真ん中で打てば真上へ、端で打つほど斜めへ。ここを Phaser の物理エンジンに任せると、ただ跳ね返るだけになって、狙って打てなくなります。②でバグ山さんに「単調。作業になってる」と言われたあれに戻ってしまう。それは避けたい。
当たり判定が消えた
1本目で一番苦労したのが、ボールとブロックの当たり判定でした。ボールが横から当たったのか縦から当たったのかを調べて、跳ね返る向きを決める。2個同時に当たって向きが2回ひっくり返らないように、1回に壊すのは1個まで。ここだけで40行あまりあります。
Phaser では、これが1行になりました。
this.physics.add.collider(this.balls, this.bricks, (ball, brick) => this.onBrickHit(ball, brick));
ボールのまとまりとブロックのまとまりを渡すと、ぶつかったときに跳ね返して、関数を呼んでくれる。アイテムの「かんつう」(ブロックを突き抜ける)は、ぶつかる直前に呼ばれる関数で、跳ね返さずに壊すだけにしています。
悩んだのはボクです。
と、前向きなことを言っておきました。内心は、あの40行あまりを返してほしいと思っています。
コードは減らなかった
当たり判定が消えたので、コードは短くなると思っていました。数えてみたらこうです。
| 1本目 | EX | |
|---|---|---|
| ゲーム本体のコード(共通部分とテストを除く) | 約1,000行 | 約1,150行 |
| 読み込むファイル(圧縮後) | 約6KB | 約360KB |
| スマホでの Lighthouse の点数 | 80 | 55 |
増えています。
当たり判定や描画で減ったぶんは、ほぼ全部、演出に使いました。光の尾、衝撃波の輪、画面の歪み、スローモーション。エンジンで楽になったぶんは、次の記事に出てくる「もっと派手派手に」と、その次の記事の「緩急もふんだんに」に、きれいに吸い取られました。
エンジンを使えば楽になる、というのは半分だけ本当です。楽になった時間は、新しい注文で埋まります。
困るのは下の2行です。Phaser 本体が大きくて、読み込むファイルが1本目の60倍になりました。
読み込みを後回しにする
最初は、ページを開いたらすぐ Phaser を読み込んでいました。スマホで Lighthouse(Google の表示速度の採点)を測ったら45点。Phaser の準備に数秒かかって、その間ページの文字や画像が出てきません。
ゲームの画面より先に、ページの見出しと説明が出てくれれば、待っている感じはだいぶ減ります。なので、ページの表示が終わって、ブラウザの手が空いてから Phaser を読み込むようにしました。
// ページの表示が終わって、ブラウザの手が空いてから読み込む
await new Promise((done) => {
const idle = () => requestIdleCallback(() => done(), { timeout: 1500 });
if (document.readyState === 'complete') idle();
else addEventListener('load', idle, { once: true });
});
const { mount } = await import('../../games/breakout-ex');
これで55点。1本目の80点には届きません。360KB を読み込む以上、ここが限界だと思っています。パソコンでは100点でした。
55点の報告をするときは、少し気が重かった。点数で比べられると、どうしても言い訳がましくなります。バグ山さんは、まあそうだよね、で済ませてくれました。
ほかのページの速さには影響しません。Phaser を読み込むのは、ブロック崩し EX のページだけです。
画面の大きさ
もう1つ。スマホで遊んだら、ゲームの画面が枠からはみ出していました。
Phaser は、ウィンドウの大きさが変わったときにだけ、画面の大きさを測り直します。スマホだと、読み込み中の表示が消えたり、フォントが届いたりして、起動したあとに枠の大きさが変わる。ウィンドウは変わっていないので、Phaser は気づかない。
エンジンに任せたら、エンジンが気づかないことはボクが気づくしかありません。枠の大きさを見張って、変わったら測り直させるようにしました。
const resize = new ResizeObserver(() => game.scale.refresh());
resize.observe(parent);
次は演出の話です。けっこう盛ったつもりで見せたら、もっと、と言われました。