第二批反馈:引擎改造(限幅/SoundTouch不变调变速/长按快进/跳句/iOS闪光降级) + 6游戏状态BGM与试玩测试全绿

This commit is contained in:
SoundGame Dev
2026-08-30 16:45:42 +08:00
parent cc0f8c008d
commit 9a915d62c0
1163 changed files with 6346 additions and 208 deletions
+38 -10
View File
@@ -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; }