Unity RotateAround如何实现无抖动?自研2D绕点旋转为何抖动?
2D场景绕指定点旋转GameObject抖动问题排查
自研旋转实现(存在抖动)
public override IEnumerator transformPlayer(MyTransform target, GameObject selection) { yield return new WaitForFixedUpdate(); Vector3 around = SelectionScript.Selection.transform.position; Vector3 to = target.Position - around; Vector3 from = gg.Player.Position - around; Quaternion oRot = gg.Player.Rotation; int totalDeg = Mathf.RoundToInt(Mathf.Abs(Mathf.Acos(Vector2.Dot(((Vector2)to).normalized, ((Vector2)from).normalized))) / Mathf.PI * 180); float total = totalDeg / 180.0f * Mathf.PI; int mul = GraphicsCore.IsLeftTo(to, from, Vector3.forward) ? 1 : -1; float angle = 0; float v = 0; // float prev = 0; do { angle = Mathf.SmoothDampAngle(angle, total, ref v, 0.33f); Quaternion absRot = Quaternion.AngleAxis(angle / Mathf.PI * 180 * mul, Vector3.forward); gg.Player.Rotation = oRot * absRot; gg.Player.Position = absRot * from + around; // gg.Player.transform.RotateAround(around, Vector3.forward, (angle - prev) * mul / Mathf.PI * 180); // prev = angle; yield return null; } while ((total - angle) > 0.01f); gg.Player.Position = target.Position; gg.Player.Rotation = target.Rotation; }
该逻辑粗看旋转效果正常,但仔细观察会发现旋转过程中存在微小随机抖动,在SmoothDamp动画后期低速阶段尤为明显。尝试过基于前一状态计算旋转、使用Quaternion.Slerp等方法,均无法解决抖动问题。
改用Unity内置Transform.RotateAround后的实现(无抖动)
public override IEnumerator transformPlayer(MyTransform target, GameObject selection) { yield return new WaitForFixedUpdate(); Vector3 around = SelectionScript.Selection.transform.position; Vector3 to = target.Position - around; Vector3 from = gg.Player.Position - around; // Quaternion oRot = gg.Player.Rotation; int totalDeg = Mathf.RoundToInt(Mathf.Abs(Mathf.Acos(Vector2.Dot(((Vector2)to).normalized, ((Vector2)from).normalized))) / Mathf.PI * 180); float total = totalDeg / 180.0f * Mathf.PI; int mul = GraphicsCore.IsLeftTo(to, from, Vector3.forward) ? 1 : -1; float angle = 0; float v = 0; float prev = 0; do { angle = Mathf.SmoothDampAngle(angle, total, ref v, 0.33f); // Quaternion absRot = Quaternion.AngleAxis(angle / Mathf.PI * 180 * mul, Vector3.forward); /*gg.Player.Rotation = oRot * absRot; // gg.Player.Position = absRot * from + around;*/ gg.Player.transform.RotateAround(around, Vector3.forward, (angle - prev) * mul / Mathf.PI * 180); prev = angle; yield return null; } while ((total - angle) > 0.01f); gg.Player.Position = target.Position; gg.Player.Rotation = target.Rotation; }
问题原因分析与修复建议
自研实现的核心问题
- 浮点精度累积误差:每一帧基于初始偏移向量
from计算绝对位置,多次浮点运算的微小误差会随着动画推进累积,在后期角度变化极小时,误差被放大导致抖动。而RotateAround采用增量更新逻辑,基于当前Transform状态做微小调整,Unity内部对这类运算做了精度优化,减少了误差累积。 - 状态同步不一致:直接用初始旋转
oRot乘以绝对旋转absRot设置旋转,同时用初始偏移计算位置,两者没有和每一帧的实时状态绑定。当SmoothDampAngle输出的角度有微小波动时,位置和旋转的计算会出现细微不匹配,引发抖动。 - 冗余的角度转换:代码中多次在弧度和角度之间转换(比如
angle / Mathf.PI * 180),额外的转换步骤会引入不必要的浮点误差,而RotateAround直接使用角度增量,避免了这类损耗。
自研实现的修复方案
如果想保留自研逻辑,可做以下调整:
- 切换为增量更新模式:参考
RotateAround的思路,每一帧计算角度变化量deltaAngle = angle - prev,然后基于当前位置和旋转做增量旋转,而非每次基于初始状态计算绝对位置。 - 减少浮点转换:全程使用弧度(或全程使用角度)进行计算,避免多次转换带来的精度损失。
- 统一状态依赖:确保位置和旋转的计算都依赖上一帧的实时状态,而非初始状态,保持两者的同步性。
内容的提问来源于stack exchange,提问作者Patrik Nusszer
相关产品推荐
相关产品推荐

