你好!我正在尝试重建Toribash的核心机制,其中您可以控制单独的肌肉以保持、放松、伸展或收缩。我的当前实现已经基本完成了 - 伸展设置目标旋转为关节的最大限制,收缩设置为最小限制。问题在于当限制之间的差异大于180°时,如果它处于最小限制,并且您设置目标为最大限制,它会尝试取最短路径,即使该旋转是向限制的方向。 我希望有一个简单的方法可以强制旋转的方向为正欧拉旋转(用于伸展)和负欧拉旋转(用于收缩),但我尝试了无数种方法,似乎都没有解决问题。我的四元数理解非常模糊。 是否有办法用可配置的关节来解决这个问题,还是我完全走错了路?谢谢! p.s. 我从 [这个 gist](https://gist.github.com/mstevenson/4958837) 中适应了扩展方法,希望正确。

using NaughtyAttributes;
using UnityEngine;

public class MuscleDriver : MonoBehaviour
{
    private ConfigurableJoint configurableJoint;
    private Quaternion initialRotation;

    // Start 是一个 MonoBehaviour 的方法,会在 Update 方法第一次被调用之前被调用。
    void Start()
    {
        configurableJoint = GetComponent<ConfigurableJoint>();
        initialRotation = transform.localRotation;
        Hold();
    }

    void ChangeState(MuscleDriverMode mode)
    {
        // Helper 来方便地切换驱动力度
        JointDrive activeDrive = new JointDrive
        {
            positionSpring = 1000f,
            positionDamper = 50f,
            maximumForce = float.MaxValue
        };

        JointDrive relaxedDrive = new JointDrive
        {
            positionSpring = 0f,
            positionDamper = 0f,
            maximumForce = 0f
        };

        configurableJoint.slerpDrive = activeDrive;
        switch (mode)
        {
            case MuscleDriverMode.Extend:
                float high = configurableJoint.highAngularXLimit.limit; // 高限制(例如 112 度周围 X)
                Quaternion extendRot = Quaternion.Euler(high, 0, 0);
                configurableJoint.SetTargetRotationLocal(extendRot, initialRotation);
                break;
            case MuscleDriverMode.Contract:
                float low = configurableJoint.lowAngularXLimit.limit; // 低限制(例如 -102 度周围 X)
                Quaternion contractRot = Quaternion.Euler(low, 0, 0);
                configurableJoint.SetTargetRotationLocal(contractRot, initialRotation);
                break;
            case MuscleDriverMode.Hold:
                // 锁定到当前本地旋转
                configurableJoint.SetTargetRotationLocal(transform.localRotation, initialRotation);
                break;
            case MuscleDriverMode.Relax:
                configurableJoint.slerpDrive = relaxedDrive;
                break;
        }
    }
}

public static class JointExtensions
{
    /// <summary>
    /// 设置一个关节的目标旋转使用一个本地空间旋转。
    /// </summary>
    public static void SetTargetRotationLocal(this ConfigurableJoint joint, Quaternion targetLocalRotation, Quaternion startLocalRotation)
    {
        // 计算相对于开始本地旋转的旋转
        Quaternion internalRotation = Quaternion.Inverse(targetLocalRotation) * startLocalRotation;
        // 转换到关节的自定义坐标系统
        Vector3 right = joint.axis;
        Vector3 forward = Vector3.Cross(joint.axis, joint.secondaryAxis);
        Vector3 up = Vector3.Cross(forward, right);
        Quaternion worldToJointSpace = Quaternion.LookRotation(forward, up);
        // 应用关节空间转换
        joint.targetRotation = Quaternion.Inverse(worldToJointSpace) * internalRotation * worldToJointSpace;
    }
}