feat: sync TTS voice with chat bubbles and battle log for zero-gap playback
Adds await-then-play pattern: audio is pre-generated and cached before visuals appear, so log text + speech bubble + mouth animation + voice all fire in the same frame. Prefetches both answers during question playback for instant transitions. Adds hideSpeechBubble() to dismiss bubbles when voice ends instead of fixed 5s timer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
e7d1f9b97b
commit
cb8a1d50fe
@@ -734,6 +734,19 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
}, duration * 1000)
|
||||
}
|
||||
|
||||
function hideSpeechBubble(side: 'a' | 'b') {
|
||||
const els = side === 'a' ? activeBubbleA : activeBubbleB
|
||||
if (!els.length) return
|
||||
els.forEach(el => {
|
||||
if (!el.exists()) return
|
||||
k.tween(el.opacity, 0, 0.25, (v) => { el.opacity = v }).then(() => {
|
||||
if (el.exists()) el.destroy()
|
||||
})
|
||||
})
|
||||
if (side === 'a') activeBubbleA = []
|
||||
else activeBubbleB = []
|
||||
}
|
||||
|
||||
function wrapBubbleText(text: string, maxChars: number): string[] {
|
||||
const words = text.split(' ')
|
||||
const lines: string[] = []
|
||||
@@ -1195,6 +1208,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
showSpeechBubble(side: 'a' | 'b', text: string, duration?: number) {
|
||||
showSpeechBubble(side, text, duration)
|
||||
},
|
||||
hideSpeechBubble(side: 'a' | 'b') { hideSpeechBubble(side) },
|
||||
|
||||
startTalking(side: 'a' | 'b') { startTalking(side) },
|
||||
stopTalking(side: 'a' | 'b') { stopTalking(side) },
|
||||
|
||||
Reference in New Issue
Block a user