Compare commits

..

3 Commits

Author SHA1 Message Date
JiWoong Sul
7e736df46c chore(assets): 앱 아이콘 추가 2026-01-07 20:59:12 +09:00
JiWoong Sul
fbc3016ab1 fix(game): 게임 플레이 화면 개선 2026-01-07 20:59:08 +09:00
JiWoong Sul
464e5e9c22 refactor(audio): BGM 재생 로직 단순화
- 뮤텍스 패턴 제거 (불필요한 복잡성)
- 단순화된 playBgm 메서드
2026-01-07 20:59:03 +09:00
3 changed files with 15 additions and 63 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

View File

@@ -1,4 +1,4 @@
import 'dart:async' show Completer, unawaited;
import 'dart:async' show Completer;
import 'package:flutter/foundation.dart' show debugPrint, kIsWeb;
import 'package:just_audio/just_audio.dart';
@@ -6,43 +6,6 @@ import 'package:just_audio/just_audio.dart';
import 'package:asciineverdie/src/core/audio/sfx_channel_pool.dart';
import 'package:asciineverdie/src/core/storage/settings_repository.dart';
/// BGM 작업 직렬화를 위한 간단한 뮤텍스
class _BgmMutex {
Completer<void>? _completer;
String? _pendingBgm;
/// 현재 작업 중인지 확인
bool get isLocked => _completer != null && !_completer!.isCompleted;
/// 락 획득 시도 (이미 잠겨있으면 대기 BGM 설정 후 false 반환)
Future<bool> tryAcquire(String bgmName) async {
if (isLocked) {
// 이미 작업 중이면 대기 BGM 설정 (마지막 것만 유지)
_pendingBgm = bgmName;
return false;
}
_completer = Completer<void>();
return true;
}
/// 락 해제 및 대기 중인 BGM 반환
String? release() {
_completer?.complete();
_completer = null;
final pending = _pendingBgm;
_pendingBgm = null;
return pending;
}
/// 강제 해제 (에러 시)
void forceRelease() {
if (_completer != null && !_completer!.isCompleted) {
_completer!.complete();
}
_completer = null;
}
}
/// 게임 오디오 서비스 (싱글톤, 핫 리로드 안전)
///
/// BGM과 SFX를 관리하며, 설정과 연동하여 볼륨을 조절합니다.
@@ -122,9 +85,6 @@ class AudioService {
// 오디오 일시정지 상태 (앱 백그라운드 시)
bool _isPaused = false;
// BGM 작업 직렬화 뮤텍스 (동시 호출 방지)
final _bgmMutex = _BgmMutex();
// ─────────────────────────────────────────────────────────────────────────
// 초기화
// ─────────────────────────────────────────────────────────────────────────
@@ -242,32 +202,17 @@ class AudioService {
// BGM 재생
// ─────────────────────────────────────────────────────────────────────────
/// BGM 재생 (뮤텍스로 동시 호출 방지)
/// BGM 재생 (단순화된 버전)
///
/// 여러 곳에서 동시에 호출되어도 안전하게 처리합니다.
/// 진행 중인 작업이 있으면 대기열에 추가하고, 완료 후 마지막 요청만 실행합니다.
/// 여러 곳에서 동시에 호출되어도 마지막 요청만 처리합니다.
Future<void> playBgm(String name) async {
if (_isPaused) return;
if (!_staticInitialized) await init();
if (_currentBgm == name) return;
if (_staticBgmPlayer == null) return;
// 뮤텍스 획득 시도 (실패하면 대기열에 추가)
if (!await _bgmMutex.tryAcquire(name)) {
debugPrint('[AudioService] BGM $name queued (mutex locked)');
return;
}
try {
await _playBgmInternal(name);
} finally {
// 락 해제 및 대기 중인 BGM 확인
final pendingBgm = _bgmMutex.release();
if (pendingBgm != null && pendingBgm != _currentBgm) {
// 대기 중인 BGM이 있으면 재귀 호출 (새 뮤텍스 획득)
unawaited(playBgm(pendingBgm));
}
}
debugPrint('[AudioService] playBgm requested: $name');
await _playBgmInternal(name);
}
/// 내부 BGM 재생 (뮤텍스 내에서 호출)

View File

@@ -256,6 +256,9 @@ class _GamePlayScreenState extends State<GamePlayScreen>
final taskType = state.progress.currentTask.type;
final isInBattleTask = taskType == TaskType.kill;
debugPrint(
'[BGM] TaskType: $taskType, isInBattle: $isInBattleTask, currentBgm: ${audio.currentBgm}');
// 전투 태스크 상태 결정
if (isInBattleTask) {
// 전투 태스크: 보스 여부에 따라 BGM 선택
@@ -266,11 +269,15 @@ class _GamePlayScreenState extends State<GamePlayScreen>
// 전환 시점이거나 현재 BGM이 일치하지 않으면 재생
if (!_wasInBattleTask || audio.currentBgm != expectedBgm) {
debugPrint('[BGM] Playing battle BGM: $expectedBgm');
audio.playBgm(expectedBgm);
}
} else if (_wasInBattleTask || audio.currentBgm == 'battle' || audio.currentBgm == 'boss') {
// 전투 태스크 종료 또는 BGM 불일치: 마을 BGM으로 복귀
audio.playBgm('town');
} else {
// 전투 태스크: 항상 마을 BGM 유지 (이미 town이면 스킵)
if (audio.currentBgm != 'town') {
debugPrint('[BGM] Playing town BGM (was: ${audio.currentBgm})');
audio.playBgm('town');
}
}
_wasInBattleTask = isInBattleTask;