This commit is contained in:
JA
2026-06-28 12:25:22 +08:00
parent c796465500
commit 6050aa0e4c
4 changed files with 220 additions and 9 deletions

View File

@@ -3,25 +3,44 @@ using UnityEngine;
namespace IndianOceanAssets.Engine2_5D
{
/// <summary>
/// 场景物件面向摄像机脚本(饥荒风格)。
/// 场景物件面向摄像机脚本(饥荒风格)v2
/// 让物体始终朝摄像机方向旋转,默认只绕 Y 轴(保持竖直不倒)。
///
/// 抖动修复v2
/// 旧版平滑旋转用 Quaternion.Slerp(rot, target, speed * deltaTime)
/// 它永远无法真正到达目标(每帧只走剩余距离的几分之一),叠加 deltaTime
/// 逐帧波动,会持续产生微小旋转 → 看起来在抖动。若摄像机本身也用 Lerp
/// 跟随CameraFollow 永不收敛),两者叠加更明显。
///
/// 修复要点:
/// 1. 指数平滑 t = 1 - exp(-speed * dt),帧率无关,比 speed * dt 更稳定。
/// 2. 剩余角度 ≤ snapAngle 时直接吸附到目标,杜绝永不收敛的微旋转。
/// 3. 已吸附后,目标方向在 deadZone 内的微小漂移(摄像机/物件微抖)一律忽略,
/// 只有方向变化超过 deadZone 才重新吸附 → 摄像机静止时物件彻底不动。
///
/// 用法:挂到需要面向摄像机的精灵/物件上即可。
/// 适合:树木、岩石、角色、道具等 2D 精灵在 3D 场景中的朝向修正。
/// </summary>
public class BillboardSprite : MonoBehaviour
{
[Tooltip("只绕Y轴旋转饥荒风格保持竖直不倒。关闭则完全面向摄像机。")]
[SerializeField] private bool lockYAxis = true;
[Tooltip("平滑旋转(lerp过渡),关闭则瞬间转向")]
[Tooltip("平滑旋转(过渡),关闭则瞬间转向")]
[SerializeField] private bool smoothRotation = false;
[Tooltip("平滑旋转速度")]
[Tooltip("平滑旋转速度(越大转得越快)")]
[SerializeField] private float rotationSpeed = 8f;
[Tooltip("剩余角度小于此值时直接吸附到目标,避免永不收敛的微旋转(主要抖动来源)。")]
[SerializeField] private float snapAngle = 0.5f;
[Tooltip("已吸附后,目标方向在此角度内的微小漂移视为噪声忽略(过滤摄像机微抖)。")]
[SerializeField] private float deadZone = 0.2f;
private Camera _mainCamera;
private Transform _camTransform;
private Quaternion _convergedRot;
private bool _hasConverged;
private void Start()
{
@@ -50,11 +69,35 @@ namespace IndianOceanAssets.Engine2_5D
Quaternion targetRot = Quaternion.LookRotation(dir);
if (smoothRotation)
transform.rotation = Quaternion.Slerp(transform.rotation, targetRot,
rotationSpeed * Time.deltaTime);
else
if (!smoothRotation)
{
transform.rotation = targetRot;
_convergedRot = targetRot;
_hasConverged = true;
return;
}
// ---- 平滑旋转 ----
float angleToTarget = Quaternion.Angle(transform.rotation, targetRot);
if (angleToTarget <= snapAngle)
{
// 已接近目标。仅当目标相对上次吸附转了超过 deadZone 才重新吸附,
// 否则视为噪声保持不动(摄像机静止时物件不抖)
if (!_hasConverged || Quaternion.Angle(targetRot, _convergedRot) > deadZone)
{
transform.rotation = targetRot;
_convergedRot = targetRot;
_hasConverged = true;
}
}
else
{
// 还差较远,指数平滑趋近(帧率无关,比 speed*deltaTime 更稳)
float t = 1f - Mathf.Exp(-rotationSpeed * Time.deltaTime);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRot, t);
_hasConverged = false;
}
}
}
}