hz-unity-meta-movement-sdk-retargeting
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMSDK Retargeting Config
MSDK重定向配置
Workflow: always start with the headless API
工作流:始终从无界面API开始
Don't hand-craft the JSON from scratch — the native side computes initial alignment, mappings, and T-pose data that would be tedious to write by hand. The right flow is:
- Generate the default config by calling via Unity MCP. This is the headless equivalent of clicking through the Retargeting Configuration Editor UI with all defaults (Next×3 → Validate → Done) and produces both artifacts:
Meta.XR.Movement.Editor.MSDKUtilityEditor.RunDefaultRetargetingSetup(GameObject asset, string customDataSourcePath = null)- — the retargeting config (this skill targets this file)
<asset>.json - — a small ScriptableObject linking the model to the JSON (rarely needs editing)
<asset>-metadata.asset
- Inspect the generated JSON to verify joint mappings look right.
- Hand-edit only if needed — the rest of this skill describes the JSON structure and the common tweaks.
The same API also backs the editor menu item, so a user who sees a project asset selected can trigger it from the menu too.
RunDefaultRetargetingSetupAssets/Movement SDK/Body Tracking/Run Default Retargeting Setup不要从头手动编写JSON——原生端会计算初始对齐、映射和T姿态数据,手动编写这些内容非常繁琐。正确流程如下:
- 生成默认配置:通过Unity MCP调用。这相当于在重定向配置编辑器UI中点击所有默认选项(三次下一步→验证→完成)的无界面版本,会生成两个文件:
Meta.XR.Movement.Editor.MSDKUtilityEditor.RunDefaultRetargetingSetup(GameObject asset, string customDataSourcePath = null)- ——重定向配置文件(本指南针对该文件)
<asset>.json - ——一个小型ScriptableObject,用于关联模型与JSON(几乎无需编辑)
<asset>-metadata.asset
- 检查生成的JSON,确认关节映射是否正确。
- 仅在必要时手动编辑——本指南剩余部分将介绍JSON结构及常见调整方法。
同一个API也支持编辑器菜单选项,因此用户选中项目资源后,也可通过菜单触发该功能。
RunDefaultRetargetingSetupAssets/Movement SDK/Body Tracking/Run Default Retargeting SetupCalling it via Unity MCP
通过Unity MCP调用
The Unity MCP script runner can't see the namespace from the wrapped harness; use reflection to invoke the public static method:
Unity_RunCommandMeta.*csharp
using UnityEngine;
using UnityEditor;
internal class CommandScript : IRunCommand
{
public void Execute(ExecutionResult result)
{
var asset = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/MyChar/MyChar.fbx");
if (asset == null) { result.LogError("asset not found"); return; }
System.Type editorType = null;
foreach (var asm in System.AppDomain.CurrentDomain.GetAssemblies())
{
var t = asm.GetType("Meta.XR.Movement.Editor.MSDKUtilityEditor");
if (t != null) { editorType = t; break; }
}
var method = editorType.GetMethod("RunDefaultRetargetingSetup");
try { method.Invoke(null, new object[] { asset, null }); }
catch (System.Reflection.TargetInvocationException tie) {
result.LogError("RunDefaultRetargetingSetup failed: " + tie.InnerException);
return;
}
result.Log("done; config at " + AssetDatabase.GetAssetPath(asset).Replace(System.IO.Path.GetExtension(AssetDatabase.GetAssetPath(asset)), ".json"));
}
}Notes:
- The harness wraps the script in , so a top-level
Unity.AI.Assistant.Agent.Dynamic.Extension.Editorwon't compile — reflection is the workaround.using Meta.XR.Movement.Editor; - Avoid at the top of the file (the harness's namespace wrap interacts badly with it). Use fully-qualified
using System.Reflection;etc. inline if you need the types.System.Reflection.MethodInfo - is non-destructive — if a JSON already exists, it loads and re-runs the per-step replay; user-made
RunDefaultRetargetingSetupandtarget.knownJointsedits survive (see the "Re-running setup" table at the bottom).source.autoMappingJointData - The asset must live under or in an embedded package. Calling on an immutable package asset via MCP can trigger Unity's "save changes to immutable package?" dialog and fail.
Assets/
Unity MCP的脚本运行器无法从封装的容器中访问命名空间;请使用反射调用该公开静态方法:
Unity_RunCommandMeta.*csharp
using UnityEngine;
using UnityEditor;
internal class CommandScript : IRunCommand
{
public void Execute(ExecutionResult result)
{
var asset = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/MyChar/MyChar.fbx");
if (asset == null) { result.LogError("asset not found"); return; }
System.Type editorType = null;
foreach (var asm in System.AppDomain.CurrentDomain.GetAssemblies())
{
var t = asm.GetType("Meta.XR.Movement.Editor.MSDKUtilityEditor");
if (t != null) { editorType = t; break; }
}
var method = editorType.GetMethod("RunDefaultRetargetingSetup");
try { method.Invoke(null, new object[] { asset, null }); }
catch (System.Reflection.TargetInvocationException tie) {
result.LogError("RunDefaultRetargetingSetup failed: " + tie.InnerException);
return;
}
result.Log("done; config at " + AssetDatabase.GetAssetPath(asset).Replace(System.IO.Path.GetExtension(AssetDatabase.GetAssetPath(asset)), ".json"));
}
}注意事项:
- 容器会将脚本封装在命名空间中,因此顶级的
Unity.AI.Assistant.Agent.Dynamic.Extension.Editor无法编译——反射是可行的解决方法。using Meta.XR.Movement.Editor; - 避免在文件顶部使用(容器的命名空间封装会与其产生不良交互)。如果需要相关类型,请在代码中使用完全限定名,如
using System.Reflection;。System.Reflection.MethodInfo - 是非破坏性的——如果JSON已存在,它会加载并重新执行每一步流程;用户对
RunDefaultRetargetingSetup和target.knownJoints的编辑会保留(请参阅底部的“重新运行设置”表格)。source.autoMappingJointData - 资源必须位于目录下或嵌入式包中。通过MCP调用不可变包资源可能会触发Unity的“是否保存对不可变包的更改?”对话框并导致失败。
Assets/
When to hand-edit the JSON
何时手动编辑JSON
The defaults are usually 80–95% right. Common reasons to hand-edit afterwards:
- The auto-detector picked the wrong bone for a known joint (e.g. mapped to a spine bone, or
chestmapped to a wrist-twist bone).wrist - Source-side joints like or
LeftHandPalmare bleeding into target hand mappings and causing jitter.*WristTwist - The model's bones use a non-standard naming convention (,
mixamorig:Hips) and you want to clean up the joint names.bn_pelvis_C_001 - You want to manually tune /
weightPositionfor a specific joint, or change a mappingweightRotationtobehavior/twist.childAlignedTwist - Character floats above ground / penetrates floor (T-pose root height fix).
The JSON drives runtime retargeting. The same skeleton-name strings appear across many sections — edit them all consistently or the native loader will silently produce broken mappings.
默认配置通常能满足80%-95%的需求。常见的手动编辑场景包括:
- 自动检测器为已知关节选择了错误的骨骼(例如映射到脊柱骨骼,或
chest映射到腕部扭转骨骼)。wrist - 源端关节如或
LeftHandPalm渗透到目标手部映射中,导致抖动。*WristTwist - 模型骨骼使用非标准命名规则(如、
mixamorig:Hips),需要清理关节名称。bn_pelvis_C_001 - 需要手动调整特定关节的/
weightPosition,或将映射weightRotation更改为behavior/twist。childAlignedTwist - 角色漂浮在空中/穿透地面(修复T姿态根节点高度)。
JSON会驱动运行时重定向。同一骨骼名称字符串会出现在多个部分中——请确保所有部分的名称一致,否则原生加载器会静默生成错误的映射。
Top-level layout
顶层结构
{
"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: { ... } }
}- — the Meta Quest body tracking skeleton (typically OVR FullBody, ~84 joints). Joint names are fixed (e.g.
source,LeftHandWrist); usually leave alone.Hips - — the user's character rig. Joint names come from the model's bone hierarchy (e.g.
target,Left_Hand,Hips).Skeleton - — for each target joint, a weighted list of source joints that drive it.
mappingis for the smallest body scale,minfor the largest. The retargeter blends between them at runtime based on the user's height.max
{
"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: { ... } }
}- ——Meta Quest身体追踪骨骼(通常为OVR FullBody,约84个关节)。关节名称是固定的(例如
source、LeftHandWrist);通常无需修改。Hips - ——用户的角色绑定。关节名称来自模型的骨骼层级(例如
target、Left_Hand、Hips)。Skeleton - ——针对每个目标关节,列出驱动它的源关节的加权列表。
mapping对应最小体型,min对应最大体型。重定向器会在运行时根据用户身高在两者之间进行混合。max
Joint names: where the same string must appear
关节名称:需保持一致的位置
A joint name (e.g. ) referenced inconsistently across sections will break the rig. When renaming/removing a target joint, update all of these:
"Right_UpperArm"| Section | What it contains |
|---|---|
| The flat list of all joint names |
| Maps semantic role → joint name (e.g. |
| Maps |
| Per-joint position+rotation, keyed by joint name |
| Top-level keys ARE target joint names; nested |
如果关节名称(例如)在不同部分中引用不一致,会导致绑定失效。重命名/删除目标关节时,请更新以下所有部分:
"Right_UpperArm"| 部分 | 包含内容 |
|---|---|
| 所有关节名称的扁平列表 |
| 语义角色→关节名称的映射(例如 |
| |
| 按关节名称索引的单关节位置+旋转数据 |
| 顶层键为目标关节名称;嵌套的 |
What users most commonly tweak
用户最常调整的内容
1. Fix a known-joint binding (most common)
1. 修复已知关节绑定(最常见)
The auto-detection sometimes picks the wrong bone (e.g. instead of , or attaches to a spine joint instead of the chest). Fix by editing :
Right_HandTwistRight_Handchesttarget.knownJointsjson
"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"
}
}The 12 keys are the only valid ones (matches enum: ). After editing this, the user typically also wants to re-run alignment — recommend they re-run on the asset (it will preserve the JSON and re-derive mappings).
KnownJointTyperoot, hips, rightUpperArm, leftUpperArm, rightWrist, leftWrist, chest, neck, rightUpperLeg, leftUpperLeg, rightAnkle, leftAnkleMSDKUtilityEditor.RunDefaultRetargetingSetup自动检测有时会选择错误的骨骼(例如而非,或绑定到脊柱关节而非胸部关节)。通过编辑修复:
Right_HandTwistRight_Handchesttarget.knownJointsjson
"target": {
"knownJoints": {
"root": "Skeleton",
"hips": "Hips",
"rightUpperArm": "Right_UpperArm",
"leftUpperArm": "Left_UpperArm",
"rightWrist": "Right_Hand", // ← 已修正
"leftWrist": "Left_Hand",
"chest": "UpperChest", // ← 已修正(原为"Chest")
"neck": "Neck",
"rightUpperLeg": "Right_UpperLeg",
"leftUpperLeg": "Left_UpperLeg",
"rightAnkle": "Right_Foot",
"leftAnkle": "Left_Foot"
}
}这12个键是唯一有效的值(与枚举匹配:)。编辑完成后,用户通常需要重新运行对齐操作——建议他们对资源重新运行(该操作会保留JSON并重新生成映射)。
KnownJointTyperoot, hips, rightUpperArm, leftUpperArm, rightWrist, leftWrist, chest, neck, rightUpperLeg, leftUpperLeg, rightAnkle, leftAnkleMSDKUtilityEditor.RunDefaultRetargetingSetup2. Exclude a joint from auto-mapping
2. 排除关节自动映射
Useful when the auto-mapper ties a source twist/palm joint to an unwanted target joint. Add to (or for target-side exclusions):
source.autoMappingJointDatatarget.autoMappingJointDatajson
"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
}
}Two flags exist (from ):
AutoMappingJointFlags- — joint is completely ignored when generating mappings
excludeFromMapping: true - — joint is still mapped normally but skipped when generating
excludeFromTwistMappings: true/behavior: "twist"entries"childAlignedTwist"
The keys' iteration order in this object is non-deterministic (native unordered map). Don't rely on it; same set of keys = equivalent config.
当自动映射器将源端扭转/手掌关节绑定到不需要的目标关节时,此操作非常有用。将关节添加到(或目标端排除则添加到):
source.autoMappingJointDatatarget.autoMappingJointDatajson
"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 } // ← 仅跳过扭转映射
}
}有两个标志(来自):
AutoMappingJointFlags- ——生成映射时完全忽略该关节
excludeFromMapping: true - ——关节仍会正常映射,但生成
excludeFromTwistMappings: true/behavior: "twist"条目时会被跳过"childAlignedTwist"
该对象中键的迭代顺序是非确定性的(原生无序映射)。请勿依赖顺序;键集相同即为等效配置。
3. Tweak per-joint mapping weights
3. 调整单关节映射权重
A mapping entry is keyed by the target joint name and lists source joints that drive it, each with (0–1) and (0–1). Both default to summing toward 1.0 across siblings, but the runtime normalizes — relative weights matter, not absolute.
weightPositionweightRotationjson
"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 */ }
}Common tweaks:
- Set to 0 if you only want a source joint to influence position (or vice-versa).
weightRotation - Add a source joint to the dict to give it influence over the target joint.
mappings - and
mapping.minare usually identical or close — set them the same when in doubt.mapping.max
映射条目以目标关节名称为键,列出驱动它的源关节,每个关节都有(0–1)和(0–1)。两者默认值相加为1.0,但运行时会进行归一化——相对权重比绝对权重更重要。
weightPositionweightRotationjson
"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": { /* 结构相同,最大体型对应不同权重 */ }
}常见调整:
- 如果仅希望源关节影响位置(或反之),可将设为0。
weightRotation - 在字典中添加源关节,使其对目标关节产生影响。
mappings - 和
mapping.min通常相同或接近——不确定时可设为一致。mapping.max
4. Change mapping behavior (normal / twist / childAlignedTwist)
4. 更改映射行为(normal / twist / childAlignedTwist)
Three legal values for :
behavior- — direct weighted blend
"normal" - — for source-side twist joints; aligns parent rotation through to the twist
"twist" - — for target-side twist behavior; aligns from twist joint down to children
"childAlignedTwist"
When a target joint has both source and target sub-mappings (e.g. for arm/leg twist), the entry has a different shape:
json
"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 }
}
}
}Note the structural difference from a "single" entry: no top-level //, instead a block + a block, each with its own behavior + mappings dict.
typebehaviormappingssourcetargetbehavior- ——直接加权混合
"normal" - ——针对源端扭转关节;将父关节旋转传递到扭转关节
"twist" - ——针对目标端扭转行为;从扭转关节向下对齐到子关节
"childAlignedTwist"
当目标关节同时有源端和目标端子映射时(例如手臂/腿部扭转),条目结构会有所不同:
json
"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 }
}
}
}注意与“单一”条目的结构差异:没有顶层的//,而是包含一个块和一个块,每个块都有自己的行为和映射字典。
typebehaviormappingssourcetarget5. Remove a joint from the target rig
5. 从目标绑定中移除关节
If a model has extra bones the source skeleton doesn't have (e.g. cape bones, weapon bones), exclude them from retargeting. The UI's button on a leaf joint does this. To do it manually, remove the joint name from all of these:
−- — drop the string
target.joints[] - — drop the
target.hierarchyentry, AND drop any entry whose value points to the removed joint (orphan check)"<jointName>": "<parent>" - ,
target.tpose,target.tposeMin— drop the entrytarget.tposeMax - ,
mapping.min— drop the entry keyed by the removed joint name; also drop any nestedmapping.maxreferencetarget.mappings.<jointName> - If the joint appeared in , you've removed something semantically important — pick a replacement first
target.knownJoints
The runtime ignores joints absent from , so leaving stale entries elsewhere produces silent dead data. Always clean them up.
target.joints如果模型包含源骨骼中没有的额外骨骼(例如披风骨骼、武器骨骼),请将其从重定向中排除。UI中叶子关节的按钮可实现此操作。手动操作时,请从以下所有部分中移除关节名称:
−- ——删除该字符串
target.joints[] - ——删除
target.hierarchy条目,同时删除所有值指向该关节的条目(检查孤儿关节)"<jointName>": "<parent>" - ,
target.tpose,target.tposeMin——删除该条目target.tposeMax - ,
mapping.min——删除以该关节名称为键的条目;同时删除所有嵌套的mapping.max引用target.mappings.<jointName> - 如果该关节出现在中,说明你移除了具有重要语义的关节——请先选择替代关节
target.knownJoints
运行时会忽略中不存在的关节,因此其他部分中残留的条目会产生静默无效数据。请务必清理干净。
target.joints6. Edit T-pose values
6. 编辑T姿态数值
tposetposeMintposeMaxpositionrotationjson
"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 }
}
}
}Common tweaks:
- Adjust hip Y-position to fix character ground penetration / floating
- Re-orient wrist rotation to match controller grip pose
- Make and
tposeMindeliberately different to give the runtime a wider scale rangetposeMax
The unit is meters (per ). Quaternions are unit-length — preserve that when editing rotations.
config.coordinateSpace.metersToUnitScale: 1.0tposetposeMintposeMaxpositionrotationjson
"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.07. Rename target joints to match a different skeleton naming convention
7. 重命名目标关节以匹配其他骨骼命名规则
If the model's bones are named but downstream tooling expects , you can globally rename in the JSON without touching the FBX. Use on the Edit tool, but only within the and / sections — never touch , which uses the fixed OVR naming.
mixamorig:HipsHipsreplace_all: truetarget.*mapping.minmapping.maxsource.*如果模型骨骼命名为,但下游工具期望,可在JSON中全局重命名而无需修改FBX。使用编辑工具的,但仅在和/部分中操作——切勿修改,因为它使用固定的OVR命名。
mixamorig:HipsHipsreplace_all: truetarget.*mapping.minmapping.maxsource.*8. Switch the source skeleton format
8. 切换源骨骼格式
Rare: changing between (X-Engine native skeletons like OVR) and (FBX/GLTF). Don't change unless you're swapping in a non-OVR source.
source.format.skeletonFlags.noRotationCorrectionOnCoordConversiontruefalse罕见场景:在(X-Engine原生骨骼如OVR)和(FBX/GLTF)之间切换。除非要替换非OVR源,否则请勿更改。
truefalsesource.format.skeletonFlags.noRotationCorrectionOnCoordConversionValidation
验证
After editing, the user should:
- Reload in Unity — Unity auto-imports JSON. If it's malformed, the console shows JSON parse errors immediately.
- Reopen in the UI — on the model. The editor will silently fall back to defaults if a known joint is missing or a mapping references a non-existent joint, so look for warnings + inspect the bone foldout.
Assets/Movement SDK/Body Tracking/Open Retargeting Configuration Editor - Spot-check at runtime — drop the character into a scene with a component (or use the existing
CharacterRetargetertest scene) and verify limbs track correctly.MovementBody
The runtime tolerates extra unrecognized fields in the JSON, so additive edits are safe; the dangerous edits are removals and renames. Always cross-check joint name consistency across all sections (use before and after to confirm a rename touched everything).
grep -c "<oldName>"编辑完成后,用户应:
- 在Unity中重新加载——Unity会自动导入JSON。如果格式错误,控制台会立即显示JSON解析错误。
- 在UI中重新打开——在模型上选择。如果缺少已知关节或映射引用了不存在的关节,编辑器会静默回退到默认值,因此请查看警告并检查骨骼折叠面板。
Assets/Movement SDK/Body Tracking/Open Retargeting Configuration Editor - 运行时抽查——将角色拖入带有组件的场景(或使用现有
CharacterRetargeter测试场景),验证肢体追踪是否正确。MovementBody
运行时会容忍JSON中额外的未识别字段,因此增量编辑是安全的;危险的编辑是删除和重命名。始终交叉检查所有部分的关节名称一致性(重命名前后使用确认所有引用都已修改)。
grep -c "<oldName>"Sample workflow recipes
示例工作流方案
"The character's wrists are bent backwards in MR" → Wrong / known joint. Open the asset's JSON, fix and to point to the actual hand bones (look at + to find the right names), then re-run .
rightWristleftWristtarget.knownJoints.rightWrist.leftWristtarget.joints[]target.hierarchyMSDKUtilityEditor.RunDefaultRetargetingSetup"Fingers are jittery" → The source has / mapped into the target. Add them to with , then re-run setup.
LeftHandPalmRightHandPalmsource.autoMappingJointDataexcludeFromMapping: true"Character floats above ground" → Adjust and and downward (often the rig has the root at hip-height instead of floor).
target.tpose.<root>.position.ytarget.tposeMin.<root>.position.ytarget.tposeMax.<root>.position.y"Need to retarget a humanoid with non-standard bone names" → After running , fix to point to the actual bone names. The auto-detector uses heuristics that fail on naming like .
RunDefaultRetargetingSetuptarget.knownJointsbn_pelvis_C_001“MR中角色手腕向后弯曲”→/已知关节错误。打开资源的JSON,将和修改为指向实际手部骨骼(查看+找到正确名称),然后重新运行。
rightWristleftWristtarget.knownJoints.rightWrist.leftWristtarget.joints[]target.hierarchyMSDKUtilityEditor.RunDefaultRetargetingSetup“手指抖动”→源端/映射到了目标端。将它们添加到并设置,然后重新运行设置。
LeftHandPalmRightHandPalmsource.autoMappingJointDataexcludeFromMapping: true“角色漂浮在空中”→向下调整、和(通常绑定的根节点位于臀部高度而非地面)。
target.tpose.<root>.position.ytarget.tposeMin.<root>.position.ytarget.tposeMax.<root>.position.y“需要重定向具有非标准骨骼名称的类人角色”→运行后,修改指向实际骨骼名称。自动检测器使用的启发式算法在遇到这类命名时会失效。
RunDefaultRetargetingSetuptarget.knownJointsbn_pelvis_C_001Re-running setup after edits
编辑后重新运行设置
MSDKUtilityEditor.RunDefaultRetargetingSetup(asset)GenerateMappingsmapping.minmapping.max| Edit | Survives re-run? |
|---|---|
| Yes, if the joint name actually exists in the model's transform hierarchy. If you typo a name, the next re-run silently sets it to empty. |
| Yes |
| No — |
| No — regenerated from scratch |
| Partial — values are read into |
| Removing a joint cleanly (all sections) | Yes — |
| Renaming target joints (consistent across all sections) | Yes, same reasoning as above |
So: do mapping-weight tweaks, twist-only exclusions, and ad-hoc T-pose tweaks last, after any final . For known-joints and exclude-from-mapping edits, re-run is fine.
RunDefaultRetargetingSetupMSDKUtilityEditor.RunDefaultRetargetingSetup(asset)GenerateMappingsmapping.minmapping.max| 编辑内容 | 重新运行后是否保留? |
|---|---|
| 是,前提是关节名称确实存在于模型的变换层级中。如果输入错误名称,下次重新运行会静默将其设为空。 |
带有 | 是 |
带有 | 否—— |
| 否——会从头重新生成 |
| 部分保留——数值会被读入 |
| 干净移除关节(所有部分) | 是—— |
| 一致重命名目标关节(所有部分) | 是,原因同上 |
因此:映射权重调整、仅排除扭转的设置和临时T姿态调整请最后进行,在最终运行之后。对于已知关节和排除映射的编辑,重新运行设置是安全的。
RunDefaultRetargetingSetup