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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 17:06:28