feat(mobile): 앱 라이프사이클에 따른 게임/오디오 상태 관리
- _playInitialBgm() 추가: 게임 로드 시 전투 상태에 맞는 BGM 재생 - 백그라운드 진입 시 게임 일시정지 및 BGM 정지 (모바일) - 포그라운드 복귀 시 세이브에서 재로드 및 화면 재생성 - didChangeAppLifecycleState 개선으로 모바일 UX 향상
This commit is contained in:
@@ -221,6 +221,31 @@ class _GamePlayScreenState extends State<GamePlayScreen>
|
|||||||
_lastProcessedEventCount = events.length;
|
_lastProcessedEventCount = events.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 초기 BGM 재생 (게임 시작/로드 시)
|
||||||
|
void _playInitialBgm(GameState state) {
|
||||||
|
final audio = widget.audioService;
|
||||||
|
if (audio == null) return;
|
||||||
|
|
||||||
|
final combat = state.progress.currentCombat;
|
||||||
|
final isInCombat = combat != null && combat.isActive;
|
||||||
|
|
||||||
|
if (isInCombat) {
|
||||||
|
// 전투 중: 보스 여부에 따라 BGM 선택
|
||||||
|
final monsterLevel = state.progress.currentTask.monsterLevel ?? 0;
|
||||||
|
final playerLevel = state.traits.level;
|
||||||
|
final isBoss = monsterLevel >= playerLevel + 5;
|
||||||
|
|
||||||
|
if (isBoss) {
|
||||||
|
audio.playBgm('boss');
|
||||||
|
} else {
|
||||||
|
audio.playBgm('battle');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 비전투: 마을 BGM
|
||||||
|
audio.playBgm('town');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 전투 상태에 따른 BGM 전환
|
/// 전투 상태에 따른 BGM 전환
|
||||||
void _updateBgmForCombatState(GameState state) {
|
void _updateBgmForCombatState(GameState state) {
|
||||||
final audio = widget.audioService;
|
final audio = widget.audioService;
|
||||||
@@ -450,17 +475,20 @@ class _GamePlayScreenState extends State<GamePlayScreen>
|
|||||||
_lastPlotStageCount = state.progress.plotStageCount;
|
_lastPlotStageCount = state.progress.plotStageCount;
|
||||||
_lastAct = getActForLevel(state.traits.level);
|
_lastAct = getActForLevel(state.traits.level);
|
||||||
|
|
||||||
// 초기 전투 상태 확인
|
// 초기 전투 상태 확인 및 BGM 설정
|
||||||
final combat = state.progress.currentCombat;
|
final combat = state.progress.currentCombat;
|
||||||
_wasInCombat = combat != null && combat.isActive;
|
_wasInCombat = combat != null && combat.isActive;
|
||||||
|
|
||||||
|
// 초기 BGM 재생 (전투 상태에 따라)
|
||||||
|
_playInitialBgm(state);
|
||||||
|
} else {
|
||||||
|
// 상태가 없으면 기본 마을 BGM
|
||||||
|
widget.audioService?.playBgm('town');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 누적 통계 로드
|
// 누적 통계 로드
|
||||||
widget.controller.loadCumulativeStats();
|
widget.controller.loadCumulativeStats();
|
||||||
|
|
||||||
// 초기 BGM 재생 (마을 테마)
|
|
||||||
widget.audioService?.playBgm('town');
|
|
||||||
|
|
||||||
// 오디오 볼륨 초기화
|
// 오디오 볼륨 초기화
|
||||||
_initAudioVolumes();
|
_initAudioVolumes();
|
||||||
}
|
}
|
||||||
@@ -485,17 +513,54 @@ class _GamePlayScreenState extends State<GamePlayScreen>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
void didChangeAppLifecycleState(AppLifecycleState appState) {
|
||||||
super.didChangeAppLifecycleState(state);
|
super.didChangeAppLifecycleState(appState);
|
||||||
|
|
||||||
// 앱이 백그라운드로 가거나 비활성화될 때 자동 저장
|
// 모바일 환경 확인 (iOS/Android)
|
||||||
if (state == AppLifecycleState.paused ||
|
final isMobile = !kIsWeb &&
|
||||||
state == AppLifecycleState.inactive ||
|
(defaultTargetPlatform == TargetPlatform.iOS ||
|
||||||
state == AppLifecycleState.detached) {
|
defaultTargetPlatform == TargetPlatform.android);
|
||||||
|
|
||||||
|
// 앱이 백그라운드로 가거나 비활성화될 때
|
||||||
|
if (appState == AppLifecycleState.paused ||
|
||||||
|
appState == AppLifecycleState.inactive ||
|
||||||
|
appState == AppLifecycleState.detached) {
|
||||||
|
// 저장
|
||||||
_saveGameState();
|
_saveGameState();
|
||||||
|
|
||||||
|
// 모바일: 게임 일시정지 + 사운드 정지
|
||||||
|
if (isMobile) {
|
||||||
|
widget.controller.pause(saveOnStop: false);
|
||||||
|
widget.audioService?.stopBgm();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 모바일: 앱이 포그라운드로 돌아올 때 전체 재로드
|
||||||
|
if (appState == AppLifecycleState.resumed && isMobile) {
|
||||||
|
_reloadGameScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 모바일 재진입 시 전체 화면 재로드
|
||||||
|
Future<void> _reloadGameScreen() async {
|
||||||
|
// 세이브 파일에서 다시 로드
|
||||||
|
await widget.controller.loadAndStart(cheatsEnabled: widget.controller.cheatsEnabled);
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
// 화면 재생성 (상태 초기화)
|
||||||
|
Navigator.of(context).pushReplacement(
|
||||||
|
MaterialPageRoute<void>(
|
||||||
|
builder: (_) => GamePlayScreen(
|
||||||
|
controller: widget.controller,
|
||||||
|
audioService: widget.audioService,
|
||||||
|
currentThemeMode: widget.currentThemeMode,
|
||||||
|
onThemeModeChange: widget.onThemeModeChange,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _saveGameState() async {
|
Future<void> _saveGameState() async {
|
||||||
final currentState = widget.controller.state;
|
final currentState = widget.controller.state;
|
||||||
if (currentState == null || !widget.controller.isRunning) return;
|
if (currentState == null || !widget.controller.isRunning) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user