You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中如何保证人形Avatar平行X轴且始终朝向-Z方向?

Unity实时对齐动捕骨架肩向量至X轴并朝向-Z方向

场景与需求

  • 外部实时动作捕捉系统将人体骨架Avatar导入Unity,可直接访问关节位置数据
  • 需要让骨架的左右肩向量(左肩-右肩)始终平行于X轴,同时骨架正面朝向-Z方向,以此适配另一程序的手臂屈曲深度轴旋转数据

当前实现代码

double getParallelAngle = Vector3.Angle(shoulderLeft - shoulderRight, new Vector3(1, 0, 0));

if(getParallelAngle > 0 && getParallelAngle < 100)
{
    skeletonToRoatate.transform.rotation = Quaternion.Euler(skeletonToRoatate.transform.eulerAngles.x, skeletonToRoatate.transform.eulerAngles.y + (float)getParallelAngle, skeletonToRoatate.transform.eulerAngles.z);
}
else
{
    skeletonToRoatate.transform.rotation = Quaternion.Euler(skeletonToRoatate.transform.eulerAngles.x, skeletonToRoatate.transform.eulerAngles.y - (float)getParallelAngle, skeletonToRoatate.transform.eulerAngles.z);
}

现存问题

骨架肩向量大致能平行X轴,但无法稳定保持朝向-Z方向,旋转逻辑存在方向判断误差。

解决方案与优化

核心问题分析

Vector3.Angle只能返回角度的绝对值,无法判断旋转方向(顺时针/逆时针),导致累加角度时容易出现朝向偏差。同时当前逻辑只处理了肩向量对齐,未单独约束骨架朝向,两个需求没有同时满足。

改进代码实现

// 1. 提取肩向量并忽略Y轴分量(仅在XZ平面处理旋转)
Vector3 shoulderVector = shoulderLeft.position - shoulderRight.position;
Vector3 shoulderFlat = new Vector3(shoulderVector.x, 0, shoulderVector.z).normalized;

// 2. 计算肩向量对齐X轴的带符号旋转角度(基于Y轴向上判断方向)
float alignShoulderAngle = Vector3.SignedAngle(shoulderFlat, Vector3.right, Vector3.up);
Quaternion alignShoulderRot = Quaternion.Euler(0, alignShoulderAngle, 0);

// 3. 应用肩向量对齐旋转
skeletonToRoatate.transform.rotation = alignShoulderRot * skeletonToRoatate.transform.rotation;

// 4. 计算骨架朝向对齐-Z的带符号旋转角度
float alignForwardAngle = Vector3.SignedAngle(skeletonToRoatate.transform.forward, Vector3.back, Vector3.up);
Quaternion alignForwardRot = Quaternion.Euler(0, alignForwardAngle, 0);

// 5. 应用朝向对齐旋转,最终同时满足两个需求
skeletonToRoatate.transform.rotation = alignForwardRot * skeletonToRoatate.transform.rotation;

更简洁的组合旋转实现

如果希望一步完成两个约束,可直接通过四元数组合构建目标旋转:

Vector3 shoulderVector = shoulderLeft.position - shoulderRight.position;
Vector3 shoulderFlat = new Vector3(shoulderVector.x, 0, shoulderVector.z).normalized;

// 先将肩向量对齐X轴
Quaternion rotToX = Quaternion.FromToRotation(shoulderFlat, Vector3.right);
// 计算对齐肩向量后的骨架前方向,再旋转至-Z
Vector3 tempForward = rotToX * skeletonToRoatate.transform.forward;
Quaternion rotToBack = Quaternion.FromToRotation(tempForward, Vector3.back);

// 组合旋转并应用
skeletonToRoatate.transform.rotation = rotToBack * rotToX * skeletonToRoatate.transform.rotation;

关键优化点

  • 使用Vector3.SignedAngle替代Vector3.Angle:直接获取带正负的旋转角度,精准控制旋转方向,避免角度累加错误
  • 分离两个约束逻辑:先对齐肩向量,再对齐朝向,确保两个需求同时满足
  • 优先使用四元数操作:避免直接修改欧拉角引发的万向锁问题,旋转更稳定

内容的提问来源于stack exchange,提问作者holouser

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 02:13:13