跳转至

name: vrchat-foot-heel-linkage description: Link Foot_Heel blendshape to wardrobe clothing states via animation clips in FX Controller Layer tags: [vrchat, unity, animation, foot-heel, wardrobe, blendshape, fx-controller]


VRChat Foot_Heel Blendshape Linkage via AnimationClips

Problem

When an avatar has multiple clothing sets with different heel heights (pumps/high heels vs sandals/flats), the Foot_Heel blendshape on Body_Base needs to change automatically when switching clothes via the wardrobe menu.

Manually setting Foot_Heel via blendshape slider only works for preview — for runtime behavior, it must be animation-driven.

Architecture (Option A: Modify Wardrobe Clips)

Use the wardrobe animation clips (already used for clothing m_IsActive control) to also set Foot_Heel blendshape value:

Layer 18 WardrobeCloth
├── Cloth_原皮.anim     → Foot_Heel = 100 (high heel)
├── Cloth_露肩短裙.anim  → Foot_Heel = 100 (high heel)
├── Cloth_休闲服.anim    → Foot_Heel = 0   (flat)
└── Cloth_全脱.anim      → Foot_Heel = 0   (flat)

Step-by-Step Implementation

eel = 0 (flat) └── Cloth_全脱.anim → Foot_Heel = 0 (flat)

## Step-by-Step Implementation### Quickest Approach (Option B: Dedicated Foot Layer)

Rather than modifying existing wardrobe clips (Option A — fragile if clips are regenerated), create a **dedicated high-priority Layer** that sets Foot_Heel based on Wardrobe_Int1:
Layer 20: FootHeight (Override, weight=1) ├── Tiptoe state (default): Foot_Heel = 100, motion=FootHeel_Tiptoe.anim, wd=false ├── Flat state: Foot_Heel = 0, motion=FootHeel_Flat.anim, wd=false ├── Transitions: │ Tiptoe → Flat: Wardrobe_Int1 Equals 2 OR Equals 3 (休闲服/全脱) │ Flat → Tiptoe: all other Wardrobe_Int1 values
**AnimationClips needed (2 total):**
- `FootHeel_Tiptoe.anim`: `Body_Base: blendShape.Foot_Heel = 100`
- `FootHeel_Flat.anim`:   `Body_Base: blendShape.Foot_Heel = 0`
dy_Base: blendShape.Foot_Heel = 100`
- `FootHeel_Flat.anim`:   `Body_Base: blendShape.Foot_Heel = 0`### Option A: Add BlendShape Curve to Existing AnimationClip

Modify each wardrobe clip directly:

```csharp
string clipPath = "Assets/Cazalis/Animation/Wardrobe/Cloth_原皮.anim";
AnimationClip clip = AssetDatabase.LoadAssetAtPath<AnimationClip>(clipPath);

// Target: Body_Base SkinnedMeshRenderer
// Property: blendShape.Foot_Heel
EditorCurveBinding binding = new EditorCurveBinding();
binding.path = "Body_Base";  // GameObject name in hierarchy
binding.propertyName = "blendShape.Foot_Heel";
binding.type = typeof(SkinnedMeshRenderer);

AnimationCurve curve = AnimationCurve.Constant(0, 0, 100f); // single key at t=0
AnimationUtility.SetEditorCurve(clip, binding, curve);

EditorUtility.SetDirty(clip);
AssetDatabase.SaveAssets();

Key points: - Single key at t=0 (constant curve) is sufficient — no interpolation needed - Body_Base is the SkinnedMeshRenderer name, must match the actual hierarchy - Property name is blendShape.Foot_Heel (case-sensitive) - Alternative path may be Cazalis zigai/Body_Base if Body_Base is not root - Body_Base has 92 blendshapes on Cazalis zigai

2. Verify Clip Contents

foreach (var binding in AnimationUtility.GetCurveBindings(clip))
{
    Debug.Log($"Path: {binding.path}, Prop: {binding.propertyName}");
}
etCurveBindings(clip)) { Debug.Log($"Path: {binding.path}, Prop: {binding.propertyName}"); } ```### 3. Connect Layer 18 Transitions

Each State in Layer 18 (WardrobeCloth) needs transitions to all other States, conditioned on Wardrobe_Int1 parameter:

int[] stateIndices = {0, 1, 2, 3, 4}; // Idle, 原皮, 露肩短裙, 休闲服, 全脱
foreach (int srcIdx in stateIndices)
{
    foreach (int dstIdx in stateIndices)
    {
        if (srcIdx == dstIdx) continue;
        var transition = states[srcIdx].AddTransition(states[dstIdx]);
        transition.hasExitTime = false;
        transition.duration = 0;
        transition.conditions = new AnimatorCondition[]
        {
            new AnimatorCondition
            {
                parameter = "Wardrobe_Int1",
                mode = AnimatorConditionMode.Equals,
                threshold = dstIdx - 1  // 原皮=0, 露肩短裙=1, 休闲服=2, 全脱=3
            }
        };
    }
}

4. Verify Transitions in Animator Window

Open Window → Animation → Animator (Ctrl+6), select Layer 18 WardrobeCloth: - Each State should have arrows to all other States - Click an arrow → Inspector should show condition: Wardrobe_Int1 Equals X

Verification

  • Each wardrobe clip has blendShape.Foot_Heel curve
  • High-heel clothes → Foot_Heel=100
  • Flat clothes → Foot_Heel=0
  • Naked → Foot_Heel=0
  • Layer 18 Transitions connected with Wardrobe_Int1 conditions
  • Test in VRChat: switch clothes, observe foot/ankle pose changes with Wardrobe_Int1 conditions
  • Test in VRChat: switch clothes, observe foot/ankle pose changes## Pitfalls

  • Cazalis zigai Body_Base path: The path for animation binding is just Body_Base (child of root Cazalis zigai). Body_Base has 92 blendshapes total.

  • Foot_Heel naming: Exact blendShape name is Foot_Heel (not Foot_Heel_L/R). There are also related blendshapes: Foot_Highheel (currently unused), Shrink_Ankle/L/R, Shrink_Foot/L/R, Shrink_Toe/L/R.
  • Wardrobe_Int1 mapping: User's setup maps Int values to clothing states. Flat-shoe states (休闲服/全脱) = 2/3, tiptoe states = 0/1.
  • Option B cleanest: Dedicated FootHeight Layer avoids touching existing wardrobe clips, cleaner architecture.
  • MCP Code transitions may not persist: C# code that modifies AnimatorController transitions may work in editor session but not save to disk. Always verify in Animator window after running code.