mcpField
H
hz-unity-meta-movement-sdk-retargeting

hz-unity-meta-movement-sdk-retargeting

github.com/meta-quest開発241147 回インストール5 週間前に更新
リポジトリ

hz-unity-meta-movement-sdk-retargeting について

Unity で Meta Movement SDK のキャラクター向けリターゲティング設定を生成・編集するためのスキルです。まずエディター API で初期 JSON を作成し、その後、関節マッピング、ウェイト、動作、T ポーズの値を必要に応じて調整します。

主な機能

  1. 01エディター API によるデフォルト設定の生成
  2. 02ターゲット関節の割り当て確認と修正
  3. 03関節の除外やマッピングウェイトの調整
  4. 04マッピング動作と T ポーズ値の編集
  5. 05関節名変更時に更新する箇所の案内

活用シーン

  1. 01自動検出された誤ったボーン割り当ての修正
  2. 02手のひらやツイスト関節による干渉の軽減
  3. 03キャラクターの接地位置や関節回転の調整
  4. 04標準外のボーン名を持つリグへの対応

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/meta-quest/agentic-tools --skill hz-unity-meta-movement-sdk-retargeting

インストール後、Claude Code で/hz-unity-meta-movement-sdk-retargetingを入力して読み込まれたか確認します。

同梱ファイルreferences/unity-mcp-fallback.md

詳細

MSDK リターゲティング設定

ワークフロー:まずヘッドレス API を使います

JSON を一から手作業で作成しないでください。ネイティブ側で初期アライメント、マッピング、T ポーズデータが計算されます。これらを手作業で記述するのは手間がかかります。次の手順で進めます。

  1. デフォルト設定を生成するには、unity-cli を使って、実行中の Editor に対して Meta.XR.Movement.Editor.MSDKUtilityEditor.RunDefaultRetargetingSetup(GameObject asset, string customDataSourcePath = null) を呼び出します。これは、すべてのデフォルト設定を使って Retargeting Configuration Editor UI を操作する(Next×3 → Validate → Done)ヘッドレス版で、次の 2 つの成果物が生成されます。
    • <asset>.json — リターゲティング設定(このスキルの対象ファイル)
    • <asset>-metadata.asset — モデルと JSON を関連付ける小さな ScriptableObject(編集が必要になることはほとんどありません)
  2. 生成された JSON を確認することで、ジョイントマッピングが適切か検証します。
  3. 必要な場合のみ手動で編集する — このスキルの残りの内容では、JSON の構造とよくある調整について説明します。

同じ RunDefaultRetargetingSetup API は、エディターメニュー項目 Assets/Movement SDK/Body Tracking/Run Default Retargeting Setup からも実行できます。プロジェクトアセットが選択されている状態であれば、ユーザーはメニューからも実行できます。

unity-cli からの呼び出し

run_script はロード済みのすべてのアセンブリを参照してコンパイルするため、Meta.* Editor 名前空間をそのまま参照できます。リフレクションは使わず、通常どおりメソッドを呼び出します。

// AgentScripts/RunRetargetingSetup.cs
using Meta.XR.Movement.Editor;
using UnityEditor;
using UnityEngine;

public static class RunRetargetingSetup
{
    public static string Run(string assetPath)
    {
        var asset = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
        if (asset == null) return $"ERROR: asset not found at {assetPath}";

        MSDKUtilityEditor.RunDefaultRetargetingSetup(asset, null);

        var jsonPath = System.IO.Path.ChangeExtension(assetPath, ".json");
        return $"done; config at {jsonPath} (exists: {System.IO.File.Exists(jsonPath)})";
    }
}
unity status --format json          # look for state "ready"
unity command run_script --file AgentScripts/RunRetargetingSetup.cs \
  --entry RunRetargetingSetup.Run --args '["Assets/MyChar/MyChar.fbx"]' --format json

.cs ファイルは Assets/ の外(パスはプロジェクトルートからの相対パス)に置いてください。書き込みによってインポートやドメインリロードが発生しないようにするためです。接続方法と --project-path については unity-cli スキルを参照してください。リフレクションを使う MCP 版については、references/unity-mcp-fallback.md を参照してください。

注意:

  • RunDefaultRetargetingSetup は非破壊的です。JSON がすでに存在する場合は、それを読み込んで各ステップを再実行します。ユーザーが行った target.knownJoints と source.autoMappingJointData の編集は保持されます(末尾の「設定の再実行」テーブルを参照してください)。
  • アセットは Assets/ の下、または埋め込みパッケージ内に配置する必要があります。変更不可のパッケージアセットに対して呼び出すと、Unity の「変更不可のパッケージへの変更を保存しますか?」というモーダルダイアログが表示され、リクエストがブロックされることがあります。まずアセットを Assets/ にコピーしてください。

JSON を手動で編集する場合

デフォルト設定は通常 80~95% の精度です。生成後に手動編集する一般的な理由は次のとおりです。

  • 自動検出で既知のジョイントに誤ったボーンが選ばれた(例:chest が脊椎のボーンに、wrist が手首のツイストボーンにマッピングされた)。
  • LeftHandPalm や *WristTwist など、ソース側のジョイントがターゲットの手のマッピングに混入してジッターが発生する。
  • モデルのボーンで標準外の命名規則(mixamorig:Hips、bn_pelvis_C_001 など)が使われており、ジョイント名を整理したい。
  • 特定のジョイントの weightPosition / weightRotation を手動で調整したい、またはマッピングの behavior を twist / childAlignedTwist に変更したい。
  • キャラクターが地面から浮く、または床にめり込む(T ポーズ時のルートの高さを修正)。
  • 縮小時にキャラクターが歪む(リグが大きすぎます。JSON ではなく FBX のインポートスケールを修正してください。「縮小時に歪むモデルを修正する」を参照)。

JSON は実行時のリターゲティングを制御します。同じスケルトン名の文字列が多くのセクションに使われています。すべて一貫して編集してください。そうしないと、ネイティブローダーがマッピングを破損したまま、エラーを通知せずに生成します。

トップレベルの構成

{
  "name": "<character name>",
  "config":  { version, coordinateSpace }
  "source":  { format, joints, manifestations, autoMappingJointData, knownJoints, hierarchy, tposeMin, tposeMax, tpose }
  "target":  { format, joints, knownJoints, hierarchy, tposeMin, tposeMax, tpose }
  "mapping": { min: { <targetJoint>: <mappingEntry> }, max: { ... } }
}
  • source — Meta VR ボディトラッキングのスケルトン(通常 OVR FullBody、約 84 ジョイント)。ジョイント名は固定です(例:LeftHandWrist、Hips)。通常は変更しません。
  • target — ユーザーのキャラクターリグ。ジョイント名はモデルのボーン階層に由来します(例:Left_Hand、Hips、Skeleton)。
  • mapping — 各ターゲットジョイントを駆動する、重み付きのソースジョイントのリスト。min は最小の身体スケール、max は最大の身体スケールを表します。リターゲッターは、ユーザーの身長に応じて実行時にその間をブレンドします。

関節名:同じ文字列を使用する必要がある箇所

関節名(例:"Right_UpperArm")がセクション間で一貫して参照されていないと、リグが正しく動作しなくなります。対象の関節の名前を変更または削除するときは、以下をすべて更新してください。

セクション内容
target.joints[]すべての関節名のフラットなリスト
target.knownJointsセマンティックな役割 → 関節名の対応(例:"hips": "Hips")
target.hierarchy"<child>": "<parent>" の対応
target.tpose、target.tposeMin、target.tposeMax関節名をキーとする関節ごとの位置と回転
mapping.min、mapping.maxトップレベルのキーは対象の関節名です。ネストされた target.mappings のキーも対象の関節名を参照します

ユーザーが最もよく調整する項目

1. 既知の関節のバインドを修正する(最も一般的)

自動検出では、間違ったボーンが選ばれることがあります(例:Right_Hand ではなく Right_HandTwist が選ばれる、または chest が胸部ではなく脊椎の関節に割り当てられる)。target.knownJoints を編集して修正します。

"target": {
  "knownJoints": {
    "root": "Skeleton",
    "hips": "Hips",
    "rightUpperArm": "Right_UpperArm",
    "leftUpperArm": "Left_UpperArm",
    "rightWrist": "Right_Hand",          // ← corrected
    "leftWrist": "Left_Hand",
    "chest": "UpperChest",                // ← corrected (was "Chest")
    "neck": "Neck",
    "rightUpperLeg": "Right_UpperLeg",
    "leftUpperLeg": "Left_UpperLeg",
    "rightAnkle": "Right_Foot",
    "leftAnkle": "Left_Foot"
  }
}

有効なキーは12個のみです(KnownJointType enum の root, hips, rightUpperArm, leftUpperArm, rightWrist, leftWrist, chest, neck, rightUpperLeg, leftUpperLeg, rightAnkle, leftAnkle に対応)。編集後は通常、アラインメントも再実行します。アセットに対して MSDKUtilityEditor.RunDefaultRetargetingSetup を再実行することをおすすめします(JSONを保持したまま、マッピングを再導出します)。

2. 自動マッピングから関節を除外する

自動マッパーがソース側のツイスト関節や手のひらの関節を、意図しない対象の関節に割り当てる場合に便利です。source.autoMappingJointData に追加します(対象側の関節を除外する場合は target.autoMappingJointData に追加します)。

"source": {
  "autoMappingJointData": {
    "LeftHandPalm":         { "excludeFromMapping": true },
    "LeftHandWristTwist":   { "excludeFromMapping": true },
    "RightHandPalm":        { "excludeFromMapping": true },
    "RightHandWristTwist":  { "excludeFromMapping": true },
    "LeftFootAnkleTwist":   { "excludeFromMapping": true },
    "RightFootAnkleTwist":  { "excludeFromMapping": true },
    "Some_Custom_Joint":    { "excludeFromTwistMappings": true }   // ← only twist mapping skipped
  }
}

AutoMappingJointFlags には次の2つのフラグがあります。

  • excludeFromMapping: true — マッピングの生成時に関節を完全に無視します
  • excludeFromTwistMappings: true — 関節は通常どおりマッピングされますが、behavior: "twist"/"childAlignedTwist" エントリーの生成時には除外されます

このオブジェクト内のキーの反復順序は非決定的です(ネイティブの順序なしマップ)。順序に依存しないでください。同じキーの集合であれば、設定は同等です。

3. 関節ごとのマッピングの重みを調整する

マッピングエントリーのキーには 対象の 関節名を使用し、各関節を動かす ソース 関節と、それぞれの weightPosition(0~1)および weightRotation(0~1)を列挙します。どちらも、兄弟関節間で合計が1.0になるように設定されていますが、実行時に正規化されるため、絶対値ではなく相対的な重みが重要です。

"mapping": {
  "min": {
    "Left_Hand": {
      "type": "source",
      "behavior": "normal",
      "mappings": {
        "LeftHandWrist": { "weightPosition": 1.0, "weightRotation": 1.0 }
      }
    },
    "Hips": {
      "type": "source",
      "behavior": "normal",
      "mappings": {
        "Hips":          { "weightPosition": 0.234, "weightRotation": 1.0 },
        "SpineLower":    { "weightPosition": 0.256, "weightRotation": 0.0 },
        "LeftUpperLeg":  { "weightPosition": 0.121, "weightRotation": 0.0 },
        "RightUpperLeg": { "weightPosition": 0.121, "weightRotation": 0.0 }
      }
    }
  },
  "max": { /* same shape, different weights for max body scale */ }
}

よくある調整:

  • ソース関節の影響を位置のみにしたい場合(またはその逆の場合)は、weightRotation を0に設定します。
  • mappings 辞書にソース関節を追加して、対象の関節に影響を与えます。
  • mapping.min と mapping.max は通常、同じ値または近い値です。迷った場合は同じ値に設定します。
4. マッピングの動作を変更する(normal/twist/childAlignedTwist)

behavior に指定できる値は次の3つです。

  • "normal" — 重み付きブレンドを直接適用します
  • "twist" — ソース側のツイスト関節用です。親の回転をツイスト関節まで引き継ぎます
  • "childAlignedTwist" — 対象側のツイストの動作用です。ツイスト関節から子関節へ向けてアラインメントします

対象の関節にソース側と対象側のサブマッピングが両方ある場合(例:腕や脚のツイスト)、エントリーの形式が異なります。

"Left_UpperArm": {
  "source": {
    "behavior": "normal",
    "mappings": {
      "LeftShoulder":  { "weightPosition": 0.086, "weightRotation": 0.086 },
      "LeftScapula":   { "weightPosition": 0.513, "weightRotation": 0.513 },
      "LeftArmUpper":  { "weightPosition": 0.402, "weightRotation": 0.402 }
    }
  },
  "target": {
    "behavior": "childAlignedTwist",
    "mappings": {
      "Left_LowerArm": { "weightPosition": 0.0, "weightRotation": 0.5 },
      "Left_UpperArm": { "weightPosition": 0.0, "weightRotation": 0.5 }
    }
  }
}

「単一」のエントリーとの構造上の違いに注意してください。トップレベルに type/behavior/mappings はなく、それぞれに独自の behavior と mappings 辞書を持つ source ブロックと target ブロックがあります。

5. ターゲットリグからジョイントを削除する

モデルにソーススケルトンにはない余分なボーン(ケープのボーンや武器のボーンなど)がある場合は、リターゲティングの対象から除外します。UI では、リーフジョイントにある − ボタンで除外できます。手動で行う場合は、次のすべてからジョイント名を削除します。

  • target.joints[] — 文字列を削除します
  • target.hierarchy — "<jointName>": "<parent>" のエントリを削除し、値が削除したジョイントを指すエントリも削除します(孤立チェック)
  • target.tpose、target.tposeMin、target.tposeMax — エントリを削除します
  • mapping.min、mapping.max — 削除したジョイント名をキーとするエントリを削除します。また、ネストされた target.mappings.<jointName> の参照も削除します
  • ジョイントが target.knownJoints に含まれていた場合は、意味的に重要なものを削除したことになります。まず代替のジョイントを選んでください

ランタイムは target.joints に含まれないジョイントを無視するため、ほかの場所に古いエントリを残すと、使われないデータが気付かれないまま残ります。必ず削除してください。

6. T ポーズの値を編集する

tpose(スケールなし)、tposeMin(最小スケール)、tposeMax(最大スケール)はジョイント名をキーとし、ワールド空間の position(xyz)と rotation(xyzw クォータニオン)が含まれます。いずれもルート原点空間(親 = ルートジョイント)です。

"target": {
  "tpose": {
    "Hips": {
      "position": { "x": 0.0, "y": 0.981, "z": -0.016 },
      "rotation": { "x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0 }
    }
  }
}

よくある調整:

  • ヒップの Y 位置を調整し、キャラクターが地面に埋まる、または浮く問題を修正します
  • 手首の回転を調整し、コントローラーのグリップポーズに合わせます
  • ランタイムでより広いスケール範囲を使えるよう、tposeMin と tposeMax に意図的に異なる値を設定します

単位はメートルです(config.coordinateSpace.metersToUnitScale: 1.0 による)。クォータニオンは単位長です。回転を編集する際は、これを維持してください。

7. ターゲットジョイントの名前を変更して、別のスケルトン命名規則に合わせる

モデルのボーン名が mixamorig:Hips で、後続のツールが Hips を想定している場合、FBX を変更せずに JSON 内で一括置換できます。Edit ツールで replace_all: true を使用します。ただし、target.* と mapping.min/mapping.max のセクション内に限ります。固定の OVR 命名を使用する source.* には触れないでください。

8. ソーススケルトン形式を切り替える

まれなケースですが、source.format.skeletonFlags.noRotationCorrectionOnCoordConversion を true(OVR などの X-Engine ネイティブスケルトン)と false(FBX/GLTF)の間で切り替えます。OVR 以外のソースに入れ替える場合を除き、変更しないでください。

9. 縮小すると歪むモデルを修正する(大きすぎるリグ)

症状: キャラクターはおおむね正しい位置にリターゲットされますが、体のパーツが引き伸ばされたり歪んだりします。特に最小Tポーズ(小柄なユーザー/縮小時)で顕著です。これは JSON マッピングの不具合ではありません。モデルのスケールの問題であり、修正するのは設定ファイルではなく、FBX のインポート設定です。

発生する理由: ランタイムは、独立した2つの方法でキャラクターのサイズを調整します。(a)リターゲットされた各ジョイントの 位置(手足の長さ。クランプされません)と、(b)ルートの均一な localScale による メッシュ(厚み/スキン)の縮小です。ルートスケールは SkeletonRetargeter._scaleRange の範囲内にクランプされます。デフォルト値は (0.8, 1.2) です。FBX が人間の身長から大きく外れたスケールで作成されている場合、ランタイムに必要なスケールが 0.8 を 下回ります。その結果、ジョイント間隔は(たとえば)30%に縮まる一方、メッシュは80%までしか縮小できず、リニアブレンドスキニングによって大きすぎるメッシュが短くなったボーンに引っ張られ、歪みが生じます。必要なスケールが 0.8 の下限を最も大きく下回る最小ポーズで、歪みが最も顕著になります。

見分け方: <asset>.json のスケール前のTポーズの高さを確認します。target.tpose の腰のY座標と全体の高さを確認してください。正しい人型モデルの身長は約1.5~2.0 m(腰の高さは約0.9 m)です。モデルがその約2倍(たとえば、身長3.7 m、腰の高さが約2.1 m)だと、必要な最小/最大スケールは約0.30~0.62となり、(0.8, 1.2) の範囲から大きく外れます。Mixamo のリグ(ボーンの接頭辞が mixamorig:)はよくある原因で、Humanoid ではなく Generic としてインポートされることもよくあります。

修正方法(推奨 — 作成時のスケールを修正する): FBX の ModelImporter で、リグが人間の身長になるように Scale Factor を設定します(targetHeight / currentHeight。例:1.7 / 3.7 ≈ 0.46)。さらに Rig → Animation Type を Humanoid に設定します。その後、モデルを再インポートし、設定ファイルを再生成します。両方の作業が必要です。

// AgentScripts/FixImportScale.cs
using UnityEditor;

public static class FixImportScale
{
    public static string Run(string path, float scale)
    {
        var importer = (ModelImporter)AssetImporter.GetAtPath(path);
        if (importer == null) return $"ERROR: no ModelImporter at {path}";

        importer.globalScale = scale;                          // targetHeight / currentHeight
        importer.useFileScale = false;                          // honor globalScale, not the FBX unit
        importer.animationType = ModelImporterAnimationType.Human;   // Humanoid
        importer.SaveAndReimport();                             // re-import with new settings
        return $"reimported {path} at globalScale={scale}";
    }
}
unity command run_script --file AgentScripts/FixImportScale.cs \
  --entry FixImportScale.Run --args '["Assets/MyChar/MyChar.fbx", 0.46]' --format json

次に、RunDefaultRetargetingSetup を再実行して(上記を参照)、スケールが正しくなったリグからTポーズ/最小/最大を再生成します。

SaveAndReimport() を実行するとアセットのインポートが行われるため、再生成は同じスクリプトで連続して実行せず、別の run_script 呼び出しで行ってください。

SaveAndReimport() の後はディスク上のTポーズが変わるため、古い <asset>.json は古くなっています。その後、必ず MSDKUtilityEditor.RunDefaultRetargetingSetup を再実行してください。スケール前のTポーズの高さが人間の身長になっていることを再確認します。

応急処置(モデルを変更できない場合のみ): CharacterRetargeter コンポーネントの _scaleRange を広げます(例:(0.25, 1.5))。これによりルートスケールがクランプされなくなり、歪みは解消しますが、リグは設計範囲外で動作するため、足の滑りやIKのオフセットに注意してください。インポートスケールの修正を推奨します。

検証

編集後、次の手順を実行してください。

  1. Unity で再読み込みする — Unity は JSON を自動でインポートします。形式に誤りがある場合、コンソールに JSON の解析エラーがすぐに表示されます。
  2. UI で開き直す — モデル上で Assets/Movement SDK/Body Tracking/Open Retargeting Configuration Editor を開きます。既知のジョイントが欠けている場合や、マッピングが存在しないジョイントを参照している場合、エディターは警告なしにデフォルト値へフォールバックします。そのため、警告を確認し、ボーンの折りたたみ項目を調べてください。
  3. ランタイムで抜き取り確認する — CharacterRetargeter コンポーネントを追加したシーンにキャラクターを配置するか、既存の MovementBody テストシーンを使い、手足が正しく追従することを確認します。次に、ライブボディトラッキングを使ってヘッドセット上で確認します。テストシーンをデプロイし、動かしながら metavr adb logcat --follow --tag Unity を実行してリターゲティングの警告をストリーミングします。

ランタイムは JSON 内の未認識の追加フィールドを許容するため、フィールドの追加は安全です。一方、削除や名前の変更は危険です。すべてのセクションでジョイント名が一致していることを必ず照合してください(grep -c "<oldName>" を実行し、変更前後を比較して、名前の変更がすべての箇所に反映されていることを確認します)。

サンプルワークフローのレシピ

「MR でキャラクターの手首が逆向きに曲がっている」 → 認識済みジョイントの rightWrist / leftWrist が誤っています。アセットの JSON を開き、target.knownJoints.rightWrist と .leftWrist が実際の手のボーンを指すように修正します(target.joints[] と target.hierarchy を確認して正しい名前を探します)。その後、MSDKUtilityEditor.RunDefaultRetargetingSetup を再実行します。

「指がガタつく」 → ソースの LeftHandPalm / RightHandPalm がターゲットにマッピングされています。excludeFromMapping: true を指定して source.autoMappingJointData に追加し、その後セットアップを再実行します。

「キャラクターが地面から浮いている」 → target.tpose.<root>.position.y、target.tposeMin.<root>.position.y、target.tposeMax.<root>.position.y を下方向に調整します(リグのルートが床ではなく腰の高さにある場合によく起こります)。

「標準以外のボーン名を持つヒューマノイドをリターゲットしたい」 → RunDefaultRetargetingSetup を実行した後、target.knownJoints が実際のボーン名を指すように修正します。自動検出はヒューリスティックを使用しているため、bn_pelvis_C_001 のような名前では失敗します。

「縮小すると身体パーツが歪む/せん断される(Min T-pose で最も顕著)」 → 作成時のリグが大きすぎます(Mixamo の mixamorig: FBX でよく起こります)。JSON でスケーリング前の T-pose の高さを確認します。人間の約 2 倍(例:3.7 m)であれば、必要なルートスケールが (0.8, 1.2) の制限を下回ります。FBX のインポート Scale Factor を targetHeight / currentHeight(例:0.46)に設定し、Animation Type を Humanoid にして SaveAndReimport() を実行した後、RunDefaultRetargetingSetup を再実行します。上記の「縮小すると歪むモデルを修正する」を参照してください。

編集後にセットアップを再実行する

MSDKUtilityEditor.RunDefaultRetargetingSetup(asset) は、既存の JSON がある場合はそれを再利用します(最初から作り直すことはありません)が、MinTPose/MaxTPose の各ステップでは GenerateMappings を実行し、mapping.min / mapping.max を上書きします。再実行後の変更の保持状況:

編集内容再実行後も保持されるか?
target.knownJoints(12 個のキーすべて)はい。ただし、ジョイント名がモデルのトランスフォーム階層に実際に存在する場合に限ります。名前を誤入力すると、次回の再実行時に何も通知されず空に設定されます。
excludeFromMapping: true を指定した source.autoMappingJointDataはい
excludeFromTwistMappings: true を指定した source.autoMappingJointDataいいえ — SkeletonData.GenerateAutoMappingExcludedJointDataFromJointNameList は Exclude フラグのみを保持してラウンドトリップします。ツイストのみの除外は再実行時に破棄されます。
mapping.min / mapping.max のウェイト調整いいえ — 最初から再生成されます
target.tpose* の位置/回転の編集一部 — 値は SkeletonData に読み込まれますが、各ステップの UpdateConfig は JointAlignmentUtility.UpdateTPoseData を介してシーンのトランスフォームを再読み込みします。モデル自体にこれらのポーズがディスク上に保存されている場合にのみ編集内容が保持され、そうでない場合はシーンの値で上書きされます。
ジョイントの完全な削除(すべてのセクション)はい — target.joints が正とするデータです
ターゲットジョイントの名前変更(すべてのセクションで一貫して変更)はい。上記と同じ理由です

つまり、マッピングのウェイト調整、ツイストのみの除外、臨時の T-pose 調整は、最後の RunDefaultRetargetingSetup を実行した後に行ってください。認識済みジョイントとマッピング除外の編集は、再実行しても問題ありません。

コメント