【Unity】WebGLでピンチズームが効かない原因と解決法|touch-action:none 一行で直る

投稿日: 2026年6月23日
対象読者: Unity WebGLをスマホ対応させていて、2本指ピンチが効かずに困っているエンジニア

この記事はこんな方向け:

  • Input.touchCount == 2 のピンチ処理を書いたのに、スマホで反応しない方
  • ピンチすると3Dビューではなく、ブラウザのページ全体がズームしてしまう方
  • 1本指のスワイプは効くのに、2本指だけ挙動がおかしい方

TL;DR

  • Unity側のピンチ処理が正しくても、ブラウザが2本指ジェスチャを先取りすると反応しない
  • 原因は「ブラウザのデフォルトのタッチ動作(ページのピンチズーム)」がイベントを奪っているから
  • 解決はC#ではなくCSS側。#unity-canvastouch-action: none; を1行足すだけ
  • viewportの user-scalable=no とは役割が違う。canvasに渡したいなら touch-action を使う

はじめに

PoseMirror(写真からポーズを3D化する無料ツール)をスマホ対応させたとき、最後まで残ったのがこのピンチズームの不具合でした。1本指スワイプでの回転は問題なく動くのに、2本指でズームしようとすると——3Dモデルは拡大されず、代わりにページ全体がぐにゃっと拡大される。Unity側のピンチ処理は何度見直しても正しい。原因はUnityの外、ブラウザ側にありました。

この記事では、その原因と、たった1行のCSSで直った解決法をまとめます。

症状:Unity側のコードは正しいのに反応しない

まず、Unity C#側のピンチ処理はごく標準的なものです。これ自体に問題はありません。

void Update()
{
    // スマホ: 2本指ピンチでズーム
    if (Input.touchCount == 2)
    {
        Touch t0 = Input.GetTouch(0);
        Touch t1 = Input.GetTouch(1);

        Vector2 t0Prev = t0.position - t0.deltaPosition;
        Vector2 t1Prev = t1.position - t1.deltaPosition;
        float prevDist = (t0Prev - t1Prev).magnitude;
        float currDist = (t0.position - t1.position).magnitude;

        float pinchDelta = currDist - prevDist;
        Camera.main.fieldOfView =
            Mathf.Clamp(Camera.main.fieldOfView - pinchDelta * 0.05f, 20f, 80f);
    }
}

PCのエディタ上では当然ピンチがないので確認できず、スマホ実機で開いて初めて「効かない」と気づくのが厄介なところです。しかも1本指スワイプは効くので、コードのロジック自体を疑ってしまい、原因の切り分けに時間がかかりました。

原因:ブラウザが2本指ジェスチャを横取りしている

結論から言うと、Unityは悪くありません。Input.touchCount2 にすらなっていないのです。

スマホのブラウザは、2本指のタッチを「ページのピンチズーム(拡大縮小)」というブラウザ標準のジェスチャとして優先的に処理します。このとき、タッチイベントはcanvasまで届かずにブラウザに消費されてしまいます。Unityから見ると「2本指のタッチが来ていない」状態になり、いくら正しいピンチ処理を書いても発火しません。

1本指スワイプが効いていたのは、1本指のドラッグはブラウザのデフォルトジェスチャと衝突しにくいからです。「1本指は効くのに2本指だけ効かない」という症状が、ブラウザがジェスチャを奪っているサインでした。

解決:canvasに touch-action: none を1行

直し方はC#ではなくCSSです。Unity WebGLの描画先である #unity-canvas に対して、ブラウザのデフォルトのタッチ動作を無効化します。

#unity-canvas {
  width: 100% !important;
  height: 100% !important;
  display: block;
  object-fit: contain;       /* 歪みを防止 */
  touch-action: none;        /* モバイルのピンチをUnityへ渡す(ページズームに奪わせない) */
}

touch-action: none; は「この要素の上では、ブラウザはスクロールもピンチズームも一切やらない」という宣言です。これでブラウザがジェスチャを横取りしなくなり、2本指のタッチがそのままcanvas経由でUnityに届くようになります。Input.touchCount が正しく 2 を返し、最初のC#コードがそのまま動き出します。

PoseMirrorでは、まさにこの1行を #unity-canvas に追加しただけでピンチズームが復活しました。

補足:user-scalable=no とは役割が違う

「ページのズームを止めたいなら、viewportの user-scalable=no でいいのでは?」と思うかもしれません。これは別物なので注意が必要です。

<!-- ページ全体のズームを禁止する(ページ単位の設定) -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
設定 効果 用途
user-scalable=no(viewport) ページ全体のユーザーズームを禁止 サイト全体でズームさせたくない時
touch-action: none(CSS) 指定要素のブラウザ標準ジェスチャを無効化し、イベントを要素に渡す canvasだけにタッチを渡したい時

user-scalable=no は「ズームを禁止する」だけで、タッチイベントをcanvasに渡してくれるわけではありません。またページ全体のズームを奪うとアクセシビリティ上の弊害もあります。canvasの中だけでピンチを使いたいなら、対象要素に絞って効く touch-action: none が正解です。PoseMirrorではviewportは initial-scale=1.0 のまま(ズーム自体は許可)で、canvasにだけ touch-action: none を当てています。

まとめ

  • Unity WebGLでピンチが効かないとき、まずUnity側ではなくブラウザ側を疑う
  • 2本指ジェスチャはブラウザがページズームとして横取りし、canvasまで届かない
  • #unity-canvastouch-action: none; を足すと、タッチがUnityに渡りピンチが復活する
  • viewportの user-scalable=no とは役割が違うので混同しない

スマホ対応全体(解像度・メモリ・Brotliなど)でつまずいている方は、あわせてUnityのWebGLをスマホ対応させる4つのポイントもどうぞ。実際にこの対応を施したツールは、写真からポーズを3D化する Pose Mirror で実機で触れます。