优化移动平台与乘客的速度继承逻辑,改用双缓冲方案以提升平滑度和兼容性
This commit is contained in:
@@ -8,21 +8,26 @@ namespace BaseGames.Core
|
||||
/// 无需修改 Transform 父子关系,避免与动画/摄像机/HitBox 系统冲突。
|
||||
///
|
||||
/// 协议(执行顺序保障):
|
||||
/// 1. MovingPlatform.FixedUpdate(-300)计算本帧期望位移 delta → 调用 SetPlatformDelta。
|
||||
/// 2. 实现方 FixedUpdate(-200)最前端消费 delta:_rb.position += delta。
|
||||
/// 3. 乘客离开传感器时 MovingPlatform 调用 OnLeavePlatform,继承平台速度防止卡顿。
|
||||
/// 1. MovingPlatform.FixedUpdate(-300)计算本帧期望位移 delta → 调用 SetPlatformDelta,
|
||||
/// 实现方将其转换为速度(delta / fixedDeltaTime)存入下帧缓冲区。
|
||||
/// 2. 实现方 FixedUpdate(-200)将缓冲区速度换入当前帧变量。
|
||||
/// 3. 状态机 FixedUpdate(-100)通过 Move() 将平台速度叠加到水平速度,
|
||||
/// 由 velocity 驱动运动,与 RigidbodyInterpolation2D.Interpolate 完全兼容。
|
||||
/// 4. 乘客离开传感器时 MovingPlatform 调用 OnLeavePlatform,继承垂直速度分量;
|
||||
/// 水平分量已通过最后一次 Move() 自然携带,无需重复叠加。
|
||||
/// </summary>
|
||||
public interface IPassengerReceiver
|
||||
{
|
||||
/// <summary>
|
||||
/// 由 MovingPlatform 每帧推送本帧平台期望位移量(不依赖 MovePosition 是否已执行)。
|
||||
/// 实现方存入待处理字段,在自己的 FixedUpdate 最前端消费,避免与 velocity 冲突。
|
||||
/// 由 MovingPlatform 每帧推送本帧平台期望位移量。
|
||||
/// 实现方将其转换为速度并存入缓冲区,在自己的 FixedUpdate 换入后
|
||||
/// 经 Move() 叠加到水平速度,保持插值平滑。
|
||||
/// </summary>
|
||||
void SetPlatformDelta(Vector2 delta);
|
||||
|
||||
/// <summary>
|
||||
/// 乘客离开平台时调用,传入平台当前帧速度,由实现方叠加到自身速度。
|
||||
/// 防止离台瞬间速度突变产生卡顿。
|
||||
/// 乘客离开平台时调用,传入平台当前帧速度。
|
||||
/// 实现方仅需继承垂直分量;水平分量已由最后一次 Move() 自然携带。
|
||||
/// </summary>
|
||||
void OnLeavePlatform(Vector2 platformVelocity);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,10 @@ namespace BaseGames.Player
|
||||
// 下一个 FixedUpdate(-200,先于状态机 -100)读取并清零,
|
||||
// 防止状态机用旧输入把速度重新写成非零值。
|
||||
private bool _pendingHorizontalZero;
|
||||
private Vector2 _pendingPlatformDelta; // MovingPlatform 推送的本帧位移,在 FixedUpdate 最前端消费
|
||||
// 移动平台速度双缓冲:Platform(-300) 写入 _next;Player(-200) 将 _next 换入 _current 并清零 _next;
|
||||
// StateM(-100) 的 Move() 读取 _current,确保速度基于 velocity 设置,与 Interpolate 系统兼容。
|
||||
private Vector2 _platformVelocity; // 本帧消费(StateM 通过 Move() 读取)
|
||||
private Vector2 _nextPlatformVelocity; // Platform(-300) 写入缓冲区
|
||||
private bool _isWallLeft;
|
||||
private bool _isWallRight;
|
||||
private bool _onOneWayPlatform;
|
||||
@@ -84,12 +87,11 @@ namespace BaseGames.Player
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
// 消费移动平台推送的位移(在 velocity 系统之前直接写 position,避免冲突)
|
||||
if (_pendingPlatformDelta != Vector2.zero)
|
||||
{
|
||||
_rb.position += _pendingPlatformDelta;
|
||||
_pendingPlatformDelta = Vector2.zero;
|
||||
}
|
||||
// 平台速度双缓冲换入:Platform(-300) 已将本帧速度写入 _next;
|
||||
// 这里将其换入 _current 供 Move() 叠加,并清零 _next 以备下帧使用。
|
||||
// 若本帧玩家已离台,Platform 不写 _next,换入结果为 Vector2.zero,自然恢复独立运动。
|
||||
_platformVelocity = _nextPlatformVelocity;
|
||||
_nextPlatformVelocity = Vector2.zero;
|
||||
|
||||
// 优先处理来自 Update 的强制清零请求(在状态机 OnStateFixedUpdate 之前执行)。
|
||||
if (_pendingHorizontalZero)
|
||||
@@ -128,10 +130,11 @@ namespace BaseGames.Player
|
||||
/// 直接赋予目标水平速度(按键即全速,松键即停,无加速过渡)。
|
||||
/// 地面状态每帧直接到达全速;空中调用时同样即时,但配合 ApplyAirDrag
|
||||
/// 在无输入时自然减速,保留跳出时的动量。
|
||||
/// 若当前站在移动平台上,自动叠加 <c>_platformVelocity.x</c>,保持与平台同步。
|
||||
/// </summary>
|
||||
public void Move(float speedX)
|
||||
{
|
||||
_rb.velocity = new Vector2(speedX, _rb.velocity.y);
|
||||
_rb.velocity = new Vector2(speedX + _platformVelocity.x, _rb.velocity.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -189,7 +192,9 @@ namespace BaseGames.Player
|
||||
// ── 朝向 ──────────────────────────────────────────────────────────────
|
||||
public void UpdateFacing()
|
||||
{
|
||||
float vx = _rb.velocity.x;
|
||||
// 减去平台速度分量,只根据玩家自身输入速度判断朝向;
|
||||
// 否则站在横向移动平台上静止时会被平台速度驱动朝向翻转。
|
||||
float vx = _rb.velocity.x - _platformVelocity.x;
|
||||
if (Mathf.Abs(vx) < 0.1f) return;
|
||||
int dir = vx > 0f ? 1 : -1;
|
||||
if (dir == _facingDirection) return;
|
||||
@@ -231,14 +236,22 @@ namespace BaseGames.Player
|
||||
|
||||
// ── IPassengerReceiver ────────────────────────────────────────────────
|
||||
/// <summary>
|
||||
/// MovingPlatform.FixedUpdate(-300) 推送本帧平台位移。
|
||||
/// 在本类 FixedUpdate(-200) 最前端消费,直接写 _rb.position,
|
||||
/// 与 velocity 系统完全隔离,避免 Dynamic Rigidbody 上 MovePosition + velocity 叠加。
|
||||
/// MovingPlatform.FixedUpdate(-300) 推送本帧平台期望位移。
|
||||
/// 转换为速度(delta / fixedDeltaTime)写入下帧缓冲区 _next,
|
||||
/// 在本类 FixedUpdate(-200) 换入 _current,由 Move() 叠加到水平速度。
|
||||
/// 基于 velocity 的方案与 RigidbodyInterpolation2D.Interpolate 完全兼容,
|
||||
/// 消除直接写 _rb.position 导致的视觉抖动/速度不一致。
|
||||
/// </summary>
|
||||
public void SetPlatformDelta(Vector2 delta) => _pendingPlatformDelta += delta;
|
||||
public void SetPlatformDelta(Vector2 delta)
|
||||
=> _nextPlatformVelocity += delta / Time.fixedDeltaTime;
|
||||
|
||||
/// <summary>离开移动平台时继承平台速度,防止速度骤变卡顿。</summary>
|
||||
public void OnLeavePlatform(Vector2 platformVelocity) => _rb.velocity += platformVelocity;
|
||||
/// <summary>
|
||||
/// 乘客离开平台时调用。
|
||||
/// 水平速度已通过 Move() 自然继承(最后一次 Move 调用已包含 platformVelocity.x),
|
||||
/// 此处仅继承垂直分量,保证从竖向移动平台离台时无速度突变。
|
||||
/// </summary>
|
||||
public void OnLeavePlatform(Vector2 platformVelocity)
|
||||
=> _rb.velocity += new Vector2(0f, platformVelocity.y);
|
||||
|
||||
// ── 冲刺 ──────────────────────────────────────────────────────────────
|
||||
/// <summary>
|
||||
|
||||
@@ -9,11 +9,12 @@ namespace BaseGames.World
|
||||
/// <summary>
|
||||
/// 移动平台。Kinematic Rigidbody2D,支持三种移动模式。
|
||||
///
|
||||
/// 乘客跟随(Delta Position 方案,替代 SetParent):
|
||||
/// - PassengerSensor(Trigger)检测进入乘客(<see cref="IPassengerReceiver"/>)。
|
||||
/// - FixedUpdate(-300) 计算本帧期望位移 delta,推送给所有乘客,再执行 MovePosition。
|
||||
/// - 乘客在自己的 FixedUpdate(-200) 最前端消费 delta:_rb.position += delta。
|
||||
/// - 乘客离台时通过 OnLeavePlatform 继承平台速度,避免卡顿。
|
||||
/// 乘客跟随(Velocity 双缓冲方案):
|
||||
/// - PassengerSensor(Trigger)通过 attachedRigidbody 检测乘客(<see cref="IPassengerReceiver"/>)。
|
||||
/// - FixedUpdate(-300) 计算本帧期望位移 delta → SetPlatformDelta(delta) 写入乘客缓冲区。
|
||||
/// - 乘客 FixedUpdate(-200) 换入缓冲速度;状态机 FixedUpdate(-100) 的 Move() 将其叠加到 velocity。
|
||||
/// - 基于 velocity 驱动,与 RigidbodyInterpolation2D.Interpolate 完全兼容,无视觉抖动。
|
||||
/// - 乘客离台时 OnLeavePlatform 继承垂直速度分量;水平分量已由 Move() 自然携带。
|
||||
///
|
||||
/// 此方案不修改 Transform 层级,与动画/摄像机/HitBox 系统完全兼容。
|
||||
/// </summary>
|
||||
@@ -143,17 +144,20 @@ namespace BaseGames.World
|
||||
private void OnTriggerEnter2D(Collider2D other)
|
||||
{
|
||||
if (!IsPassengerLayer(other)) return;
|
||||
if (!other.TryGetComponent<IPassengerReceiver>(out var receiver)) return;
|
||||
// attachedRigidbody:若玩家碰撞体在子对象上,也能正确找到根对象上的 IPassengerReceiver
|
||||
if (!other.attachedRigidbody) return;
|
||||
if (!other.attachedRigidbody.TryGetComponent<IPassengerReceiver>(out var receiver)) return;
|
||||
if (!_passengers.Contains(receiver))
|
||||
_passengers.Add(receiver);
|
||||
}
|
||||
|
||||
private void OnTriggerExit2D(Collider2D other)
|
||||
{
|
||||
if (!other.TryGetComponent<IPassengerReceiver>(out var receiver)) return;
|
||||
if (!other.attachedRigidbody) return;
|
||||
if (!other.attachedRigidbody.TryGetComponent<IPassengerReceiver>(out var receiver)) return;
|
||||
if (!_passengers.Remove(receiver)) return;
|
||||
|
||||
// 继承平台速度,防止离台时速度骤变卡顿
|
||||
// 继承平台垂直速度;水平速度已由 IPassengerReceiver.Move() 自然携带,无需重复叠加
|
||||
receiver.OnLeavePlatform(_frameVelocity);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user