52 lines
2.0 KiB
Markdown
52 lines
2.0 KiB
Markdown
## Context
|
||
|
||
当前 `MovementComponent` 的 `Reconcile()` 方法在收到服务器 PlayerState 后:
|
||
1. 强制 snap 到 server position
|
||
2. 重放 pending inputs(可能产生位移)
|
||
3. 用 bounded correction 从重放后位置收敛回 server position
|
||
|
||
问题:server position 每帧都在变化(服务器在广播),bounded correction 的收敛目标一直在变,导致永远追不上。
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
- 将模拟层(服务器权威 truth)和表现层(纯视觉插值)解耦
|
||
- 表现层用 Lerp 替代 bounded correction,消除追赶震荡
|
||
- 模拟层只输出"目标位置",表现层只管插值
|
||
|
||
**Non-Goals:**
|
||
- 不改变网络协议或服务器逻辑
|
||
- 不修改远程玩家的插值逻辑
|
||
|
||
## Decisions
|
||
|
||
### Decision: 表现层直接设置 rigid.position,不使用 MovePosition
|
||
|
||
**选择:表现层直接设置 `_rigid.position`**
|
||
|
||
- Rigidbody interpolation 设为 `None`
|
||
- 表现层直接写入 `_rigid.position` 和 `_rigid.rotation`
|
||
- 不经过 `Rigidbody.MovePosition`(避免物理引擎介入)
|
||
|
||
### Decision: 模拟层收到服务器状态时立即计算 target
|
||
|
||
收到服务器 PlayerState 时:
|
||
1. Acknowledge inputs(移除 tick <= AckTick 的输入)
|
||
2. 从 authoritative position 重放剩余 pending inputs
|
||
3. 计算 `target = authoritativePosition + replayDisplacement`
|
||
4. 判断 error:error > SnapThreshold → snap;else → 更新 `_presentationTarget`
|
||
|
||
**关键**:`_presentationTarget` 在两次收到服务器状态之间保持不变,表现层稳定 Lerp。
|
||
|
||
### Decision: 插值策略使用 Lerp
|
||
|
||
固定 alpha = 0.15~0.2。后续可根据 RTT 动态调整或改用 SmoothDamp。
|
||
|
||
## Risks / Trade-offs
|
||
|
||
[Risk] 插值延迟导致本地玩家看到的位置比服务器延迟
|
||
→ [Mitigation] Lerp 的延迟是固定的(不像 bounded correction 那样持续追赶),且不影响服务器权威性
|
||
|
||
[Risk] 删除 bounded correction 后无法平滑大误差
|
||
→ [Mitigation] Snap 阈值(0.5 unit)处理大误差,直接跳到目标;Lerp 处理小误差自然收敛
|