摄像机区域的优化

This commit is contained in:
2026-05-17 07:56:12 +08:00
parent f264329751
commit d25f237e76
62 changed files with 25774 additions and 5450 deletions

View File

@@ -0,0 +1,85 @@
using UnityEngine;
using Unity.Cinemachine;
namespace BaseGames.Camera
{
/// <summary>
/// Y 轴非对称阻尼扩展。实现下落快、起跳缓的非对称相机追随手感:
/// - <b>下落时快速跟随</b>(低阻尼):玩家落下时相机迅速移动,提前呈现地面地形;
/// - <b>起跳时缓慢上移</b>(高阻尼):相机不会在跳跃峰值前立刻拉高,保留地面视野。
///
/// <para>使用须知:</para>
/// <list type="bullet">
/// <item>挂载在 VCamA / VCamB 上(<see cref="CameraStateController"/> 初始化时自动识别)。</item>
/// <item>此扩展存在时,<see cref="CameraStateController.ConfigureSlot"/> 会自动将
/// <see cref="CinemachinePositionComposer.Damping"/> 的 Y 分量清零,避免双重阻尼。</item>
/// <item>阻尼值可由 <see cref="CameraArea"/> 通过 <c>DampingDown</c> / <c>DampingUp</c>
/// 属性 per-area 覆写,无需手动修改此组件。</item>
/// </list>
/// </summary>
[AddComponentMenu("Cinemachine/Extensions/Camera Asymmetric Damping")]
[DisallowMultipleComponent]
public class CameraAsymmetricDampingExtension : CinemachineExtension
{
[Tooltip("相机向下(下落)时的 Y 轴阻尼(秒)。\n" +
"越小跟随越快,玩家能提前看到地面。推荐 0.05 ~ 0.15。")]
[SerializeField] private float _dampingDown = 0.08f;
[Tooltip("相机向上(起跳)时的 Y 轴阻尼(秒)。\n" +
"越大跟随越慢,保留地面视野、不会在起跳瞬间拉高。推荐 0.5 ~ 0.8。")]
[SerializeField] private float _dampingUp = 0.65f;
/// <summary>供 <see cref="CameraStateController"/> 运行时按区域写入阻尼值。</summary>
public float DampingDown { get => _dampingDown; set => _dampingDown = value; }
/// <summary>供 <see cref="CameraStateController"/> 运行时按区域写入阻尼值。</summary>
public float DampingUp { get => _dampingUp; set => _dampingUp = value; }
private float _smoothedY;
private bool _initialized;
protected override void PostPipelineStageCallback(
CinemachineVirtualCameraBase vcam,
CinemachineCore.Stage stage,
ref CameraState state,
float deltaTime)
{
if (stage != CinemachineCore.Stage.Body) return;
// deltaTime <= 0编辑器预览 / 初始帧,重置平滑器避免脏状态
if (deltaTime <= 0f)
{
_initialized = false;
return;
}
// 此时 state.RawPosition 是 CinemachinePositionComposerDamping.y = 0输出的"理想"位置
float idealY = state.RawPosition.y;
if (!_initialized)
{
_smoothedY = idealY;
_initialized = true;
return;
}
// idealY < _smoothedY → 相机目标在当前位置下方(玩家下落) → 低阻尼快速跟随
// idealY > _smoothedY → 相机目标在当前位置上方(玩家起跳) → 高阻尼缓慢跟随
float damping = idealY < _smoothedY ? _dampingDown : _dampingUp;
float t = damping > 0f
? 1f - Mathf.Exp(-deltaTime / damping)
: 1f;
_smoothedY = Mathf.LerpUnclamped(_smoothedY, idealY, t);
var pos = state.RawPosition;
pos.y = _smoothedY;
state.RawPosition = pos;
}
/// <summary>
/// 重置内部平滑状态。在相机硬切instantCut时由 CameraStateController 调用,
/// 确保新房间的 Y 坐标从目标位置开始,不受旧房间阻尼状态影响。
/// </summary>
public void ResetState() => _initialized = false;
}
}