【Unity WebGL】Pose Mirrorのパフォーマンス改善3つのアプローチ
投稿日: 2026年3月3日 対象読者: Unity WebGLアプリの表示速度や操作感を改善したいエンジニア
この記事はこんな方向け:
- Unity WebGLがブラウザで重い・カクつくと感じている方
- どこから最適化すればいいか分からない方
- Pose Mirror の実装で行った改善内容を知りたい方
TL;DR
- まず 計測してどこが遅いかを特定する(闇雲に最適化しない)
- 重い処理は毎フレーム実行しない。同じ入力からの結果はキャッシュする
- JS ↔ Unity 間のデータ送信は必要なタイミングに限定する(往復コストが大きい)
はじめに
Unity WebGLで作ったアプリを公開して「思ったより重い…」と感じたことはありませんか?
Pose Mirror は、MediaPipe(JavaScript)で取得したポーズデータを Unity WebGL に送信しながらブラウザ上で操作する構成のため、表示速度と操作の軽さが体験品質に直結します。筆者はこのアプリを実際に運用する中で、「どの処理がボトルネックになっているか」を計測しながら改善を繰り返しました。この記事では、調整時に見ている観点と、実装時に意識している改善ポイントを整理します。
アプローチ①:まず計測してボトルネックを特定する
最も重要なのは計測なしに最適化しないことです。描画・データ変換・UI更新のどこに時間がかかっているかを切り分けることで、改善対象を誤りにくくなります。
function measureStep(label, fn) {
const start = performance.now();
const result = fn();
const elapsed = performance.now() - start;
console.log(`${label}: ${elapsed.toFixed(2)}ms`);
return result;
}
// 使用例:各処理の区間を計測する
const landmarks = measureStep('MediaPipe解析', () => runPose(image));
const payload = measureStep('座標変換', () => convertToUnity(landmarks));
measureStep('Unity送信', () => sendToUnity(payload));
このように区間ごとの計測を入れておくと、UI側の変更で遅くなったのか、姿勢データの変換処理で遅くなったのかを把握しやすくなります。
アプローチ②:処理頻度・データ量を減らす
ボトルネックが特定できたら、以下の観点で対策します。
- 重い処理を毎フレーム実行しない:例えばMediaPipeの解析はユーザーのボタン操作時のみ実行し、描画ループ内では行わない
- 同じ入力から得られる計算結果は再利用する:座標変換結果をキャッシュし、入力が変わっていない場合は再計算しない
- DOM更新はまとめて行い、不要な再レイアウトを避ける:複数の要素の変更を一度に適用する
// NG: 毎フレーム重い処理を実行
function update() {
const pose = runMediaPipe(videoFrame); // 毎フレーム解析は重い
render(pose);
requestAnimationFrame(update);
}
// OK: ユーザー操作時のみ解析、描画は軽量に保つ
document.getElementById('btn-detect').addEventListener('click', async () => {
const pose = await runMediaPipe(uploadedImage); // ボタン押下時のみ
sendToUnity(pose);
});
アプローチ③:JS ↔ Unity 間の通信を最小化する
ブラウザ上のUIとWebGLコンテンツが同時に動く構成では、JS ↔ Unity 間の往復(SendMessage)のコストが思いのほか大きくなります。
- Unity WebGL 側に渡すデータは、必要なタイミングに限定して送る
- 送信データは必要な関節座標のみに絞る(全33点ではなく使う12点のみ)
- 送信頻度を下げる:60FPS 連続送信ではなく、変化があったときだけ送信する
let lastPayloadJson = '';
function sendIfChanged(payload) {
const json = JSON.stringify(payload);
if (json === lastPayloadJson) return; // 変化なければスキップ
lastPayloadJson = json;
unityInstance.SendMessage('PoseReceiver', 'ReceivePoseData', json);
}
個別の処理最適化よりも「不要な往復を減らす」ことが効く場面が Pose Mirror の開発では多くありました。
今後の改善候補
- 計測結果を収集しやすいログ導線を整える
- 端末性能に応じて処理頻度を切り替える(スマホは解析を間引く)
- 画像入力サイズの扱いを見直し、初期表示コストを抑える
まとめ
- 計測が最初:闇雲に最適化するより、どこが遅いかを
performance.now()で特定してから対処する - 処理頻度を下げる:毎フレーム実行が不要な処理はイベント駆動に切り替える
- JS ↔ Unity 通信を絞る:データ量・送信頻度の両面で「必要最小限」を意識する
Pose Mirror でもこの順序を守ることで、操作感を崩さずに改善を進められました。
ぜひ実際に Pose Mirror を動かして操作感を確かめてみてください!
関連記事:
- 【Firebase】Unity WebGLを無料で公開する手順 — デプロイ設定と読み込み速度の最適化
- 【Unity】WebGLをスマホ対応させる4つのポイント — モバイルでの速度改善