RewindEvent
暂停恢复时,宿主不直接从暂停点 T 续走,而是先把判定 / 渲染时间按缓动曲线倒流回 T-3s(历时约 3 秒,等于 3-2-1 倒计时),随后再正向「重演」回 T。这段区间称为倒带重演窗口 [fromMs, toMs],其中 fromMs = T-3s、toMs = T。
窗口建立时投递一次 Phase.BEGIN,窗口结束(正向重演越过 T)时投递一次 Phase.END。一次暂停-恢复周期内两者各一次;若在窗口内再次暂停并恢复,宿主先用 END 闭合旧窗口,再以新窗口投递 BEGIN(严格成对交替)。
方法速览
| 方法 | 用途 |
|---|---|
phase() | 阶段 BEGIN / END |
isBegin() / isEnd() | 阶段判断 |
fromMs() | 窗口左端(T-3s) |
toMs() | 窗口右端(T) |
durationMs() | 窗口时长(toMs - fromMs) |
phase()
public Phase phase();行为:返回窗口阶段。BEGIN 表示时间即将倒流,END 表示已正向重演越过 T。
isBegin()
public boolean isBegin();行为:phase() == Phase.BEGIN。
isEnd()
public boolean isEnd();行为:phase() == Phase.END。
fromMs()
public long fromMs();行为:返回窗口左端(音乐时间毫秒,= 暂停点 T 减去回看时长),也是时间将被倒回的下界。
toMs()
public long toMs();行为:返回窗口右端(音乐时间毫秒,= 暂停点 T)。
durationMs()
public long durationMs();行为:返回 toMs - fromMs(毫秒)。宿主保证 fromMs <= toMs。
示例
ctx.eventBus().subscribe(RewindEvent.class, event -> {
if (event.isBegin()) {
// 宿主时间将倒流回 fromMs:重建自身动画 / 计时器等累计状态
rebuildCumulativeState(event.fromMs());
} else {
// 已正向重演越过 toMs:恢复正常累加
resumeAccumulationAt(event.toMs());
}
});完整签名
public class RewindEvent extends Event {
public enum Phase {
BEGIN,
END
}
public RewindEvent(Phase phase, long fromMs, long toMs);
public Phase phase();
public boolean isBegin();
public boolean isEnd();
public long fromMs();
public long toMs();
public long durationMs();
}行为核验:BEGIN 期间宿主时间 τ 会倒流且不再产生新的计分判定(重演的旧音符是「幽灵」,不触发 NoteJudgedEvent、不 MISS)。模组的累积型状态(state += dt 的动画、计时器、缓动)会与宿主时间脱节:应在 BEGIN 处快照 / 重建,或在窗口内改为按 τ 求值。窗口内 GameplayTickEvent.rewinding() 与 GameplayContext.isRewinding() 同为 true,可作为切换开关。
倒带跟随参考实现:测试模组 官方示例模组 的 RewindFollowHud(正向按 deltaSeconds 积分、窗口内按 musicTimeMs 求值并回滚),由 RW-follow 用例以真 GameplayModHost 全时序驱动验证(正向 → BEGIN → 倒流 → 重演 → END → 续推,零漂移)。需要倒带兼容的演出模组可照抄该实现。
稳定性: 新增,2.x 内只增不破。