fix arrow spawning and suppress step labels during calibration
ArrowGame: use ref for onSongEnd to avoid unstable deps causing the audio effect to re-run and lose songStartRef. Reset arrows, combo, and spawn index when the game starts. Log audio play failures instead of swallowing them. GestureOverlay: only show 'arms-out' label during calibration, not step labels which are confusing before the game starts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>main
parent
d2630b1b64
commit
184a24f425
|
|
@ -17,9 +17,11 @@ interface Props {
|
|||
export function GestureOverlay({ gesture, calibrationStatus, framingHint }: Props) {
|
||||
if (calibrationStatus === 'ready') return null
|
||||
|
||||
const showGesture = gesture === 'arms-out'
|
||||
|
||||
return (
|
||||
<>
|
||||
{gesture && (
|
||||
{showGesture && (
|
||||
<div className="gesture-label">
|
||||
{GESTURE_LABELS[gesture]}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ export const ArrowGame = forwardRef<ArrowGameHandle, Props>(({ active = true, ch
|
|||
const audioRef = useRef<HTMLAudioElement | null>(null)
|
||||
const songStartRef = useRef<number | null>(null)
|
||||
const spawnedUpToRef = useRef(0)
|
||||
const onSongEndRef = useRef(onSongEnd)
|
||||
onSongEndRef.current = onSongEnd
|
||||
|
||||
const showJudgment = useCallback((text: string) => {
|
||||
if (judgmentTimer.current) clearTimeout(judgmentTimer.current)
|
||||
|
|
@ -140,16 +142,24 @@ export const ArrowGame = forwardRef<ArrowGameHandle, Props>(({ active = true, ch
|
|||
useEffect(() => {
|
||||
if (!active || !chartNotes || !audioUrl) return
|
||||
|
||||
// Reset state for fresh start
|
||||
arrowsRef.current = []
|
||||
setArrows([])
|
||||
spawnedUpToRef.current = 0
|
||||
comboRef.current = 0
|
||||
setScore({ perfect: 0, good: 0, miss: 0, combo: 0 })
|
||||
|
||||
const audio = new Audio(audioUrl)
|
||||
audioRef.current = audio
|
||||
songStartRef.current = null
|
||||
spawnedUpToRef.current = 0
|
||||
|
||||
audio.play().then(() => {
|
||||
songStartRef.current = performance.now()
|
||||
}).catch(() => {})
|
||||
}).catch((e) => {
|
||||
console.warn('Audio play failed:', e)
|
||||
})
|
||||
|
||||
audio.addEventListener('ended', () => onSongEnd?.())
|
||||
audio.addEventListener('ended', () => onSongEndRef.current?.())
|
||||
|
||||
return () => {
|
||||
audio.pause()
|
||||
|
|
@ -157,7 +167,7 @@ export const ArrowGame = forwardRef<ArrowGameHandle, Props>(({ active = true, ch
|
|||
audioRef.current = null
|
||||
songStartRef.current = null
|
||||
}
|
||||
}, [active, chartNotes, audioUrl, onSongEnd])
|
||||
}, [active, chartNotes, audioUrl]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// Chart mode: spawn arrows based on song time
|
||||
useEffect(() => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue