角色能力,存档

This commit is contained in:
2026-05-19 11:50:21 +08:00
parent d25f237e76
commit 2dcb7a961a
136 changed files with 36035 additions and 27551 deletions

View File

@@ -0,0 +1,164 @@
using UnityEngine;
using Unity.Cinemachine;
namespace BaseGames.Camera
{
/// <summary>
/// 方向感知水平偏置扩展Facing-aware Horizontal Bias
///
/// 持续估算跟随目标的水平移动速度,动态将相机 X 轴向玩家前进方向偏移,
/// 使玩家始终出现在画面偏后侧,前方预留更多可见视野。
///
/// <para>方向逻辑:</para>
/// <list type="bullet">
/// <item>向右移动 → 相机向右偏置 → 玩家出现在屏幕左侧 → 右方视野增加。</item>
/// <item>向左移动 → 相机向左偏置 → 玩家出现在屏幕右侧 → 左方视野增加。</item>
/// <item>静止或速度低于阈值 → 保持当前偏置方向,不回中(避免静止时镜头漂回)。</item>
/// </list>
///
/// <para>
/// 注意:本扩展通过直接修改 <c>state.RawPosition.x</c> 在 Body 阶段之后叠加偏置,
/// 不与 <see cref="CinemachinePositionComposer"/> 的 ScreenPosition.x 冲突;
/// 但偏置量会占用 Confiner 的裁剪空间,在较窄区域中建议通过
/// <see cref="CameraArea._facingBiasOverride"/> 设为较小值或 0 以防止越界。
/// </para>
///
/// <para>挂载顺序(扩展执行顺序由组件顺序决定):</para>
/// <list type="number">
/// <item><see cref="CameraAsymmetricDampingExtension"/> — Y 轴非对称阻尼</item>
/// <item><see cref="CameraFallBiasExtension"/> — 下坠 Y 偏置</item>
/// <item><b>本扩展CameraFacingBiasExtension</b> — X 轴方向偏置</item>
/// <item><c>CinemachineConfiner3D</c> — 最后裁剪至限位边界</item>
/// </list>
/// </summary>
[AddComponentMenu("Cinemachine/Extensions/Camera Facing Bias")]
[DisallowMultipleComponent]
public class CameraFacingBiasExtension : CinemachineExtension
{
[Tooltip("相机沿面朝方向的偏移量(世界单位)。\n" +
"玩家向右时相机向右偏此值,使玩家出现在画面左侧,前方(右侧)视野增加;反之亦然。\n" +
"0 = 禁用。推荐 1.5~2.5,视房间宽度和视口大小而定。")]
[Range(0f, 6f)]
[SerializeField] private float _facingBias = 2f;
[Tooltip("偏置方向切换时的过渡时长(秒)。越大越平滑但延迟越长。\n" +
"推荐 0.3~0.5。")]
[Min(0f)]
[SerializeField] private float _transitionTime = 0.4f;
[Tooltip("触发方向切换所需的最小水平速度(世界单位/秒)。\n" +
"低于此值时不更新目标偏置,防止静止时微小输入造成镜头反复横跳。\n" +
"推荐 1.0~2.0。")]
[Min(0f)]
[SerializeField] private float _directionThreshold = 1.5f;
[Tooltip("水平速度估算的平滑强度(越大响应越快)。推荐 6~10。")]
[Min(0.1f)]
[SerializeField] private float _velocitySmoothing = 8f;
// ── 内部状态 ──────────────────────────────────────────────────────────
private float _currentBiasX; // 当前实际偏置(世界单位,已指数平滑)
private float _targetBiasX; // 当前目标偏置
private float _estimatedVX; // 平滑后的水平速度估算(世界单位/秒)
private float _lastFollowX;
private bool _initialized;
private int _externalFacing = 0; // 0 = 未设置(回退到速度估算);+1 = 朝右;-1 = 朝左
// ── 公开 API ──────────────────────────────────────────────────────────
/// <summary>
/// 供 <see cref="CameraStateController"/> 运行时按区域写入偏置量。
/// 0 = 禁用此区域的方向偏置。
/// </summary>
public float FacingBias { get => _facingBias; set => _facingBias = value; }
/// <summary>
/// 由 PlayerController 在精灵翻转时直接注入面朝方向,使偏置立即响应角色转向。
/// direction: +1 = 朝右,-1 = 朝左0 = 清除外部输入(回退到速度估算)。
/// 外部常量优先于速度估算,即使玩家缓慢行走或原地站立翻转,相机也能立即更新。
/// </summary>
public void SetExternalFacing(int direction)
{
_externalFacing = Mathf.Clamp(direction, -1, 1);
}
/// <summary>
/// 重置内部状态。在相机硬切instantCut时由 CameraStateController 调用,
/// 防止旧房间的方向偏置和速度历史带入新房间。
/// </summary>
public void ResetState()
{
_initialized = false;
_estimatedVX = 0f;
// 硬切时直接吸附到当前目标值,不保留旧房间的过渡惯性
_currentBiasX = _targetBiasX;
}
// ── Cinemachine Extension ─────────────────────────────────────────────
protected override void PostPipelineStageCallback(
CinemachineVirtualCameraBase vcam,
CinemachineCore.Stage stage,
ref CameraState state,
float deltaTime)
{
// 在 Body 阶段之后Composer 已计算出位置)叠加水平偏置
if (stage != CinemachineCore.Stage.Body) return;
// 编辑器预览 / 初始帧:重置平滑器
if (deltaTime <= 0f)
{
_initialized = false;
return;
}
// 偏置量为 0 时跳过,不修改位置
if (_facingBias <= 0f) return;
Transform follow = vcam.Follow;
if (follow == null) return;
// ── 初始化:第一帧直接吸附,不做过渡动画 ────────────────────────
if (!_initialized)
{
_lastFollowX = follow.position.x;
_initialized = true;
_currentBiasX = _targetBiasX;
return;
}
// ── 估算水平速度(帧率无关指数平滑)────────────────────────────
float rawVX = (follow.position.x - _lastFollowX) / deltaTime;
_lastFollowX = follow.position.x;
_estimatedVX = Mathf.Lerp(_estimatedVX, rawVX, 1f - Mathf.Exp(-deltaTime * _velocitySmoothing));
// ── 方向判定(带死区,静止时保持当前目标方向)──────────────────
// 优先使用外部注入朝向PlayerController.OnFlip 传入);
// 未设置时回退到速度估算(却保留静止不回中行为)。
if (_externalFacing != 0)
{
_targetBiasX = _externalFacing * _facingBias;
}
else
{
if (_estimatedVX > _directionThreshold)
_targetBiasX = +_facingBias; // 向右 → 相机右偏 → 玩家在左,前方(右侧)视野增
else if (_estimatedVX < -_directionThreshold)
_targetBiasX = -_facingBias; // 向左 → 相机左偏 → 玩家在右,前方(左侧)视野增
// else: 速度不足,保持 _targetBiasX 不变(不回中)
}
// ── 指数平滑过渡 ─────────────────────────────────────────────────
float t = _transitionTime > 0f
? 1f - Mathf.Exp(-deltaTime / _transitionTime)
: 1f;
_currentBiasX = Mathf.Lerp(_currentBiasX, _targetBiasX, t);
// ── 叠加到 Composer 已计算的位置上 ──────────────────────────────
// 注意:此偏置在 CinemachineConfiner3D 之前施加,
// Confiner 会在之后将结果裁剪回限位边界内。
var pos = state.RawPosition;
pos.x += _currentBiasX;
state.RawPosition = pos;
}
}
}