@@ -7,15 +7,17 @@ class PacManScene extends Phaser.Scene {
77 const arrowDistance = this . currentScale * 80 ; // Distance from Pacman center
88 const keyScale = this . currentScale * 0.065 ; // Scale for key icons
99
10- // Create arrows and keys for each direction
10+ // Create arrows and keys for each direction (M sits to the right of D, same spacing as A–D)
1111 const directions = [
1212 { icon : 'w-key-logo' , angle : 0 , x : 0 , y : - arrowDistance / 2 , direction : 'up' } ,
1313 { icon : 's-key-logo' , angle : 0 , x : 0 , y : arrowDistance / 2 , direction : 'down' } ,
1414 { icon : 'a-key-logo' , angle : 0 , x : - arrowDistance / 2 , y : 0 , direction : 'left' } ,
15- { icon : 'd-key-logo' , angle : 0 , x : arrowDistance / 2 , y : 0 , direction : 'right' }
15+ { icon : 'd-key-logo' , angle : 0 , x : arrowDistance / 2 , y : 0 , direction : 'right' } ,
16+ { icon : 'm-key-logo' , angle : 0 , x : arrowDistance / 2 + arrowDistance , y : 0 , direction : 'mute' , showCaption : true }
1617 ] ;
1718
1819 this . instructionArrows = [ ] ;
20+ this . instructionMuteCaption = null ;
1921
2022 directions . forEach ( dir => {
2123 // Calculate bounding box size based on key icon dimensions
@@ -46,7 +48,19 @@ class PacManScene extends Phaser.Scene {
4648
4749 // Create container for this direction's elements
4850 const directionContainer = this . add . container ( dir . x , dir . y ) ;
49- directionContainer . add ( [ boundingBox , keyIcon ] ) ; // Box first (behind), then icon
51+ const containerChildren = [ boundingBox , keyIcon ] ;
52+
53+ if ( dir . showCaption ) {
54+ const caption = this . add . text ( boxWidth / 2 + 50 , 0 , "mute / unmute" , {
55+ fontSize : `${ Math . floor ( 10 * Math . max ( 0.55 , this . currentScale ) ) } px` ,
56+ fill : '#ffffff' ,
57+ fontFamily : 'Monaco'
58+ } ) . setOrigin ( 0.5 ) ;
59+ this . instructionMuteCaption = caption ;
60+ containerChildren . push ( caption ) ;
61+ }
62+
63+ directionContainer . add ( containerChildren ) ;
5064
5165 // Store references
5266 this . instructionArrows . push ( {
@@ -2238,6 +2252,53 @@ class PacManScene extends Phaser.Scene {
22382252 if ( this . pacman && this . pacmanInitialized ) {
22392253 this . setupCamera ( ) ;
22402254 }
2255+
2256+ }
2257+
2258+ setupBackgroundMusicAndMute ( ) {
2259+ if ( ! this . cache . audio . exists ( 'bgm' ) ) return ;
2260+
2261+ this . bgmSound = this . sound . add ( 'bgm' , { loop : true , volume : 0.35 } ) ;
2262+
2263+ const tryPlayBgm = ( ) => {
2264+ if ( ! this . bgmSound ) return ;
2265+ if ( ! this . bgmSound . isPlaying ) {
2266+ this . bgmSound . play ( ) ;
2267+ }
2268+ } ;
2269+
2270+ tryPlayBgm ( ) ;
2271+ this . input . once ( 'pointerdown' , tryPlayBgm ) ;
2272+ this . bgmSound . once ( 'play' , this . onBgmStartedPlaying , this ) ;
2273+
2274+ this . muteKey = this . input . keyboard . addKey ( Phaser . Input . Keyboard . KeyCodes . M ) ;
2275+ this . muteKey . on ( 'down' , this . toggleMusicMute , this ) ;
2276+ }
2277+
2278+ onBgmStartedPlaying ( ) {
2279+ this . bgmCaptionDynamicReady = true ;
2280+ this . updateMusicHintVisual ( ) ;
2281+ }
2282+
2283+ toggleMusicMute ( ) {
2284+ this . sound . mute = ! this . sound . mute ;
2285+ this . updateMusicHintVisual ( ) ;
2286+ }
2287+
2288+ updateMusicHintVisual ( ) {
2289+ if ( ! this . instructionMuteCaption ) return ;
2290+ if ( ! this . bgmCaptionDynamicReady ) {
2291+ this . instructionMuteCaption . setText ( 'mute / unmute' ) ;
2292+ this . instructionMuteCaption . setColor ( '#ffffff' ) ;
2293+ return ;
2294+ }
2295+ if ( this . sound . mute ) {
2296+ this . instructionMuteCaption . setText ( 'unmute' ) ;
2297+ this . instructionMuteCaption . setColor ( '#ff8888' ) ;
2298+ } else {
2299+ this . instructionMuteCaption . setText ( 'mute' ) ;
2300+ this . instructionMuteCaption . setColor ( '#ffffff' ) ;
2301+ }
22412302 }
22422303
22432304 // Phaser looks for this by-default on initialization
@@ -2293,8 +2354,10 @@ class PacManScene extends Phaser.Scene {
22932354 this . load . image ( 's-key-logo' , 'assets/keyboard-s-key-logo.png' )
22942355 this . load . image ( 'a-key-logo' , 'assets/keyboard-a-key-logo.png' )
22952356 this . load . image ( 'd-key-logo' , 'assets/keyboard-d-key-logo.png' )
2357+ this . load . image ( 'm-key-logo' , 'assets/keyboard-m-key-logo.png' )
22962358 this . load . image ( 'esc-key-logo' , 'assets/keyboard-esc-key-logo.png' )
2297- this . load . image ( 'enter-key-logo' , 'assets/keyboard-enter-key-logo.png' )
2359+ this . load . image ( 'enter-key-logo' , 'assets/keyboard-enter-key-logo.png' ) ;
2360+ this . load . audio ( 'bgm' , [ 'assets/pvz-graze-the-roof-128k.mp3' ] ) ;
22982361 }
22992362
23002363 create ( ) {
@@ -2306,6 +2369,7 @@ class PacManScene extends Phaser.Scene {
23062369 this . animationsStarted = false ; // Flag to prevent multiple animation starts
23072370 this . fontLoaded = false ;
23082371 this . overlayOpen = false ; // Track overlay state
2372+ this . bgmCaptionDynamicReady = false ; // Mute caption stays "mute / unmute" until BGM actually plays
23092373 // this.canInteract = true; // Track if orb can be interacted with (to deal with race condition)
23102374
23112375 // Create section data
@@ -2351,6 +2415,8 @@ class PacManScene extends Phaser.Scene {
23512415
23522416 this . resize ( ) ; // Initial resize, also stores other parameters
23532417 this . scale . on ( 'resize' , this . resize , this ) ; // Listen for resize events
2418+
2419+ this . setupBackgroundMusicAndMute ( ) ;
23542420 }
23552421
23562422 update ( ) {
@@ -2386,6 +2452,17 @@ class PacManScene extends Phaser.Scene {
23862452 this . instructionArrows . forEach ( instruction => {
23872453 this . tweens . killTweensOf ( instruction . arrow ) ;
23882454 } ) ;
2455+ }
2456+
2457+ if ( this . muteKey ) {
2458+ this . muteKey . off ( 'down' , this . toggleMusicMute , this ) ;
2459+ this . muteKey = null ;
2460+ }
2461+
2462+ if ( this . bgmSound ) {
2463+ this . bgmSound . stop ( ) ;
2464+ this . bgmSound . destroy ( ) ;
2465+ this . bgmSound = null ;
23892466 }
23902467
23912468 // Clean up all section content
0 commit comments