スマホのジャイロでぷるぷる動くHTML5アプリを作った話:DeviceMotion × バネ物理の実装メモ

スポンサーリンク

「スマホを振ったら画面の球体がぷるんと揺れるアプリを作りたい」というリクエストをもらって、HTML5のDeviceMotion APIとバネ-ダンパー物理を組み合わせた実装を試みました。1ファイルのHTMLで完結し、GitHub Pagesで即公開できる構成です。詰まったポイントと最終的な設計をまとめます。

最初の実装:自由飛翔ボールは「ぷるん」しない

最初に作ったのは「ジャイロで重力方向が変わり、ボールが画面内を飛び回る」もの。物理的には動きましたが、求められていたのは「柔らかく体に固定されたものが揺れる」感触でした。この2つは物理モデルが根本から違います。

  • 自由飛翔モデル:重力に従って動き、壁で反発する自由粒子
  • バネ固定モデル:アンカー点にバネで繋がれた質点。引っ張ると戻り、揺れながら収束する

DeviceMotion APIの使い分け:傾きと振りは別のプロパティ

DeviceMotionEventには2種類の加速度が入っています。用途によって使い分けるのがポイントです。

window.addEventListener('devicemotion', e => {
  // 傾き検出 → accelerationIncludingGravity(重力込み)
  const ag = e.accelerationIncludingGravity;
  gravX = -(ag.x || 0) / 9.8;  // 右に傾けると正
  gravY = -(ag.y || 0) / 9.8;  // 下に傾けると正(縦持ち時)

  // 振り検出 → acceleration(重力除去済み)
  const a = e.acceleration;
  ball.vx -= (a.x || 0) * 7;   // 衝撃を直接velocityに加算
  ball.vy -= (a.y || 0) * 7;
});

傾きには重力方向の変化(accelerationIncludingGravity)、振りには純粋な加速度(acceleration)を使います。振りの衝撃は力として毎フレーム加算するより、velocityへの直接加算のほうがレスポンスよく感じられました。

バネ-ダンパー物理の実装

アンカー点に繋がれた質点の更新式はシンプルです。

const k   = 42;    // バネ定数(低いほどたぷんたぷん)
const c   = 5;     // 減衰(低いほど長く揺れる)
const gs  = 1100;  // 傾きゲイン

// バネ力 + 減衰 + 重力による傾き
const fx = -k * (x - ax) - c * vx + gravX * gs;
const fy = -k * (y - ay) - c * vy + gravY * gs;
vx += fx * dt;
vy += fy * dt;
x  += vx * dt;
y  += vy * dt;

パラメータの感覚値:k=35〜50でゼリー・肉系の柔らかさ、k=80〜120でゴム・スライム系。減衰比(c / (2 * sqrt(k)))を0.25〜0.35に設定すると、3〜5回揺れて自然に収束します。

iOSのセンサー許可:ボタンクリック後でないと動かない

iOSはDeviceMotionEventの使用にユーザー許可が必要で、ユーザーのジェスチャー(タップ等)に紐づいたコールバック内でないと許可ダイアログが出ません。ページロード時に呼んでも無効です。

startButton.addEventListener('click', () => {
  // ここで呼ぶのがポイント
  if (typeof DeviceMotionEvent?.requestPermission === 'function') {
    DeviceMotionEvent.requestPermission()
      .then(r => { if (r === 'granted') setupMotion(); });
  } else {
    setupMotion(); // Android・PCは許可不要
  }
});

PCデバッグ用のマウスフォールバック

スマホ実機がない環境でのデバッグに、マウス位置で重力方向をエミュレートするフォールバックを付けておくと便利です。

window.addEventListener('mousemove', e => {
  if (motionActive) return; // スマホでは無効化
  gravX = (e.clientX / W - 0.5) * 1.8;
  gravY = 0.4 + (e.clientY / H - 0.5) * 1.2;
});

まとめ

HTML5だけで「ジャイロ連動の柔らかい物理」は十分実装できます。要点は①傾きと振りでAPIを使い分ける、②自由粒子でなくバネ-ダンパー系にする、③iOSの許可はボタンコールバック内で取る、の3点です。1ファイルでGitHub Pages公開できるので、アイデアを試すのに向いている構成だと思います。

コメント

タイトルとURLをコピーしました