スマホ実機のタッチ座標ズレをどう直したか
VOID STRIKERはもともとキーボード操作だけを前提に作り、あとからタッチ操作を追加したブラウザゲームです。PCのブラウザやChromeのデバイスツールバーでは問題なく動いていたのに、実際のスマートフォンで開いてもらうと「タイトル画面のSTARTを押したつもりがHANGARに飛ぶ」「HANGARの項目をタップしたのに1つ下の項目が選択される」といった、タップした場所と実際の判定位置がズレる不具合が報告されました。これはエミュレータや開発者ツールのタッチシミュレーションでは再現しづらく、実機でしか発覚しない厄介な種類のバグでした。
症状の切り分け
まず疑ったのは判定ロジック側(HANGARの行のあたり判定やタイトルメニューのY座標)でしたが、コード上は特に間違っていませんでした。次に疑ったのがタッチ座標の変換処理です。VOID STRIKERは内部解像度480×640のcanvasを、実際の画面サイズに合わせてCSSで拡大縮小して表示しています。タップされたブラウザ座標(clientX/clientY)を、この拡大縮小率で割り戻して内部座標に変換する必要があるのですが、この「拡大縮小率」の算出方法に問題がありました。
当時は画面全体を包む要素に transform: scale(...)
を使って縮小表示していました。CSSのtransformは見た目のサイズだけを変え、レイアウト計算上の幅・高さ(要素が実際に占めるボックスサイズ)は変化させません。つまりブラウザの目には「480pxという、画面幅を超える大きな要素がある」ように映り続けます。スマートフォンのブラウザは、ページの内容が画面幅に収まらないと判断すると、ページ全体を自動的にズームアウトして収めようとする挙動を持っています。この自動ズームと、こちらが意図して適用していた
transform:scale
による縮小が二重にかかってしまい、実際に画面へ表示されているサイズと、JavaScriptが「表示サイズのはずだ」と認識している値がズレてしまっていたのです。
デバッグ用のオーバーレイを先に作る
実機でしか再現しないバグは、ログを見るだけでは原因を特定しにくいものです。そこで先に、URLに debugtouch
というクエリを含めると、タップした内部座標の位置に十字マーカーと座標の数値を約1秒間描画するデバッグ用オーバーレイを実装しました。これを使い、実機で「ここを押した」という自覚のある位置と、実際に記録された内部座標を見比べることで、ズレの方向と大きさを定量的に観測できるようになりました。原因の仮説を検証するための可視化ツールを先に作る、というのは今回に限らずこの手のバグ調査で有効なアプローチだったと感じています。
根本修正: 実寸レイアウトへの切り替え
最終的な修正は、transform:scale
をやめて、ゲームを包む要素(#frame)の
width/height
そのものを、計算した縮小率にもとづくピクセル数で直接指定する方式に切り替えることでした。具体的には
scale = min(画面幅 / 480, 画面高さ / 640) * 0.97
を求め、Math.round(480 * scale)px /
Math.round(640 * scale)pxを要素の実際の幅・高さとして設定します。こうすればブラウザから見てもレイアウト上のサイズと視覚的なサイズが完全に一致するため、画面幅を超えていると誤認されることがなくなり、ブラウザ側の自動ズームが発動する余地そのものがなくなります。タッチ座標の変換に使う
getBoundingClientRect()
の値も、この実寸レイアウトと完全に一致するため、二重縮小によるズレは根本的に解消されました。
もう一段階の修正: カーソルがタップに追従しない問題
座標ズレを直したあとも、もう一つ細かな不具合が残っていました。HANGARやCODE、タイトルメニューをタップすると、正しい項目が実行はされるのに、画面上のカーソル(ハイライト表示)が別の項目に残ったままになる、という表示上の不整合です。原因は単純で、タップ処理が「その場で該当項目を直接実行する」ことだけを行い、キーボード操作用のカーソル位置(titleCursorやhangarCursorなど)自体を更新していなかったためでした。修正としては、タップでヒットした項目に一度カーソルを移動させたうえで決定処理を行うようにし、見た目と実際の操作結果を一致させました。
学んだこと
この一連の修正を通して、「見た目の拡大縮小」と「レイアウト上のサイズ」を分けて考えることの大切さを再確認しました。CSSのtransformは便利な反面、レイアウトやタッチ座標変換のように「実際の占有スペース」を前提にした計算とは相性が悪い場面があります。モバイル対応のcanvasゲームでは、見た目の縮小と要素の実サイズを一致させておくことが、タッチ精度のバグを未然に防ぐ近道だと感じました。