Add background music and fix pause menu: - Add GameMusic class for background music playback - Add music toggle button to UI - Fix resume button to properly resume game instead of restarting - Add volume control icons - Add proper music lifecycle management

This commit is contained in:
cmclark00 2025-03-26 16:52:22 -04:00
parent fabb2742da
commit a56f08afb9
7 changed files with 172 additions and 7 deletions

View file

@ -17,6 +17,7 @@ import com.mintris.game.GameView
import com.mintris.game.NextPieceView import com.mintris.game.NextPieceView
import android.view.HapticFeedbackConstants import android.view.HapticFeedbackConstants
import com.mintris.model.GameBoard import com.mintris.model.GameBoard
import com.mintris.audio.GameMusic
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
@ -25,9 +26,11 @@ class MainActivity : AppCompatActivity() {
private lateinit var gameView: GameView private lateinit var gameView: GameView
private lateinit var gameHaptics: GameHaptics private lateinit var gameHaptics: GameHaptics
private lateinit var gameBoard: GameBoard private lateinit var gameBoard: GameBoard
private lateinit var gameMusic: GameMusic
// Game state // Game state
private var isSoundEnabled = true private var isSoundEnabled = true
private var isMusicEnabled = true
private var selectedLevel = 1 private var selectedLevel = 1
private val maxLevel = 20 private val maxLevel = 20
@ -40,6 +43,7 @@ class MainActivity : AppCompatActivity() {
gameBoard = GameBoard() gameBoard = GameBoard()
gameHaptics = GameHaptics(this) gameHaptics = GameHaptics(this)
gameView = binding.gameView gameView = binding.gameView
gameMusic = GameMusic(this)
// Set up game view // Set up game view
gameView.setGameBoard(gameBoard) gameView.setGameBoard(gameBoard)
@ -51,6 +55,13 @@ class MainActivity : AppCompatActivity() {
binding.nextPieceView.invalidate() binding.nextPieceView.invalidate()
} }
// Set up music toggle
binding.musicToggle.setOnClickListener {
isMusicEnabled = !isMusicEnabled
gameMusic.setEnabled(isMusicEnabled)
updateMusicToggleUI()
}
// Start game immediately // Start game immediately
startGame() startGame()
@ -94,7 +105,7 @@ class MainActivity : AppCompatActivity() {
binding.resumeButton.setOnClickListener { binding.resumeButton.setOnClickListener {
gameHaptics.performHapticFeedback(it, HapticFeedbackConstants.VIRTUAL_KEY) gameHaptics.performHapticFeedback(it, HapticFeedbackConstants.VIRTUAL_KEY)
hidePauseMenu() hidePauseMenu()
gameView.start() resumeGame()
} }
binding.settingsButton.setOnClickListener { binding.settingsButton.setOnClickListener {
@ -217,11 +228,19 @@ class MainActivity : AppCompatActivity() {
vibrator.vibrate(VibrationEffect.createPredefined(effectId)) vibrator.vibrate(VibrationEffect.createPredefined(effectId))
} }
private fun updateMusicToggleUI() {
binding.musicToggle.setImageResource(
if (isMusicEnabled) R.drawable.ic_volume_up
else R.drawable.ic_volume_off
)
}
private fun startGame() { private fun startGame() {
gameView.visibility = View.VISIBLE
gameBoard.startGame()
gameView.start() gameView.start()
hidePauseMenu() gameMusic.setEnabled(isMusicEnabled) // Explicitly set enabled state
if (isMusicEnabled) {
gameMusic.start()
}
} }
private fun restartGame() { private fun restartGame() {
@ -231,10 +250,22 @@ class MainActivity : AppCompatActivity() {
showPauseMenu() showPauseMenu()
} }
private fun pauseGame() {
gameView.pause()
gameMusic.pause()
}
private fun resumeGame() {
gameView.resume()
if (isMusicEnabled) {
gameMusic.start()
}
}
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
if (!gameView.isGameOver()) { if (!gameView.isGameOver()) {
gameView.pause() pauseGame()
showPauseMenu() showPauseMenu()
binding.pauseStartButton.visibility = View.GONE binding.pauseStartButton.visibility = View.GONE
binding.resumeButton.visibility = View.VISIBLE binding.resumeButton.visibility = View.VISIBLE
@ -256,12 +287,19 @@ class MainActivity : AppCompatActivity() {
binding.resumeButton.visibility = View.VISIBLE binding.resumeButton.visibility = View.VISIBLE
} else { } else {
hidePauseMenu() hidePauseMenu()
gameView.start() resumeGame()
} }
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
gameView.resume() if (!gameView.isGameOver()) {
resumeGame()
}
}
override fun onDestroy() {
super.onDestroy()
gameMusic.release()
} }
} }

View file

@ -0,0 +1,93 @@
package com.mintris.audio
import android.content.Context
import android.media.MediaPlayer
import android.media.AudioAttributes
import android.os.Build
import android.util.Log
import com.mintris.R
class GameMusic(private val context: Context) {
private var mediaPlayer: MediaPlayer? = null
private var isEnabled = false
init {
setupMediaPlayer()
}
private fun setupMediaPlayer() {
try {
Log.d("GameMusic", "Setting up MediaPlayer")
mediaPlayer = MediaPlayer.create(context, R.raw.game_music).apply {
isLooping = true
setVolume(0.5f, 0.5f)
// Set audio attributes for better performance
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setAudioAttributes(
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build()
)
}
}
Log.d("GameMusic", "MediaPlayer setup complete")
} catch (e: Exception) {
Log.e("GameMusic", "Error setting up MediaPlayer", e)
}
}
fun start() {
try {
Log.d("GameMusic", "Starting music playback, isEnabled: $isEnabled")
if (isEnabled && mediaPlayer?.isPlaying != true) {
mediaPlayer?.start()
Log.d("GameMusic", "Music playback started")
}
} catch (e: Exception) {
Log.e("GameMusic", "Error starting music", e)
}
}
fun pause() {
try {
Log.d("GameMusic", "Pausing music playback")
mediaPlayer?.pause()
} catch (e: Exception) {
Log.e("GameMusic", "Error pausing music", e)
}
}
fun stop() {
try {
Log.d("GameMusic", "Stopping music playback")
mediaPlayer?.stop()
mediaPlayer?.prepare()
} catch (e: Exception) {
Log.e("GameMusic", "Error stopping music", e)
}
}
fun setEnabled(enabled: Boolean) {
Log.d("GameMusic", "Setting music enabled: $enabled")
isEnabled = enabled
if (enabled) {
start()
} else {
pause()
}
}
fun isEnabled(): Boolean = isEnabled
fun release() {
try {
Log.d("GameMusic", "Releasing MediaPlayer")
mediaPlayer?.release()
mediaPlayer = null
} catch (e: Exception) {
Log.e("GameMusic", "Error releasing MediaPlayer", e)
}
}
}

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zM19,12c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9L3,9v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18L12,4z"/>
</vector>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"/>
</vector>

View file

@ -225,6 +225,19 @@
android:text="@string/sound_on" android:text="@string/sound_on"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="18sp" /> android:textSize="18sp" />
<ImageButton
android:id="@+id/musicToggle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="16dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/toggle_music"
android:padding="12dp"
android:src="@drawable/ic_volume_up"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/pauseStartButton"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

Binary file not shown.

View file

@ -15,4 +15,5 @@
<string name="select_level">Select Level</string> <string name="select_level">Select Level</string>
<string name="sound_on">Sound: On</string> <string name="sound_on">Sound: On</string>
<string name="sound_off">Sound: Off</string> <string name="sound_off">Sound: Off</string>
<string name="toggle_music">Toggle music</string>
</resources> </resources>