优化移动平台与乘客的速度处理逻辑,改用位移累积方案以提升运动平滑度和代码清晰度

This commit is contained in:
2026-05-21 19:08:18 +08:00
parent c7c8171b8a
commit 39483c4460
2 changed files with 34 additions and 32 deletions

View File

@@ -9,25 +9,28 @@ namespace BaseGames.Core
///
/// 协议(执行顺序保障):
/// 1. MovingPlatform.FixedUpdate-300计算本帧期望位移 delta → 调用 SetPlatformDelta
/// 实现方将其转换为速度delta / fixedDeltaTime存入下帧缓冲区
/// 2. 实现方 FixedUpdate-200将缓冲区速度换入当前帧变量
/// 3. 状态机 FixedUpdate-100通过 Move() 将平台速度叠加到水平速度,
/// 由 velocity 驱动运动,与 RigidbodyInterpolation2D.Interpolate 完全兼容。
/// 4. 乘客离开传感器时 MovingPlatform 调用 OnLeavePlatform继承垂直速度分量
/// 水平分量已通过最后一次 Move() 自然携带,无需重复叠加
/// 实现方将其累积到 _pendingPlatformDelta
/// 2. 实现方 FixedUpdate-200执行 _rb.position += _pendingPlatformDelta 并清零
/// 使用 Rigidbody2D.position而非 transform.positionUnity 保证在 FixedUpdate 中
/// 赋值时会同步更新插值参考点,不引起视觉抖动,与 RigidbodyInterpolation2D.Interpolate 兼容。
/// 3. 状态机 FixedUpdate-100正常调用 Move(speedX)_rb.velocity.x 仅含玩家输入速度,
/// UpdateFacing / ApplyAirDrag 无需减去任何平台速度,语义清晰
/// 4. 乘客离开传感器时 MovingPlatform 调用 OnLeavePlatform继承完整平台速度含水平
/// 保证从移动平台起跳时具有平台动量,手感自然。
/// </summary>
public interface IPassengerReceiver
{
/// <summary>
/// 由 MovingPlatform 每帧推送本帧平台期望位移量。
/// 实现方将其转换为速度并存入缓冲区,在自己的 FixedUpdate 换入后
/// 经 Move() 叠加到水平速度,保持插值平滑
/// 实现方将其累积到内部字段,在自己的 FixedUpdate 中通过 Rigidbody2D.position
/// 直接施加位移,不混入 velocity保持玩家速度语义纯净
/// </summary>
void SetPlatformDelta(Vector2 delta);
/// <summary>
/// 乘客离开平台时调用,传入平台当前帧速度。
/// 实现方仅需继承垂直分量;水平分量已由最后一次 Move() 自然携带。
/// 实现方应将完整平台速度(含水平)叠加到自身 velocity
/// 保证离台瞬间具有平台动量,手感自然。
/// </summary>
void OnLeavePlatform(Vector2 platformVelocity);
}

View File

@@ -31,10 +31,11 @@ namespace BaseGames.Player
// 下一个 FixedUpdate-200先于状态机 -100读取并清零
// 防止状态机用旧输入把速度重新写成非零值。
private bool _pendingHorizontalZero;
// 移动平台速度双缓冲Platform(-300) 写入 _nextPlayer(-200) 将 _next 换入 _current 并清零 _next
// StateM(-100) 的 Move() 读取 _current确保速度基于 velocity 设置,与 Interpolate 系统兼容。
private Vector2 _platformVelocity; // 本帧消费StateM 通过 Move() 读取)
private Vector2 _nextPlatformVelocity; // Platform(-300) 写入缓冲区
// 移动平台位移累积Platform(-300) 写入Player(-200) 通过 _rb.position += 消费后清零。
// 使用位置偏移而非速度叠加_rb.velocity.x 保持纯粹的玩家输入速度,
// UpdateFacing / ApplyAirDrag 无需任何修正。
// Unity 文档明确FixedUpdate 中设置 Rigidbody2D.position 与 Interpolate 完全兼容。
private Vector2 _pendingPlatformDelta;
private bool _isWallLeft;
private bool _isWallRight;
private bool _onOneWayPlatform;
@@ -87,11 +88,11 @@ namespace BaseGames.Player
private void FixedUpdate()
{
// 平台速度双缓冲换入Platform(-300) 已将本帧速度写入 _next
// 这里将其换入 _current 供 Move() 叠加,并清零 _next 以备下帧使用。
// 若本帧玩家已离台Platform 不写 _next换入结果为 Vector2.zero自然恢复独立运动。
_platformVelocity = _nextPlatformVelocity;
_nextPlatformVelocity = Vector2.zero;
// 消费本帧平台位移:直接修改物理位置,不污染 _rb.velocity。
// Rigidbody2D.position 在 FixedUpdate 内赋值时Unity 会同步更新插值参考点,
// 不会引起视觉抖动。
_rb.position += _pendingPlatformDelta;
_pendingPlatformDelta = Vector2.zero;
// 优先处理来自 Update 的强制清零请求(在状态机 OnStateFixedUpdate 之前执行)。
if (_pendingHorizontalZero)
@@ -130,11 +131,11 @@ namespace BaseGames.Player
/// 直接赋予目标水平速度(按键即全速,松键即停,无加速过渡)。
/// 地面状态每帧直接到达全速;空中调用时同样即时,但配合 ApplyAirDrag
/// 在无输入时自然减速,保留跳出时的动量。
/// 若当前站在移动平台上,自动叠加 <c>_platformVelocity.x</c>,保持与平台同步
/// 平台携带通过 _rb.position 偏移实现,此处无需叠加平台速度
/// </summary>
public void Move(float speedX)
{
_rb.velocity = new Vector2(speedX + _platformVelocity.x, _rb.velocity.y);
_rb.velocity = new Vector2(speedX, _rb.velocity.y);
}
/// <summary>
@@ -192,9 +193,9 @@ namespace BaseGames.Player
// ── 朝向 ──────────────────────────────────────────────────────────────
public void UpdateFacing()
{
// 减去平台速度分量,只根据玩家自身输入速度判断朝向;
// 否则站在横向移动平台上静止时会被平台速度驱动朝向翻转
float vx = _rb.velocity.x - _platformVelocity.x;
// _rb.velocity.x 是纯玩家输入速度(平台携带通过位置偏移,不混入 velocity
// 直接读取即可,无需减去平台速度
float vx = _rb.velocity.x;
if (Mathf.Abs(vx) < 0.1f) return;
int dir = vx > 0f ? 1 : -1;
if (dir == _facingDirection) return;
@@ -237,21 +238,19 @@ namespace BaseGames.Player
// ── IPassengerReceiver ────────────────────────────────────────────────
/// <summary>
/// MovingPlatform.FixedUpdate(-300) 推送本帧平台期望位移。
/// 转换为速度delta / fixedDeltaTime写入下帧缓冲区 _next
/// 在本类 FixedUpdate(-200) 换入 _current由 Move() 叠加到水平速度
/// 基于 velocity 的方案与 RigidbodyInterpolation2D.Interpolate 完全兼容,
/// 消除直接写 _rb.position 导致的视觉抖动/速度不一致。
/// 累积到 _pendingPlatformDelta在本类 FixedUpdate(-200) 通过 _rb.position += 消费
/// 与玩家 velocity 完全解耦UpdateFacing / ApplyAirDrag 无需修正
/// </summary>
public void SetPlatformDelta(Vector2 delta)
=> _nextPlatformVelocity += delta / Time.fixedDeltaTime;
=> _pendingPlatformDelta += delta;
/// <summary>
/// 乘客离开平台时调用。
/// 水平速度已通过 Move() 自然继承(最后一次 Move 调用已包含 platformVelocity.x
/// 此处仅继承垂直分量,保证从竖向移动平台离台时无速度突变
/// 乘客离开平台时调用,传入平台当前帧速度
/// 此方案下 _rb.velocity 为纯玩家速度,离台时直接继承平台完整速度(含水平
/// 保证从移动平台起跳后具有平台动量,手感自然
/// </summary>
public void OnLeavePlatform(Vector2 platformVelocity)
=> _rb.velocity += new Vector2(0f, platformVelocity.y);
=> _rb.velocity += platformVelocity;
// ── 冲刺 ──────────────────────────────────────────────────────────────
/// <summary>