Unity中2D对象接触碰撞器时Z轴平滑旋转问题
问题分析
你的核心问题是**OnTriggerEnter2D仅执行一次**,导致平滑旋转的插值逻辑只运行了一帧,还没完成过渡就被OnTriggerStay2D里的直接赋值覆盖,所以旋转呈现跳变,完全没有平滑效果。
修复方案
要实现进入时平滑旋转、停留时实时更新的需求,需要把平滑逻辑改成持续执行,直到玩家旋转到目标角度后再切换到实时更新模式。以下是修改后的完整代码:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GravitationalBody : MonoBehaviour { [Range(5f, 20f)] [SerializeField] private float m_OrientationSmoothingSpeed = 10f; // 平滑旋转速度,数值越大旋转越快 [SerializeField] private float m_RotationThreshold = 1f; // 旋转差值小于该值时,停止平滑并直接对齐 // 存储需要平滑旋转的对象及其目标角度 private Dictionary<Collider2D, Quaternion> _smoothingObjects = new Dictionary<Collider2D, Quaternion>(); void OnTriggerEnter2D(Collider2D obj) { Rigidbody2D rb = obj.GetComponent<Rigidbody2D>(); if (rb == null) return; Vector3 gravityDirection = transform.position - obj.transform.position; rb.AddForce(gravityDirection.normalized * 5f); // 归一化后乘以系数,避免引力过大 // 计算目标旋转角度 float orientation = 90 + Mathf.Atan2(gravityDirection.y, gravityDirection.x) * Mathf.Rad2Deg; Quaternion targetRot = Quaternion.Euler(0f, 0f, orientation); // 若当前旋转与目标差值较大,加入平滑队列 if (Quaternion.Angle(obj.transform.rotation, targetRot) > m_RotationThreshold) { if (_smoothingObjects.ContainsKey(obj)) _smoothingObjects[obj] = targetRot; else _smoothingObjects.Add(obj, targetRot); } else { obj.transform.rotation = targetRot; } } void OnTriggerStay2D(Collider2D obj) { Rigidbody2D rb = obj.GetComponent<Rigidbody2D>(); if (rb == null) return; Vector3 gravityDirection = transform.position - obj.transform.position; rb.AddForce(gravityDirection.normalized * 5f); // 不在平滑状态时,直接实时更新旋转 if (!_smoothingObjects.ContainsKey(obj)) { float orientation = 90 + Mathf.Atan2(gravityDirection.y, gravityDirection.x) * Mathf.Rad2Deg; obj.transform.rotation = Quaternion.Euler(0f, 0f, orientation); } else { // 平滑过程中同步更新目标旋转(适配玩家移动后的角度变化) float orientation = 90 + Mathf.Atan2(gravityDirection.y, gravityDirection.x) * Mathf.Rad2Deg; _smoothingObjects[obj] = Quaternion.Euler(0f, 0f, orientation); } } void OnTriggerExit2D(Collider2D obj) { // 离开引力范围时移除平滑队列 if (_smoothingObjects.ContainsKey(obj)) _smoothingObjects.Remove(obj); } void FixedUpdate() { List<Collider2D> completedObjects = new List<Collider2D>(); foreach (var pair in _smoothingObjects) { Collider2D obj = pair.Key; Quaternion targetRot = pair.Value; // 用Slerp做球面插值,旋转过渡更自然 obj.transform.rotation = Quaternion.Slerp(obj.transform.rotation, targetRot, m_OrientationSmoothingSpeed * Time.fixedDeltaTime); // 检查是否已接近目标旋转,是则结束平滑 if (Quaternion.Angle(obj.transform.rotation, targetRot) <= m_RotationThreshold) { obj.transform.rotation = targetRot; completedObjects.Add(obj); } } // 移除完成平滑的对象 foreach (var obj in completedObjects) _smoothingObjects.Remove(obj); } }
关键改动说明
- 用
Dictionary维护平滑状态:跟踪需要平滑的对象和对应的目标旋转,避免单次调用插值无效的问题 FixedUpdate中持续执行平滑:和刚体物理逻辑同步,避免帧抖动,确保平滑过程完整执行- 替换
Lerp为Slerp:球面插值更适合旋转操作,大角度过渡时不会出现速度不均的问题 - 添加旋转阈值:避免无限接近目标角度的情况,差值足够小时直接对齐目标
- 优化引力逻辑:对引力方向归一化后乘以系数,避免距离过近时引力过大导致的异常
内容的提问来源于stack exchange,提问作者MarkHughes88
相关产品推荐
相关产品推荐

