feat: 收敛玩法运行时配置并加入故障恢复

This commit is contained in:
2026-04-01 13:04:26 +08:00
parent 1635a11780
commit 3ef841ecc7
73 changed files with 8820 additions and 2122 deletions

View File

@@ -3,11 +3,13 @@ import { type AnimationLevel } from '../../utils/animationLevel'
export type FeedbackCueKey =
| 'session_started'
| 'session_finished'
| 'hint:changed'
| 'control_completed:start'
| 'control_completed:control'
| 'control_completed:finish'
| 'punch_feedback:warning'
| 'guidance:searching'
| 'guidance:distant'
| 'guidance:approaching'
| 'guidance:ready'
@@ -83,12 +85,14 @@ export const DEFAULT_GAME_HAPTICS_CONFIG: GameHapticsConfig = {
cues: {
session_started: { enabled: false, pattern: 'short' },
session_finished: { enabled: true, pattern: 'long' },
'hint:changed': { enabled: true, pattern: 'short' },
'control_completed:start': { enabled: true, pattern: 'short' },
'control_completed:control': { enabled: true, pattern: 'short' },
'control_completed:finish': { enabled: true, pattern: 'long' },
'punch_feedback:warning': { enabled: true, pattern: 'short' },
'guidance:searching': { enabled: false, pattern: 'short' },
'guidance:approaching': { enabled: false, pattern: 'short' },
'guidance:distant': { enabled: true, pattern: 'short' },
'guidance:approaching': { enabled: true, pattern: 'short' },
'guidance:ready': { enabled: true, pattern: 'short' },
},
}
@@ -98,11 +102,13 @@ export const DEFAULT_GAME_UI_EFFECTS_CONFIG: GameUiEffectsConfig = {
cues: {
session_started: { enabled: false, punchFeedbackMotion: 'none', contentCardMotion: 'none', punchButtonMotion: 'none', mapPulseMotion: 'none', stageMotion: 'none', hudProgressMotion: 'none', hudDistanceMotion: 'none', durationMs: 0 },
session_finished: { enabled: false, punchFeedbackMotion: 'none', contentCardMotion: 'none', punchButtonMotion: 'none', mapPulseMotion: 'none', stageMotion: 'none', hudProgressMotion: 'none', hudDistanceMotion: 'none', durationMs: 0 },
'hint:changed': { enabled: false, punchFeedbackMotion: 'none', contentCardMotion: 'none', punchButtonMotion: 'none', mapPulseMotion: 'none', stageMotion: 'none', hudProgressMotion: 'none', hudDistanceMotion: 'none', durationMs: 0 },
'control_completed:start': { enabled: true, punchFeedbackMotion: 'success', contentCardMotion: 'pop', punchButtonMotion: 'none', mapPulseMotion: 'control', stageMotion: 'control', hudProgressMotion: 'success', hudDistanceMotion: 'success', durationMs: 560 },
'control_completed:control': { enabled: true, punchFeedbackMotion: 'success', contentCardMotion: 'pop', punchButtonMotion: 'none', mapPulseMotion: 'control', stageMotion: 'control', hudProgressMotion: 'success', hudDistanceMotion: 'success', durationMs: 560 },
'control_completed:finish': { enabled: true, punchFeedbackMotion: 'success', contentCardMotion: 'finish', punchButtonMotion: 'none', mapPulseMotion: 'finish', stageMotion: 'finish', hudProgressMotion: 'finish', hudDistanceMotion: 'success', durationMs: 680 },
'punch_feedback:warning': { enabled: true, punchFeedbackMotion: 'warning', contentCardMotion: 'none', punchButtonMotion: 'warning', mapPulseMotion: 'none', stageMotion: 'none', hudProgressMotion: 'none', hudDistanceMotion: 'none', durationMs: 560 },
'guidance:searching': { enabled: false, punchFeedbackMotion: 'none', contentCardMotion: 'none', punchButtonMotion: 'none', mapPulseMotion: 'none', stageMotion: 'none', hudProgressMotion: 'none', hudDistanceMotion: 'none', durationMs: 0 },
'guidance:distant': { enabled: false, punchFeedbackMotion: 'none', contentCardMotion: 'none', punchButtonMotion: 'none', mapPulseMotion: 'none', stageMotion: 'none', hudProgressMotion: 'none', hudDistanceMotion: 'none', durationMs: 0 },
'guidance:approaching': { enabled: false, punchFeedbackMotion: 'none', contentCardMotion: 'none', punchButtonMotion: 'none', mapPulseMotion: 'none', stageMotion: 'none', hudProgressMotion: 'none', hudDistanceMotion: 'none', durationMs: 0 },
'guidance:ready': { enabled: true, punchFeedbackMotion: 'none', contentCardMotion: 'none', punchButtonMotion: 'ready', mapPulseMotion: 'ready', stageMotion: 'none', hudProgressMotion: 'none', hudDistanceMotion: 'none', durationMs: 900 },
},
@@ -137,11 +143,13 @@ export function mergeGameHapticsConfig(overrides?: GameHapticsConfigOverrides |
const cues: GameHapticsConfig['cues'] = {
session_started: mergeHapticCue(DEFAULT_GAME_HAPTICS_CONFIG.cues.session_started, overrides && overrides.cues ? overrides.cues.session_started : undefined),
session_finished: mergeHapticCue(DEFAULT_GAME_HAPTICS_CONFIG.cues.session_finished, overrides && overrides.cues ? overrides.cues.session_finished : undefined),
'hint:changed': mergeHapticCue(DEFAULT_GAME_HAPTICS_CONFIG.cues['hint:changed'], overrides && overrides.cues ? overrides.cues['hint:changed'] : undefined),
'control_completed:start': mergeHapticCue(DEFAULT_GAME_HAPTICS_CONFIG.cues['control_completed:start'], overrides && overrides.cues ? overrides.cues['control_completed:start'] : undefined),
'control_completed:control': mergeHapticCue(DEFAULT_GAME_HAPTICS_CONFIG.cues['control_completed:control'], overrides && overrides.cues ? overrides.cues['control_completed:control'] : undefined),
'control_completed:finish': mergeHapticCue(DEFAULT_GAME_HAPTICS_CONFIG.cues['control_completed:finish'], overrides && overrides.cues ? overrides.cues['control_completed:finish'] : undefined),
'punch_feedback:warning': mergeHapticCue(DEFAULT_GAME_HAPTICS_CONFIG.cues['punch_feedback:warning'], overrides && overrides.cues ? overrides.cues['punch_feedback:warning'] : undefined),
'guidance:searching': mergeHapticCue(DEFAULT_GAME_HAPTICS_CONFIG.cues['guidance:searching'], overrides && overrides.cues ? overrides.cues['guidance:searching'] : undefined),
'guidance:distant': mergeHapticCue(DEFAULT_GAME_HAPTICS_CONFIG.cues['guidance:distant'], overrides && overrides.cues ? overrides.cues['guidance:distant'] : undefined),
'guidance:approaching': mergeHapticCue(DEFAULT_GAME_HAPTICS_CONFIG.cues['guidance:approaching'], overrides && overrides.cues ? overrides.cues['guidance:approaching'] : undefined),
'guidance:ready': mergeHapticCue(DEFAULT_GAME_HAPTICS_CONFIG.cues['guidance:ready'], overrides && overrides.cues ? overrides.cues['guidance:ready'] : undefined),
}
@@ -156,11 +164,13 @@ export function mergeGameUiEffectsConfig(overrides?: GameUiEffectsConfigOverride
const cues: GameUiEffectsConfig['cues'] = {
session_started: mergeUiCue(DEFAULT_GAME_UI_EFFECTS_CONFIG.cues.session_started, overrides && overrides.cues ? overrides.cues.session_started : undefined),
session_finished: mergeUiCue(DEFAULT_GAME_UI_EFFECTS_CONFIG.cues.session_finished, overrides && overrides.cues ? overrides.cues.session_finished : undefined),
'hint:changed': mergeUiCue(DEFAULT_GAME_UI_EFFECTS_CONFIG.cues['hint:changed'], overrides && overrides.cues ? overrides.cues['hint:changed'] : undefined),
'control_completed:start': mergeUiCue(DEFAULT_GAME_UI_EFFECTS_CONFIG.cues['control_completed:start'], overrides && overrides.cues ? overrides.cues['control_completed:start'] : undefined),
'control_completed:control': mergeUiCue(DEFAULT_GAME_UI_EFFECTS_CONFIG.cues['control_completed:control'], overrides && overrides.cues ? overrides.cues['control_completed:control'] : undefined),
'control_completed:finish': mergeUiCue(DEFAULT_GAME_UI_EFFECTS_CONFIG.cues['control_completed:finish'], overrides && overrides.cues ? overrides.cues['control_completed:finish'] : undefined),
'punch_feedback:warning': mergeUiCue(DEFAULT_GAME_UI_EFFECTS_CONFIG.cues['punch_feedback:warning'], overrides && overrides.cues ? overrides.cues['punch_feedback:warning'] : undefined),
'guidance:searching': mergeUiCue(DEFAULT_GAME_UI_EFFECTS_CONFIG.cues['guidance:searching'], overrides && overrides.cues ? overrides.cues['guidance:searching'] : undefined),
'guidance:distant': mergeUiCue(DEFAULT_GAME_UI_EFFECTS_CONFIG.cues['guidance:distant'], overrides && overrides.cues ? overrides.cues['guidance:distant'] : undefined),
'guidance:approaching': mergeUiCue(DEFAULT_GAME_UI_EFFECTS_CONFIG.cues['guidance:approaching'], overrides && overrides.cues ? overrides.cues['guidance:approaching'] : undefined),
'guidance:ready': mergeUiCue(DEFAULT_GAME_UI_EFFECTS_CONFIG.cues['guidance:ready'], overrides && overrides.cues ? overrides.cues['guidance:ready'] : undefined),
}

View File

@@ -1,10 +1,11 @@
import { DEFAULT_GAME_AUDIO_CONFIG, type GameAudioConfig } from '../audio/audioConfig'
import { DEFAULT_GAME_AUDIO_CONFIG, type AudioCueKey, type GameAudioConfig } from '../audio/audioConfig'
import { SoundDirector } from '../audio/soundDirector'
import { type GameEffect } from '../core/gameResult'
import { type AnimationLevel } from '../../utils/animationLevel'
import {
DEFAULT_GAME_HAPTICS_CONFIG,
DEFAULT_GAME_UI_EFFECTS_CONFIG,
type FeedbackCueKey,
type GameHapticsConfig,
type GameUiEffectsConfig,
} from './feedbackConfig'
@@ -61,12 +62,20 @@ export class FeedbackDirector {
this.soundDirector.setAppAudioMode(mode)
}
playAudioCue(key: AudioCueKey): void {
this.soundDirector.play(key)
}
playHapticCue(key: FeedbackCueKey): void {
this.hapticsDirector.trigger(key)
}
handleEffects(effects: GameEffect[]): void {
this.soundDirector.handleEffects(effects)
this.hapticsDirector.handleEffects(effects)
this.uiEffectDirector.handleEffects(effects)
if (effects.some((effect) => effect.type === 'session_finished')) {
if (effects.some((effect) => effect.type === 'session_finished' || effect.type === 'session_timed_out')) {
this.host.stopLocationTracking()
}
}

View File

@@ -66,6 +66,10 @@ export class HapticsDirector {
this.trigger('guidance:searching')
continue
}
if (effect.guidanceState === 'distant') {
this.trigger('guidance:distant')
continue
}
if (effect.guidanceState === 'approaching') {
this.trigger('guidance:approaching')
continue

View File

@@ -258,17 +258,19 @@ export class UiEffectDirector {
'success',
cue ? this.getPunchFeedbackMotionClass(cue.punchFeedbackMotion) : '',
)
this.host.showContentCard(
effect.displayTitle,
effect.displayBody,
cue ? this.getContentCardMotionClass(cue.contentCardMotion) : '',
{
contentKey: effect.controlId,
autoPopup: effect.displayAutoPopup,
once: effect.displayOnce,
priority: effect.displayPriority,
},
)
if (effect.controlKind !== 'finish' && effect.displayAutoPopup) {
this.host.showContentCard(
effect.displayTitle,
effect.displayBody,
cue ? this.getContentCardMotionClass(cue.contentCardMotion) : '',
{
contentKey: effect.controlId,
autoPopup: effect.displayAutoPopup,
once: effect.displayOnce,
priority: effect.displayPriority,
},
)
}
if (cue && cue.mapPulseMotion !== 'none') {
this.host.showMapPulse(effect.controlId, this.getMapPulseMotionClass(cue.mapPulseMotion))
}