Unity VR轮子绕自身Z轴旋转脚本的问题解决与实现
Unity 绕自身Z轴旋转的可抓取轮子实现方案
需求
实现Unity中仅绕Z轴旋转的轮子交互脚本,核心要求:
- 可抓取轮子使其跟随手部运动
- 松开后再次从任意位置抓取,不会重置轮子当前旋转状态
初始尝试与问题
初始编写的LookAtScript_v2脚本如下:
public class LookAtScript_v2 : MonoBehaviour { // Testing vars. [SerializeField] TextMeshPro DegreesFromUpToSnapText; // <- Text used for debugging purposes only. // Visible attributes. [SerializeField] private Transform target; // <- Variable that represents the target we're following. [SerializeField] private bool watchingTheObject = true; // <- Variable that mocks the grabbing and letting go of the object. [SerializeField] [Range(1f, 36f)] // <- Change here the range of snaps if needed (future functionality). private int SnapPoints = 1; // Background attributes. // Variables used for rotating the wheel on one axis without it always snapping towards the hand. private float alphaAngle = 0; // Let go of the wheel only once tracker bool letGo = true; void Update() { if(target != null && watchingTheObject) { letGo = false; Vector2 vectorToTarget = target.position - transform.position; // rotatedVector is a vector that points in "the direction of our target" - the angle alpha (angle between our target vector and the direction // our wheel is already facing). Vector2 rotatedVector; // Calculate the angle alpha only the first time we grab the wheel, afterwards it should be constant if (alphaAngle == 0) { alphaAngle = Vector2.SignedAngle(vectorToTarget, transform.right); } // We use rotatedVector to point in the direction where the x axis is currently looking // this allows us to rotate the wheel from its current position, without it always rotating to face our target. rotatedVector = Quaternion.AngleAxis(alphaAngle, Vector3.forward) * vectorToTarget; // Use transform.right to look in the same direction as our rotatedVector. transform.right = rotatedVector.normalized; } else if(!watchingTheObject && !letGo) { alphaAngle = 0; letGo = true; } } }
遇到的问题
该脚本使用Vector2进行计算,导致轮子始终朝向世界Z轴,无法适配任意朝向的父物体,也无法基于局部坐标保持原有旋转逻辑。
解决方案
层级结构调整
需创建三层物体结构:
- 父物体(用于整体定位)
- 枢轴点(Pivot Point,作为旋转中心,需与轮子中心重合)
- 轮子本体(Valve,挂载脚本,仅绕自身Z轴旋转)
最终可用脚本 LookAtScript_v3
using UnityEngine; public class LookAtScript_v3 : MonoBehaviour { [SerializeField] private Transform target; // 跟随目标,替代VR手部 [SerializeField] private bool watchingTheObject = false; // 模拟抓取/松开状态(无VR设备时测试用) [SerializeField] private Transform pivotPoint; // 轮子旋转的枢轴中心点 private float alphaAngle = 0; // 轮子当前朝向与目标方向的初始夹角 private float newAngle = 0; private float previousAngle = 0; // 用于判断旋转方向(顺时针/逆时针) private float rotationSpeed = 400f; // 旋转平滑速度 void Update() { if(target != null && watchingTheObject) { Vector3 vectorToTarget = target.position - pivotPoint.position; Vector3 vectorToTargetLocal = pivotPoint.transform.InverseTransformDirection(vectorToTarget); vectorToTargetLocal = Quaternion.Euler(pivotPoint.rotation.eulerAngles) * vectorToTargetLocal; // 首次抓取时计算初始夹角 if (alphaAngle == 0) { alphaAngle = Vector3.SignedAngle(vectorToTargetLocal, transform.up, pivotPoint.forward); } Vector3 rotatedVectorToTarget = Quaternion.AngleAxis(alphaAngle, transform.forward) * vectorToTargetLocal; // 获取目标旋转角度(绕局部Z轴) float targetAngle = Vector3.SignedAngle(pivotPoint.up, rotatedVectorToTarget, pivotPoint.forward); // 获取轮子当前绕局部Z轴的角度 float currentAngle = transform.localEulerAngles.z; // 将角度转换为-180~180范围,避免旋转跳变 if (currentAngle - alphaAngle > 180) currentAngle -= 360; if (targetAngle - alphaAngle > 180) targetAngle -= 360; // 平滑旋转到目标角度 newAngle = Mathf.MoveTowardsAngle(currentAngle, targetAngle, rotationSpeed * Time.deltaTime); // 应用旋转(仅绕局部Z轴) transform.localEulerAngles = new Vector3(0, 0, newAngle); previousAngle = newAngle; } else if(!watchingTheObject) { alphaAngle = 0; } } }
效果说明
该脚本可实现:
- 轮子仅绕自身局部Z轴旋转
- 松开后再次抓取,会保留当前旋转状态
- 不受父物体整体朝向影响,适配任意场景布局
内容的提问来源于stack exchange,提问作者Viktor291
相关产品推荐
相关产品推荐

