Enhanced game over experience: Added louder game over sound, improved game over animation, and added haptic feedback

This commit is contained in:
cmclark00 2025-03-30 19:02:27 -04:00
parent ce19427cca
commit 292ea656f8
6 changed files with 134 additions and 2 deletions

View file

@ -366,6 +366,12 @@ class MainActivity : AppCompatActivity() {
// Flag to track if high score screen will be shown
var showingHighScore = false
// Play game over sound and trigger animation
if (isSoundEnabled) {
gameMusic.playGameOver()
}
gameView.startGameOverAnimation()
// Show progression screen first with XP animation
showProgressionScreen(xpGained, newRewards)

View file

@ -9,12 +9,14 @@ import com.mintris.R
class GameMusic(private val context: Context) {
private var mediaPlayer: MediaPlayer? = null
private var gameOverPlayer: MediaPlayer? = null
private var isEnabled = true
private var isPrepared = false
init {
try {
setupMediaPlayer()
setupGameOverPlayer()
} catch (e: Exception) {
Log.e("GameMusic", "Error initializing: ${e.message}")
}
@ -46,6 +48,49 @@ class GameMusic(private val context: Context) {
}
}
private fun setupGameOverPlayer() {
try {
Log.d("GameMusic", "Setting up GameOver MediaPlayer")
gameOverPlayer = MediaPlayer.create(context, R.raw.game_over).apply {
setVolume(1.0f, 1.0f) // Increased from 0.7f to 1.0f for maximum volume
// 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_SONIFICATION)
.build()
)
}
}
Log.d("GameMusic", "GameOver MediaPlayer setup complete")
} catch (e: Exception) {
Log.e("GameMusic", "Error setting up GameOver MediaPlayer", e)
gameOverPlayer = null
}
}
fun playGameOver() {
if (isEnabled && gameOverPlayer != null) {
try {
Log.d("GameMusic", "Playing game over sound")
// Temporarily lower background music volume
mediaPlayer?.setVolume(0.2f, 0.2f)
// Play game over sound
gameOverPlayer?.start()
// Restore background music volume after a delay
gameOverPlayer?.setOnCompletionListener {
mediaPlayer?.setVolume(0.5f, 0.5f)
}
} catch (e: Exception) {
Log.e("GameMusic", "Error playing game over sound: ${e.message}")
}
}
}
fun start() {
if (isEnabled && mediaPlayer != null && isPrepared) {
try {
@ -107,7 +152,9 @@ class GameMusic(private val context: Context) {
try {
Log.d("GameMusic", "Releasing MediaPlayer")
mediaPlayer?.release()
gameOverPlayer?.release()
mediaPlayer = null
gameOverPlayer = null
isPrepared = false
} catch (e: Exception) {
Log.e("GameMusic", "Error releasing music: ${e.message}")

View file

@ -91,4 +91,26 @@ class GameHaptics(private val context: Context) {
vibrator.vibrate(vibrationEffect)
}
}
fun vibrateForGameOver() {
Log.d(TAG, "Attempting to vibrate for game over")
// Only proceed if the device has a vibrator and it's available
if (!vibrator.hasVibrator()) return
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create a strong, long vibration effect
val vibrationEffect = VibrationEffect.createOneShot(300L, VibrationEffect.DEFAULT_AMPLITUDE)
vibrator.vibrate(vibrationEffect)
Log.d(TAG, "Game over vibration triggered successfully")
} else {
@Suppress("DEPRECATION")
vibrator.vibrate(300L)
Log.w(TAG, "Device does not support vibration effects (Android < 8.0)")
}
} catch (e: Exception) {
Log.e(TAG, "Error triggering game over vibration", e)
}
}
}

View file

@ -178,6 +178,9 @@ class GameView @JvmOverloads constructor(
private var pulseAlpha = 0f
private var isPulsing = false
private var linesToPulse = mutableListOf<Int>() // Track which lines are being cleared
private var gameOverAnimator: ValueAnimator? = null
private var gameOverAlpha = 0f
private var isGameOverAnimating = false
private val ghostPaint = Paint().apply {
style = Paint.Style.STROKE
@ -439,6 +442,18 @@ class GameView @JvmOverloads constructor(
// Draw active piece
drawActivePiece(canvas)
}
// Draw game over effect if animating
if (isGameOverAnimating) {
val gameOverPaint = Paint().apply {
color = Color.WHITE
alpha = (128 * gameOverAlpha).toInt()
isAntiAlias = true
style = Paint.Style.FILL
maskFilter = BlurMaskFilter(48f * gameOverAlpha, BlurMaskFilter.Blur.OUTER)
}
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), gameOverPaint)
}
}
/**
@ -1133,4 +1148,41 @@ class GameView @JvmOverloads constructor(
super.setBackgroundColor(color)
invalidate()
}
/**
* Start the game over animation
*/
fun startGameOverAnimation() {
Log.d(TAG, "Starting game over animation")
// Cancel any existing animations
pulseAnimator?.cancel()
gameOverAnimator?.cancel()
// Trigger haptic feedback
gameHaptics?.vibrateForGameOver()
// Create new game over animation
gameOverAnimator = ValueAnimator.ofFloat(0f, 1f, 0.7f).apply {
duration = 1500L // 1.5 seconds total
interpolator = LinearInterpolator()
addUpdateListener { animation ->
gameOverAlpha = animation.animatedValue as Float
isGameOverAnimating = true
invalidate()
Log.d(TAG, "Game over animation update: alpha = $gameOverAlpha")
}
addListener(object : android.animation.AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: android.animation.Animator) {
isGameOverAnimating = false
gameOverAlpha = 0.7f // Keep at 70% opacity
invalidate()
Log.d(TAG, "Game over animation ended")
}
})
}
gameOverAnimator?.start()
}
}

View file

@ -136,10 +136,10 @@ class GameBoard(
currentPiece = nextPiece
spawnNextPiece()
// Center the piece horizontally
// Center the piece horizontally and spawn one unit higher
currentPiece?.apply {
x = (width - getWidth()) / 2
y = 0
y = -1 // Spawn one unit above the top of the screen
Log.d(TAG, "spawnPiece() - new piece spawned at position (${x},${y}), type=${type}")
@ -326,6 +326,11 @@ class GameBoard(
if (boardY >= 0 && grid[boardY][boardX]) {
return false
}
// Check if the position is more than one unit above the top of the screen
if (boardY < -1) {
return false
}
}
}
}

Binary file not shown.