HTMLでモグラたたきゲームをつくりました

燃え上がれ！
時間内にマスコットをひたすら集める、シンプルなゲームです

<img width="480" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/846293/0ec5f687-88fa-bcd2-32f3-ba84937c575a.png">

今年もミニゲームを公開する季節がやってきました。
今回作ったのはモグラたたき風のゲーム。

モグラたたきと言えば、ゲーム作りの入門編とも言えるたいへんミニマムなゲームです。
それはもうミニマムに作って。サッとお出しするつもりでした。

ササッ（半月遅れ）

:::note warn
そこそこ音が出ます
:::

https://hasegawa-campfire.github.io/fire-max/

色々ありました。そんな諸々はnoteにまとめて。
こちらはいつものように知見あつめです。

https://note.com/8kg/n/ne8a46b9264e3

## 技術概要

今年のテーマは原点回帰！
回帰もなにも、まだ原点のうしろに2つしかないような気もしますが！

ともあれ**HTML**です。WebGLでなければCanvasですらない。
HTMLでマークアップ。CSSで装飾＆アニメーション。JavaScriptは添えるだけ。
実にピュアなWebページ。

なお。毎度のようにライブラリ利用はありません。
今回はランキングもないので本当に何もなし。
ビルドせずに、すぐそのまま食べられます。

※ローカルサーバーは別途ご用意ください
※公開にあたっては諸々ビルドしています

### Web Components

お元気ですか。ウェブコンポーネントです。
Web標準でタグ名を定義したりスタイルを閉じ込める、技術的 ~~4本~~ 3本柱。

今回、作りとしては普段見慣れたようなコンポーネント指向になっています。
そのため全てがカスタム要素で、シャドウDOMで、HTMLテンプレート！
使いたかっただけです。特に深い理由はありません。

### HTML Modules

テンプレート。スクリプト。スタイル。
それらをまとめたコンポーネントファイルを実現する方法は様々です。
今回はhtmlファイルをコンポーネントとして利用する形にしました。
当たり前ですが、ごく普通のHTMLファイルなので大変素直に扱いやすい。

そんなHTMLコンポーネントファイルを読み込むのに便利なのがHTML Modules。
文字通り、HTMLファイルをモジュールとしてインポートできます。

できます。と言いつつまだ利用できる段階ではありません。
ずっと待っています。お元気ですか？

今回ビルドにおいてはプラグインで吸収しています。
またローカルにおいてはServiceWorkerでそれらしく実現しています。

### Web Animation API

CSSアニメーションをスクリプトで実現するためのAPI。
ですが、今回はむしろCSSアニメの制御に利用しています。

と言うのも今回、ゲーム内の動きはその全てがCSSアニメ。
スクリプトはせいぜい、その開始と終了を指示する程度。

それだけならAPI使わなくてもよくない？　と思うかもしれません。
その通りなんですけどね。classの付け替えでも十分に事足ります。

ただ今回なんとなくDOMの変更は避けたくて利用しています。
実際、開発中盤まではゲーム中にDOM変更が一切ありませんでした。
今もCSS変数の書き換えくらいでしょうか。

ともあれ、ここで言いたい事はひとつだけ。
動きの記述は、スクリプトよりスタイルの方が遥かに快適ということです。

### CSSアニメーション

ついでに。昔から様々なところで書かれていることですが、実感したので改めて。
アニメーションさせる上でtransformは最適化されていて本当に軽いです。正義です。

次いでopacity。辛うじてfilter。
基本的にはこの辺りでアニメーションは済ませられると良さそうでした。
少なくとも数が多いとか、広い面積が変わるとか、そんな箇所は。

今回だとwidthやcolorの変化が思いのほか重くなりがちでした。
あのタイトルロゴ。実はタイトル背景の大量キャラよりも、ゲーム本編よりも重かった。
colorを変化させていただけなんですが。

色の変化なら、色の異なる2枚を重ねてopacity変化させた方が数倍軽くなります。
ゲージの幅も、widthよりtransform（scaleやtranslate）を利用しましょう。

### hue-rotate()

更に余談なんですが。
CSSのfilterとして用意されている`hue-rotate()`。あれが中々の曲者でした。
当初、キャラ画像などの色パターンはそれで済ませようと思っていたんです。

しかし実際に変化させると……何か想定と違う？　いや、かなり暗いような？
具体的には下の画像。

<img width="560" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/846293/14f691d4-8a2e-98e0-6f15-e5a5ba2457eb.png">

左の赤に対して、色相を180度変化させた期待値が真ん中の明るいシアン。
しかし実際に表示されるのは右の淀んだ青緑。

そもそもhue-rotateの理解が違ったのか。色相を変えるだけじゃあないのか。
そう思って出てきたのが下の回答。
色相を変えるものという理解は合っているようだけど。

https://stackoverflow.com/questions/19187905/why-doesnt-hue-rotation-by-180deg-and-180deg-yield-the-original-color/19325417#19325417

翻訳機能頼りで自信はないですが。
要するにHSLではなくRGBのまま擬似的に算出しているので誤差も已む無し。とのこと。
つらい。

### CSS変数

おまけ。動的に値を変えられることで大変便利なCSS変数。
ただこれ変更にはスタイルと同じくstyle属性の書き換えが発生します。
しかし実はWeb Animation APIを使えば値だけの変更も。例えばこんな。

```js
el.animate({ '--hoge': '100px' }, { fill: 'forwards' })
```

ただし、この値はCSSアニメーションなどいくつかの場面では使えないようです。

### will-change

要素の変化を予め伝えることでブラウザに身構えてもらう。そんな最適化仕様。
なのですが。今回はイマイチその効果も感じられなくて、正直しづらさがあります。

なにしろ手持ちの中でも最弱のRakuten miniでさえ変化がわからない。
3年前はたいへん効果的だったのに……。

憶測ですが。今回動きは全てがCSSで宣言されているアニメーションであり。
そうなると、初めから相応の最適化が済んでいるのかもしれません。

### CSS Containment

今ひとつ恩恵が感じられない。その2。
子孫のサイズ計算や表示など、様々な処理を明示的に省かせるという最適化。

例えばゲーム作りだとabsoluteが頻出するのでレイアウト省略が捗るとか。
基本的に大半の要素はペイント省略を付けられるのでは？　などなど。
改めてそれぞれの要素の立ち位置を認識して回る、その感じは地味に面白い。

しかし元から割とスムーズに動いているので何ともコメントし難い。
ざっくりなパフォーマンス計測だと今のところ差が見られないのも切ない。
ただ不要ならいつでも影響なく消せるのは良いですね。will-changeにしても。

### Web Audio API

いつもの。相変わらずユーザーアクションがあるまで音を出せないやつ。
ただ今回、本来音を出したかった時間を保持して、
鳴らせるようになったら本来の時間に合うようシークするという小ワザを仕込みました。
去年まで地味に困っていた部分だったので、もっと早くこの対処に気付きたかった所。

### PWA

オフラインでも動作したり。インストールが出来るようになったり。
そんなネイティブアプリのような振る舞いを得られるという、俗に言うPWA。
一応対応しました。
と言ってもマニフェストファイルとサービスワーカーを置くだけ。簡単ですね。

注意点は、ビルド出力されたファイルの一覧をサービスワーカーに渡す必要がある程度。
オフライン対応のためには全部キャッシュさせたいですからね。
今回はdist内のファイルを列挙してjsonとして保存。それをfetchさせています。

### 型

ここからは使わなかったもの。その代表が静的型付け。

世に名だたるVSCode＆TypeScript。
実はHTML内のインラインスクリプトについては型付けがあまり対応していません。つらい。
HTMLの標準仕様だというのに、そんなにも需要が無いんですね。

よって半端にjsファイルだけ対応するのも何なので、完全に排しています。

### CSS Container Queries

コンテナ要素を定義して、そのサイズを基準に子孫のスタイルを定義できるステキ仕様。

今回、せっかくのHTMLなのだからゲームでは中々拝めない本格レスポンシブをやるぞ！
見よ！　これがコンテナクエリだ！
と息巻いていたのですが。使いませんでした。

正確には、使ったけれど消しました。別にコンテナクエリが悪いわけではないんです。
使わなくても十分だと気付いてしまったんです。代わりはビューポート単位。
あと地味に画面リサイズの負荷が増える印象。想定しなくて良い話ですが。

という事で。画面幅が狭くても、画面高さが狭くても1画面に収めたい！
そんな貴方に `dvmin` おすすめです。気が抜けるほどお手軽カンタン。
その上で細部の調整を加えるとよいでしょう。特に垂直方向は大事。

### CSS Nesting

ついにCSSが標準でネスト可能に！　という事で使うつもりだったんですが。
気付いたら使ってませんでした。
あまり必要とする場面が無かったとも言えます。

### その他

スコア表記をCSSでくるくる回すつもりでした。が、見た目の問題で消えました。
実は下の記事はこれの下準備のつもりだったのです。

https://qiita.com/koichi-hasegawa/items/b4d9a9483ca09bcb619c

あと配色についても、アクセスの時間帯やダークモードで変えることを考えていました。
ソース上でも頑張って色の定義をまとめようとしていた形跡が残っています。
結局は時間不足＆色定義をまとめる難しさ＆配色が最後まで決まらず。で立ち消え。
悔しい。けど難しい。いつかアップデート対応するかもしれません。

## おわりに

モグラたたきゲームを作ろう！　と決めたのはかなり早い段階だったので。
今年は余裕だな！　と思っていました。半年以上前は。

なぜこんなことになってしまったんだ。
続きはnoteで。

ともあれHTMLで作ると決めたその時点で、
今年はいろいろ盛り込める（紹介できる）と分かっていたので、
そこは楽しい開発でした。来年は何を作ろうかな。

Webフロントはたのしいですね！
Webフロントはたのしいですよ！

## 補足

※ FIRE MAX リポジトリ

https://github.com/hasegawa-campfire/fire-max/

※ 2022 年の記事

https://qiita.com/koichi-hasegawa/items/af8a8cb962979836c6bc

※ 2021 年の記事

https://qiita.com/koichi-hasegawa/items/af96293c29d1f2474847

※ 2020 年の記事

https://qiita.com/koichi-hasegawa/items/1273eb88782e0f1d2835