194 lines
5.8 KiB
C#
194 lines
5.8 KiB
C#
#if UNITY_EDITOR
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using RootMotion.FinalIK;
|
||
|
||
public static class CCDIKRodWeightEditor
|
||
{
|
||
// 默认鱼竿权重曲线:
|
||
// 根部硬,末端软
|
||
private static readonly AnimationCurve DefaultRodCurve = new AnimationCurve(
|
||
new Keyframe(0.00f, 1.00f),
|
||
new Keyframe(0.30f, 0.88f),
|
||
new Keyframe(0.60f, 0.58f),
|
||
new Keyframe(0.82f, 0.22f),
|
||
new Keyframe(1.00f, 0.05f)
|
||
);
|
||
|
||
/// <summary>
|
||
/// 对当前选中物体上的 CCDIK 应用默认鱼竿权重
|
||
/// </summary>
|
||
[MenuItem("Tools/FinalIK/CCDIK/Apply Rod Weights (Selected)")]
|
||
public static void ApplyRodWeightsToSelected()
|
||
{
|
||
var go = Selection.activeGameObject;
|
||
if (go == null)
|
||
{
|
||
Debug.LogWarning("没有选中任何 GameObject。");
|
||
return;
|
||
}
|
||
|
||
var ccdik = go.GetComponent<CCDIK>();
|
||
if (ccdik == null)
|
||
{
|
||
Debug.LogWarning($"选中的物体 [{go.name}] 上没有 CCDIK 组件。");
|
||
return;
|
||
}
|
||
|
||
ApplyWeights(ccdik, DefaultRodCurve, 0.05f, 1.0f, true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 稍微软一点的版本
|
||
/// </summary>
|
||
[MenuItem("Tools/FinalIK/CCDIK/Apply Rod Weights Soft (Selected)")]
|
||
public static void ApplyRodWeightsSoftToSelected()
|
||
{
|
||
var go = Selection.activeGameObject;
|
||
if (go == null)
|
||
{
|
||
Debug.LogWarning("没有选中任何 GameObject。");
|
||
return;
|
||
}
|
||
|
||
var ccdik = go.GetComponent<CCDIK>();
|
||
if (ccdik == null)
|
||
{
|
||
Debug.LogWarning($"选中的物体 [{go.name}] 上没有 CCDIK 组件。");
|
||
return;
|
||
}
|
||
|
||
var softCurve = new AnimationCurve(
|
||
new Keyframe(0.00f, 1.00f),
|
||
new Keyframe(0.25f, 0.92f),
|
||
new Keyframe(0.50f, 0.72f),
|
||
new Keyframe(0.75f, 0.35f),
|
||
new Keyframe(1.00f, 0.10f)
|
||
);
|
||
|
||
ApplyWeights(ccdik, softCurve, 0.10f, 1.0f, true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更硬的版本
|
||
/// </summary>
|
||
[MenuItem("Tools/FinalIK/CCDIK/Apply Rod Weights Stiff (Selected)")]
|
||
public static void ApplyRodWeightsStiffToSelected()
|
||
{
|
||
var go = Selection.activeGameObject;
|
||
if (go == null)
|
||
{
|
||
Debug.LogWarning("没有选中任何 GameObject。");
|
||
return;
|
||
}
|
||
|
||
var ccdik = go.GetComponent<CCDIK>();
|
||
if (ccdik == null)
|
||
{
|
||
Debug.LogWarning($"选中的物体 [{go.name}] 上没有 CCDIK 组件。");
|
||
return;
|
||
}
|
||
|
||
var stiffCurve = new AnimationCurve(
|
||
new Keyframe(0.00f, 1.00f),
|
||
new Keyframe(0.35f, 0.94f),
|
||
new Keyframe(0.65f, 0.65f),
|
||
new Keyframe(0.88f, 0.18f),
|
||
new Keyframe(1.00f, 0.03f)
|
||
);
|
||
|
||
ApplyWeights(ccdik, stiffCurve, 0.03f, 1.0f, true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 核心逻辑:
|
||
/// 第0个bone = 杆尾(高权重)
|
||
/// 最后一个bone = 竿稍(低权重)
|
||
/// </summary>
|
||
public static void ApplyWeights(CCDIK ccdik, AnimationCurve curve, float minWeight, float maxWeight, bool logResult)
|
||
{
|
||
if (ccdik == null)
|
||
{
|
||
Debug.LogWarning("CCDIK 为空。");
|
||
return;
|
||
}
|
||
|
||
var solver = ccdik.solver;
|
||
if (solver == null)
|
||
{
|
||
Debug.LogWarning("CCDIK.solver 为空。");
|
||
return;
|
||
}
|
||
|
||
if (solver.bones == null || solver.bones.Length == 0)
|
||
{
|
||
Debug.LogWarning($"[{ccdik.name}] 的 solver.bones 为空,请先正确配置 CCDIK Chain。");
|
||
return;
|
||
}
|
||
|
||
Undo.RecordObject(ccdik, "Apply CCDIK Rod Weights");
|
||
|
||
int count = solver.bones.Length;
|
||
|
||
if (count == 1)
|
||
{
|
||
solver.bones[0].weight = maxWeight;
|
||
EditorUtility.SetDirty(ccdik);
|
||
Debug.Log($"[{ccdik.name}] 只有 1 根 bone,已设置 weight = {maxWeight:F3}");
|
||
return;
|
||
}
|
||
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
// 0 = 杆尾,1 = 竿稍
|
||
float t = 1f - (i / (float)(count - 1));
|
||
|
||
// 曲线前高后低
|
||
float curveValue = curve.Evaluate(t);
|
||
|
||
// 限制范围
|
||
float weight = Mathf.Lerp(minWeight, maxWeight, curveValue);
|
||
weight = Mathf.Clamp(weight, minWeight, maxWeight);
|
||
|
||
solver.bones[i].weight = weight;
|
||
}
|
||
|
||
EditorUtility.SetDirty(ccdik);
|
||
|
||
if (logResult)
|
||
{
|
||
Debug.Log(BuildWeightLog(ccdik));
|
||
}
|
||
}
|
||
|
||
private static string BuildWeightLog(CCDIK ccdik)
|
||
{
|
||
if (ccdik == null || ccdik.solver == null || ccdik.solver.bones == null)
|
||
return "CCDIK 或 bones 无效。";
|
||
|
||
var bones = ccdik.solver.bones;
|
||
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||
sb.AppendLine($"[{ccdik.name}] CCDIK Rod Weights Applied");
|
||
sb.AppendLine($"Bone Count = {bones.Length}");
|
||
sb.AppendLine("Index 0 = 杆尾,Last = 竿稍");
|
||
sb.AppendLine("--------------------------------");
|
||
|
||
for (int i = 0; i < bones.Length; i++)
|
||
{
|
||
string boneName = bones[i].transform != null ? bones[i].transform.name : "NULL";
|
||
sb.AppendLine($"[{i}] {boneName} weight = {bones[i].weight:F3}");
|
||
}
|
||
|
||
return sb.ToString();
|
||
}
|
||
|
||
[MenuItem("Tools/FinalIK/CCDIK/Apply Rod Weights (Selected)", true)]
|
||
[MenuItem("Tools/FinalIK/CCDIK/Apply Rod Weights Soft (Selected)", true)]
|
||
[MenuItem("Tools/FinalIK/CCDIK/Apply Rod Weights Stiff (Selected)", true)]
|
||
private static bool ValidateApplyRodWeights()
|
||
{
|
||
return Selection.activeGameObject != null &&
|
||
Selection.activeGameObject.GetComponent<CCDIK>() != null;
|
||
}
|
||
}
|
||
#endif |