优化移动平台与玩家的交互逻辑,采用速度双缓冲方案以提升运动平滑度和代码清晰度
This commit is contained in:
@@ -31,11 +31,14 @@ namespace BaseGames.Player
|
||||
// 下一个 FixedUpdate(-200,先于状态机 -100)读取并清零,
|
||||
// 防止状态机用旧输入把速度重新写成非零值。
|
||||
private bool _pendingHorizontalZero;
|
||||
// 移动平台位移累积:Platform(-300) 写入;Player(-200) 通过 _rb.position += 消费后清零。
|
||||
// 使用位置偏移而非速度叠加,_rb.velocity.x 保持纯粹的玩家输入速度,
|
||||
// UpdateFacing / ApplyAirDrag 无需任何修正。
|
||||
// Unity 文档明确:FixedUpdate 中设置 Rigidbody2D.position 与 Interpolate 完全兼容。
|
||||
private Vector2 _pendingPlatformDelta;
|
||||
// 移动平台速度双缓冲:Platform(-300) 写入 _next;Player(-200) 将 _next 换入 _current 并清零;
|
||||
// 状态机(-100) 的 Move() 将 _platformVelocity.x 叠加到水平速度,保持与平台同步。
|
||||
// 使用速度(而非 _rb.position+=):Kinematic 平台的 MovePosition 已通过物理接触推动 Dynamic 乘客;
|
||||
// 若再手动写 position 会产生双重施加(2× 速度)。速度方案仅覆盖 velocity,无此问题。
|
||||
private Vector2 _platformVelocity; // 本帧消费(Move() 叠加到 velocity)
|
||||
private Vector2 _nextPlatformVelocity; // Platform(-300) 写入缓冲区
|
||||
// 玩家自身输入水平速度(不含平台分量);由 Move() 写入,UpdateFacing / ApplyAirDrag 读此值。
|
||||
private float _inputVelocityX;
|
||||
private bool _isWallLeft;
|
||||
private bool _isWallRight;
|
||||
private bool _onOneWayPlatform;
|
||||
@@ -88,15 +91,14 @@ namespace BaseGames.Player
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
// 消费本帧平台位移:直接修改物理位置,不污染 _rb.velocity。
|
||||
// Rigidbody2D.position 在 FixedUpdate 内赋值时,Unity 会同步更新插值参考点,
|
||||
// 不会引起视觉抖动。
|
||||
_rb.position += _pendingPlatformDelta;
|
||||
_pendingPlatformDelta = Vector2.zero;
|
||||
// 平台速度双缓冲换入:Platform(-300) 已写入 _next;换入 _current 供 Move() 叠加。
|
||||
_platformVelocity = _nextPlatformVelocity;
|
||||
_nextPlatformVelocity = Vector2.zero;
|
||||
|
||||
// 优先处理来自 Update 的强制清零请求(在状态机 OnStateFixedUpdate 之前执行)。
|
||||
if (_pendingHorizontalZero)
|
||||
{
|
||||
_inputVelocityX = 0f;
|
||||
_rb.velocity = new Vector2(0f, _rb.velocity.y);
|
||||
_pendingHorizontalZero = false;
|
||||
}
|
||||
@@ -131,11 +133,13 @@ namespace BaseGames.Player
|
||||
/// 直接赋予目标水平速度(按键即全速,松键即停,无加速过渡)。
|
||||
/// 地面状态每帧直接到达全速;空中调用时同样即时,但配合 ApplyAirDrag
|
||||
/// 在无输入时自然减速,保留跳出时的动量。
|
||||
/// 平台携带通过 _rb.position 偏移实现,此处无需叠加平台速度。
|
||||
/// 若当前站在移动平台上,自动叠加 <c>_platformVelocity.x</c> 保持同步;
|
||||
/// <c>_inputVelocityX</c> 只记录玩家输入分量,供 UpdateFacing / ApplyAirDrag 使用。
|
||||
/// </summary>
|
||||
public void Move(float speedX)
|
||||
{
|
||||
_rb.velocity = new Vector2(speedX, _rb.velocity.y);
|
||||
_inputVelocityX = speedX;
|
||||
_rb.velocity = new Vector2(speedX + _platformVelocity.x, _rb.velocity.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -145,8 +149,10 @@ namespace BaseGames.Player
|
||||
/// </summary>
|
||||
public void ApplyAirDrag(float factor)
|
||||
{
|
||||
// 空中不在平台上,_platformVelocity = 0,velocity.x == _inputVelocityX;同步写回保持一致。
|
||||
float newX = _rb.velocity.x * factor;
|
||||
if (Mathf.Abs(newX) < 0.05f) newX = 0f;
|
||||
_inputVelocityX = newX;
|
||||
_rb.velocity = new Vector2(newX, _rb.velocity.y);
|
||||
}
|
||||
|
||||
@@ -181,9 +187,10 @@ namespace BaseGames.Player
|
||||
=> _rb.velocity = direction.normalized * force;
|
||||
|
||||
// ── 速度控制 ──────────────────────────────────────────────────────────
|
||||
public void ZeroVelocity() => _rb.velocity = Vector2.zero;
|
||||
public void ZeroVelocity() { _inputVelocityX = 0f; _rb.velocity = Vector2.zero; }
|
||||
public void ZeroHorizontalVelocity()
|
||||
{
|
||||
_inputVelocityX = 0f;
|
||||
_rb.velocity = new Vector2(0f, _rb.velocity.y);
|
||||
// 设置标记:下一个 FixedUpdate 开头再次强制清零,
|
||||
// 防止因读到旧输入而把速度重新写成非零值。
|
||||
@@ -193,9 +200,8 @@ namespace BaseGames.Player
|
||||
// ── 朝向 ──────────────────────────────────────────────────────────────
|
||||
public void UpdateFacing()
|
||||
{
|
||||
// _rb.velocity.x 是纯玩家输入速度(平台携带通过位置偏移,不混入 velocity),
|
||||
// 直接读取即可,无需减去平台速度。
|
||||
float vx = _rb.velocity.x;
|
||||
// 读取玩家输入速度(不含平台分量),避免平台横向运动驱动朝向翻转。
|
||||
float vx = _inputVelocityX;
|
||||
if (Mathf.Abs(vx) < 0.1f) return;
|
||||
int dir = vx > 0f ? 1 : -1;
|
||||
if (dir == _facingDirection) return;
|
||||
@@ -238,19 +244,18 @@ namespace BaseGames.Player
|
||||
// ── IPassengerReceiver ────────────────────────────────────────────────
|
||||
/// <summary>
|
||||
/// MovingPlatform.FixedUpdate(-300) 推送本帧平台期望位移。
|
||||
/// 累积到 _pendingPlatformDelta;在本类 FixedUpdate(-200) 通过 _rb.position += 消费,
|
||||
/// 与玩家 velocity 完全解耦,UpdateFacing / ApplyAirDrag 无需修正。
|
||||
/// 转换为速度(delta / fixedDeltaTime)写入下帧缓冲区 _next,
|
||||
/// 在本类 FixedUpdate(-200) 换入,由 Move() 叠加到水平速度。
|
||||
/// </summary>
|
||||
public void SetPlatformDelta(Vector2 delta)
|
||||
=> _pendingPlatformDelta += delta;
|
||||
=> _nextPlatformVelocity += delta / Time.fixedDeltaTime;
|
||||
|
||||
/// <summary>
|
||||
/// 乘客离开平台时调用,传入平台当前帧速度。
|
||||
/// 此方案下 _rb.velocity 为纯玩家速度,离台时直接继承平台完整速度(含水平),
|
||||
/// 保证从移动平台起跳后具有平台动量,手感自然。
|
||||
/// 水平速度已由最后一次 Move() 自然携带(包含 _platformVelocity.x),仅继承垂直分量。
|
||||
/// </summary>
|
||||
public void OnLeavePlatform(Vector2 platformVelocity)
|
||||
=> _rb.velocity += platformVelocity;
|
||||
=> _rb.velocity += new Vector2(0f, platformVelocity.y);
|
||||
|
||||
// ── 冲刺 ──────────────────────────────────────────────────────────────
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user