Linear ArchiveArchived issues viewer
← Back to list
INS-836

`shortSound API 新設: preload/play/release 統合 + やられボイス不具合解消`

StatusDone
TeamInstansys
Assigneeasuki.uehata@instansys.co.jp
PriorityNo priority
Created2026/06/10 06:09
Completed2026/06/12 04:05
Archived2026/06/19 04:54

Description

背景

  • INS-817playOneShot API を新設・マージ。
  • 並行して develop 側で playOneShotVoiceByUrl / preloadUrlSound(やられボイス用)が別 PR で追加され、命名・所有層が playOneShot と混乱。
  • battle-1-1(academy-1-normal-1)でヤンキー学生1/2 がやられた際に、ボイスが鳴らない不具合を確認(enemy-defeat-voice.ts:29playOneShotVoiceByUrl 経路)。SE(キラーン)は鳴っているため defeatEffect ハンドラ自体は通っており、voice の preload / play 経路に問題がある。

目的

  1. 短音再生の抽象再設計: 連打・頻発・preload 前提の短音群(やられボイス、SE群、キャラ standby voice 等)を扱う shortSound 抽象を新設し、preload と release を柔軟に管理できるようにする。
  2. やられボイス不具合の同時解消: 新 API への置換により、旧 playOneShotVoiceByUrl のロード/再生経路を捨てる。
  3. API 表面の整理: 旧 playOneShotVoiceByUrl / preloadUrlSound を削除し、命名混乱を解消。

スコープ

  • 未完了soundManager.shortSound 名前空間を新設(apps/client/src/lib/sound.ts
  • 未完了以下 API を実装:
    • preload(alias, url): void
    • play(alias, options?): void — 既定 category: "voice"、未ロード時は 50ms × 60 = 3秒 polling
    • release(alias): void
    • releaseMatching(pattern): void — glob (* のみサポート)
    • releaseAllExceptMatching(pattern): void — glob 除外
    • releaseAll(): void
  • 未完了内部所有層 _shortSoundOwnedAliases (Set) を registeredSoundAliases / _scenarioOwnedAliases / _oneShotOwnedAliases と独立に管理(INS-817 の所有層独立原則を踏襲)
  • 未完了enemy-defeat-voice.ts:29: playOneShotVoiceByUrlshortSound.play 置換
  • 未完了Battle.tsx:1995-1999: preloadUrlSoundshortSound.preload 置換 + バトル離脱時 cleanup で shortSound.releaseMatching("https://*supabase*") 等で sweep
  • 未完了旧 playOneShotVoiceByUrl / preloadUrlSound を sound.ts から削除

スコープ外

  • 既存 playOneShot のリネーム(playShortSound 化等)は別チケット
  • 既存 _scenarioOwnedAliases 体系(シナリオ voice)との統合は別途検討
  • 参照カウント方式(シーン跨ぎ alias 重複対応)は今回入れない

受け入れ条件

  1. 動作確認: bun run debug-cmd skip-to test academy-1-normal-1 でバトル開始 → ヤンキー1/2 をやられさせた時に「ちくしょう...」「そうか...」が鳴る
  2. メモリ解放: バトル離脱時に shortSound 所有 alias が sound.remove まで届いている(DevTools の sound.exists(url) で false 確認)
  3. 既存 playOneShot の回帰なし: リザルトボイス(ChapterResult / StageResultView)・編成 standby ボイス(PartyEditModal)が引き続き正常動作
  4. bun lint / 該当ファイル型チェック通過

関連

  • instansys/ultemist#693(マージ済 — playOneShot 本体)
  • 引き継ぎ docs: docs/audio-memory-leak-handoff.md

Comments (3)

asuki.uehata@instansys.co.jp2026/06/10 06:21

統合する必要はなさそう。

でも、私が実装したAPIと名前が混同するのでわけるべき あと、"playOneShotVoiceByUrl" って OneShot じゃなくて、重ね再生の方だから 実際には、 OneShot という機能ではなさそう。

asuki.uehata@instansys.co.jp2026/06/10 09:48

API仕様 予定 草案

正規表現でまとめてメモリ解放できるようにする。

soundManager.shortSound = {
  preload(alias: string, url: string): void,
  play(alias: string, options?: { volume?: number; category?: "voice" | "battle" | ...; pitch?: number; ... }): void,

  release(alias: string): void,
  releaseMatching(pattern: string): void,
  releaseAllExceptMatching(pattern: string): void,
  releaseAll(): void,
}
asuki.uehata@instansys.co.jp2026/06/10 13:39

INS-836 Enemy.tsx で dead transition 時にやられボイス発火

playEnemyDefeatVoice の呼出が AnimationDebug.tsx のプレビュー用しか

存在しておらず、バトル中の Enemy.tsx から発火されていなかった (機能未実装)。

Enemy.tsx に useRef + useEffect で enemy.state の "dead" 立ち上がりを

検知する仕組みを追加し、shortSound 経由でやられボイスを発火する。

manualControl 経由の手動 "dead" 切替 (AnimationDebug) は既に

AnimationDebug 側で playEnemyDefeatVoice を呼んでいるため、

!isManualControl ガードで二重発火を回避。