「スマホを振ったら画面の球体がぷるんと揺れるアプリを作りたい」というリクエストをもらって、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公開できるので、アイデアを試すのに向いている構成だと思います。

コメント