第二批反馈:引擎改造(限幅/SoundTouch不变调变速/长按快进/跳句/iOS闪光降级) + 6游戏状态BGM与试玩测试全绿
This commit is contained in:
+38
-10
@@ -18,6 +18,11 @@
|
||||
* - **菜单**(round2-review 全球约定):下滑呼出(模态暂停)→ 单击循环选项 →
|
||||
* 双击确认 → 上滑帮助。下滑与"下滑闪避"冲突的折中:**仅当无下方陨石悬置时**
|
||||
* 下滑才呼出菜单(有下方陨石时下滑永远是闪避),见 README。
|
||||
* - **状态 BGM**(10 号文档 §6):菜单切 `bgm/menu`(crossfade 300ms)、回对局淡回
|
||||
* `bgm/engine`(500ms)、坠毁结算语音链播完淡入 `bgm/menu`(800ms)。
|
||||
* - **单击跳过旁白**(12 号文档):演练旁白/feat 引入句/对局归因与里程碑/结算播报
|
||||
* 播放中单击 = `skipVoice()` 跳下一句;无语音在播时单击维持原义(加速/重开)。
|
||||
* 首局 D0 在 drill_welcome 后插播一次 skip_hint。
|
||||
*
|
||||
* 震动分工:手势识别反馈引擎内置(swipe/confirm),本层只发语义事件——
|
||||
* 撞击 hit / 护盾格挡 success / 警报两档 danger / 护盾就绪 pickup / 里程碑 confirm /
|
||||
@@ -118,6 +123,7 @@ export class SpaceGame extends Game {
|
||||
'sfx/alarm', 'sfx/boost', 'sfx/shield', 'sfx/shield_block',
|
||||
'sfx/dodge', 'sfx/hit', 'sfx/crash',
|
||||
'voice/drill_welcome', 'voice/drill_rule', 'voice/second_run_ask',
|
||||
'voice/skip_hint',
|
||||
]);
|
||||
this._session++;
|
||||
this._clearTimers();
|
||||
@@ -127,7 +133,7 @@ export class SpaceGame extends Game {
|
||||
this.phase = 'ask';
|
||||
this.audio.play('voice/second_run_ask');
|
||||
} else {
|
||||
this._startDrill();
|
||||
this._startDrill(true); // 首局:D0 插播一次 skip_hint
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,22 +164,28 @@ export class SpaceGame extends Game {
|
||||
this._onMenuGesture(g);
|
||||
break;
|
||||
case 'over':
|
||||
if (g === 'tap') this._startRun(); // 单击再飞一次
|
||||
else if (g === 'down') this._goHome(); // 下滑回大厅(restart_hint 口径)
|
||||
// 结算语音在播时单击 = 跳过本句(链继续),无语音时 = 再飞一次
|
||||
if (g === 'tap') {
|
||||
if (this._voiceBusy()) this.audio.skipVoice();
|
||||
else this._startRun();
|
||||
} else if (g === 'down') this._goHome(); // 下滑回大厅(restart_hint 口径)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- D0-D5 交互式演练 ----------------
|
||||
|
||||
_startDrill() {
|
||||
_startDrill(withSkipHint = false) {
|
||||
this._session++;
|
||||
this._clearTimers();
|
||||
this.audio.stopVoice();
|
||||
this._resetRunState();
|
||||
this.phase = 'drill';
|
||||
// D0:建立预期(无陨石)
|
||||
this._speakGuarded(['voice/drill_welcome', 'voice/drill_rule'], () => this._nextDrill());
|
||||
// D0:建立预期(无陨石)。首局在 welcome 后插播一次 skip_hint(12 号文档 §3 预留 id)。
|
||||
const d0 = withSkipHint
|
||||
? ['voice/drill_welcome', 'voice/skip_hint', 'voice/drill_rule']
|
||||
: ['voice/drill_welcome', 'voice/drill_rule'];
|
||||
this._speakGuarded(d0, () => this._nextDrill());
|
||||
}
|
||||
|
||||
_nextDrill() {
|
||||
@@ -203,8 +215,10 @@ export class SpaceGame extends Game {
|
||||
|
||||
_onDrillGesture(g) {
|
||||
if (!this.drillOb) {
|
||||
// 无演练陨石悬置(旁白/反馈播放中):下滑 = 跳过演练,其余忽略
|
||||
// 无演练陨石悬置(旁白/反馈播放中):下滑 = 跳过演练;
|
||||
// 旁白在播时单击 = 跳过本句(语音链跳下一句),其余忽略
|
||||
if (g === 'down') this._skipDrill();
|
||||
else if (g === 'tap' && this._voiceBusy()) this.audio.skipVoice();
|
||||
return;
|
||||
}
|
||||
const d = DRILLS[this.drillIdx];
|
||||
@@ -287,6 +301,8 @@ export class SpaceGame extends Game {
|
||||
}
|
||||
break;
|
||||
case 'tap':
|
||||
// 归因/里程碑等播报在播时单击 = 跳过本句(12 号文档裁定:space 对局语音可跳)
|
||||
if (this._voiceBusy()) { this.audio.skipVoice(); break; }
|
||||
// 加速是持续型增益(~1.8s),期间的撞击一律算冲过空隙,
|
||||
// 因此 tap 的 ~300ms 判定延迟不影响手感。第 20s 引入后才解锁。
|
||||
if (this.boostIntroDone && now >= this.boostReadyAt) {
|
||||
@@ -336,6 +352,9 @@ export class SpaceGame extends Game {
|
||||
}
|
||||
|
||||
_onFeatureGesture(g) {
|
||||
// 引入旁白在播时单击 = 跳过本句(12 号文档:feat_* 引入句可跳);
|
||||
// 单击加速教学需旁白播完(或被跳过)后再单击一次触发
|
||||
if (g === 'tap' && this._voiceBusy()) { this.audio.skipVoice(); return; }
|
||||
const want = this.featureKind === 'boost' ? 'tap' : 'doubletap';
|
||||
if (g !== want) return; // 其他手势忽略,等超时自动继续
|
||||
const now = performance.now();
|
||||
@@ -350,8 +369,7 @@ export class SpaceGame extends Game {
|
||||
this.shieldReadyAt = now + SHIELD_COOLDOWN_MS;
|
||||
this.audio.play('sfx/shield');
|
||||
this._speak(['voice/feat_shield_done']);
|
||||
this._endFeature();
|
||||
this._scheduleShieldReady(); // _endFeature 清过 timers,充能播报在其后重排
|
||||
this._endFeature(); // _endFeature → _rearmRunTimers 会按 shieldReadyAt 重排充能播报
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,6 +519,7 @@ export class SpaceGame extends Game {
|
||||
this.obstacles.clear();
|
||||
this.swipes = [];
|
||||
this._menuIdx = 0;
|
||||
this.audio.play('bgm/menu', { fadeMs: 300 }); // 状态 BGM:进菜单快切(10 号文档 §6)
|
||||
this._speak(['voice/paused', 'voice/menu_main', MENU_ITEMS[0]]);
|
||||
}
|
||||
|
||||
@@ -522,6 +541,7 @@ export class SpaceGame extends Game {
|
||||
|
||||
_closeMenu() {
|
||||
this.phase = 'playing';
|
||||
this.audio.play('bgm/engine', { fadeMs: 500 }); // 状态 BGM:回对局淡回 engine
|
||||
this._scheduleSpawn(800);
|
||||
this._rearmRunTimers();
|
||||
}
|
||||
@@ -549,7 +569,10 @@ export class SpaceGame extends Game {
|
||||
const ids = [this._timeBandVoice(aliveSec)];
|
||||
if (isRecord) ids.push('voice/new_record');
|
||||
ids.push(this._dodgeBandVoice(this.dodges), 'voice/restart_hint');
|
||||
this._speak(ids);
|
||||
// 结算语音链播完淡入 menu pad(10 号文档 §6);被跳过的句仍逐句推进链
|
||||
this._speak(ids, () => {
|
||||
if (this.phase === 'over') this.audio.play('bgm/menu', { fadeMs: 800 });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -619,6 +642,11 @@ export class SpaceGame extends Game {
|
||||
|
||||
// ---------------- 工具 ----------------
|
||||
|
||||
/** voice 层是否有对白在播(单击=跳过本句的语境判定依据)。 */
|
||||
_voiceBusy() {
|
||||
return !!this.audio._voice;
|
||||
}
|
||||
|
||||
/** 语音串行链:自然播完才接下一句;被 stopVoice 打断则整条作废。 */
|
||||
_speak(ids, onDone) {
|
||||
if (!ids.length) { if (onDone) onDone(); return; }
|
||||
|
||||
Reference in New Issue
Block a user