mirror of
https://github.com/cmclark00/mintris.git
synced 2025-05-18 21:15:20 +01:00
Fix menu shifting issue and code cleanup. Fix menu shifting by adding fillViewport and padding to ScrollView. Added missing getLastClearedLines method. Improved code quality with proper logging constants and removed unused imports.
This commit is contained in:
parent
8661fd8a80
commit
9ab9b53407
5 changed files with 119 additions and 73 deletions
|
@ -1,26 +1,18 @@
|
|||
package com.mintris
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.VibrationEffect
|
||||
import android.os.Vibrator
|
||||
import android.os.VibratorManager
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.view.HapticFeedbackConstants
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.mintris.databinding.ActivityMainBinding
|
||||
import com.mintris.game.GameHaptics
|
||||
import com.mintris.game.GameView
|
||||
import com.mintris.game.NextPieceView
|
||||
import com.mintris.game.TitleScreen
|
||||
import android.view.HapticFeedbackConstants
|
||||
import com.mintris.model.GameBoard
|
||||
import com.mintris.audio.GameMusic
|
||||
import com.mintris.model.HighScoreManager
|
||||
|
@ -33,10 +25,15 @@ import android.graphics.Color
|
|||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import android.graphics.Rect
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MainActivity"
|
||||
}
|
||||
|
||||
// UI components
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var gameView: GameView
|
||||
|
@ -165,18 +162,18 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
gameView.onLineClear = { lineCount ->
|
||||
android.util.Log.d("MainActivity", "Received line clear callback: $lineCount lines")
|
||||
Log.d(TAG, "Received line clear callback: $lineCount lines")
|
||||
// Use enhanced haptic feedback for line clears
|
||||
if (isSoundEnabled) {
|
||||
android.util.Log.d("MainActivity", "Sound is enabled, triggering haptic feedback")
|
||||
Log.d(TAG, "Sound is enabled, triggering haptic feedback")
|
||||
try {
|
||||
gameHaptics.vibrateForLineClear(lineCount)
|
||||
android.util.Log.d("MainActivity", "Haptic feedback triggered successfully")
|
||||
Log.d(TAG, "Haptic feedback triggered successfully")
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("MainActivity", "Error triggering haptic feedback", e)
|
||||
Log.e(TAG, "Error triggering haptic feedback", e)
|
||||
}
|
||||
} else {
|
||||
android.util.Log.d("MainActivity", "Sound is disabled, skipping haptic feedback")
|
||||
Log.d(TAG, "Sound is disabled, skipping haptic feedback")
|
||||
}
|
||||
// Record line clear in stats
|
||||
statsManager.recordLineClear(lineCount)
|
||||
|
|
|
@ -7,15 +7,66 @@ import android.os.Vibrator
|
|||
import android.os.VibratorManager
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.View
|
||||
import android.util.Log
|
||||
|
||||
/**
|
||||
* Handles haptic feedback for game events
|
||||
*/
|
||||
class GameHaptics(private val context: Context) {
|
||||
private val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "GameHaptics"
|
||||
}
|
||||
|
||||
// Vibrator service
|
||||
private val vibrator: Vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
|
||||
vibratorManager.defaultVibrator
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
}
|
||||
|
||||
// Vibrate for line clear (more intense for more lines)
|
||||
fun vibrateForLineClear(lineCount: Int) {
|
||||
Log.d(TAG, "Attempting to vibrate for $lineCount lines")
|
||||
|
||||
// Only proceed if the device has a vibrator and it's available
|
||||
if (!vibrator.hasVibrator()) return
|
||||
|
||||
// Scale duration and amplitude based on line count
|
||||
// More lines = longer and stronger vibration
|
||||
val duration = when(lineCount) {
|
||||
1 -> 50L // Single line: short vibration
|
||||
2 -> 80L // Double line: slightly longer
|
||||
3 -> 120L // Triple line: even longer
|
||||
4 -> 200L // Tetris: longest vibration
|
||||
else -> 50L
|
||||
}
|
||||
|
||||
val amplitude = when(lineCount) {
|
||||
1 -> 80 // Single line: mild vibration (80/255)
|
||||
2 -> 120 // Double line: medium vibration (120/255)
|
||||
3 -> 180 // Triple line: strong vibration (180/255)
|
||||
4 -> 255 // Tetris: maximum vibration (255/255)
|
||||
else -> 80
|
||||
}
|
||||
|
||||
Log.d(TAG, "Vibration parameters - Duration: ${duration}ms, Amplitude: $amplitude")
|
||||
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
vibrator.vibrate(VibrationEffect.createOneShot(duration, amplitude))
|
||||
Log.d(TAG, "Vibration triggered successfully")
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
vibrator.vibrate(duration)
|
||||
Log.w(TAG, "Device does not support vibration effects (Android < 8.0)")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error triggering vibration", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun performHapticFeedback(view: View, feedbackType: Int) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
|
@ -26,40 +77,6 @@ class GameHaptics(private val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun vibrateForLineClear(lineCount: Int) {
|
||||
android.util.Log.d("GameHaptics", "Attempting to vibrate for $lineCount lines")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val duration = when (lineCount) {
|
||||
4 -> 200L // Tetris - doubled from 100L
|
||||
3 -> 160L // Triples - doubled from 80L
|
||||
2 -> 120L // Doubles - doubled from 60L
|
||||
1 -> 80L // Singles - doubled from 40L
|
||||
else -> 0L
|
||||
}
|
||||
|
||||
val amplitude = when (lineCount) {
|
||||
4 -> 255 // Full amplitude for Tetris
|
||||
3 -> 230 // 90% amplitude for triples
|
||||
2 -> 180 // 70% amplitude for doubles
|
||||
1 -> 128 // 50% amplitude for singles
|
||||
else -> 0
|
||||
}
|
||||
|
||||
android.util.Log.d("GameHaptics", "Vibration parameters - Duration: ${duration}ms, Amplitude: $amplitude")
|
||||
if (duration > 0 && amplitude > 0) {
|
||||
try {
|
||||
val vibrationEffect = VibrationEffect.createOneShot(duration, amplitude)
|
||||
vibrator.vibrate(vibrationEffect)
|
||||
android.util.Log.d("GameHaptics", "Vibration triggered successfully")
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("GameHaptics", "Error triggering vibration", e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
android.util.Log.w("GameHaptics", "Device does not support vibration effects (Android < 8.0)")
|
||||
}
|
||||
}
|
||||
|
||||
fun vibrateForPieceLock() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val vibrationEffect = VibrationEffect.createOneShot(50L, VibrationEffect.DEFAULT_AMPLITUDE)
|
||||
|
|
|
@ -12,12 +12,12 @@ import android.os.Build
|
|||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.view.WindowManager
|
||||
import android.view.Display
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.view.Display
|
||||
import com.mintris.model.GameBoard
|
||||
import com.mintris.model.Tetromino
|
||||
import com.mintris.model.TetrominoType
|
||||
|
@ -33,6 +33,10 @@ class GameView @JvmOverloads constructor(
|
|||
defStyleAttr: Int = 0
|
||||
) : View(context, attrs, defStyleAttr) {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "GameView"
|
||||
}
|
||||
|
||||
// Game board model
|
||||
private var gameBoard = GameBoard()
|
||||
private var gameHaptics: GameHaptics? = null
|
||||
|
@ -165,17 +169,17 @@ class GameView @JvmOverloads constructor(
|
|||
gameBoard.onPieceMove = { onPieceMove?.invoke() }
|
||||
gameBoard.onPieceLock = { onPieceLock?.invoke() }
|
||||
gameBoard.onLineClear = { lineCount, clearedLines ->
|
||||
android.util.Log.d("GameView", "Received line clear from GameBoard: $lineCount lines")
|
||||
Log.d(TAG, "Received line clear from GameBoard: $lineCount lines")
|
||||
try {
|
||||
onLineClear?.invoke(lineCount)
|
||||
// Use the lines that were cleared directly
|
||||
linesToPulse.clear()
|
||||
linesToPulse.addAll(clearedLines)
|
||||
android.util.Log.d("GameView", "Found ${linesToPulse.size} lines to pulse")
|
||||
Log.d(TAG, "Found ${linesToPulse.size} lines to pulse")
|
||||
startPulseAnimation(lineCount)
|
||||
android.util.Log.d("GameView", "Forwarded line clear callback")
|
||||
Log.d(TAG, "Forwarded line clear callback")
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("GameView", "Error forwarding line clear callback", e)
|
||||
Log.e(TAG, "Error forwarding line clear callback", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,7 +188,11 @@ class GameView @JvmOverloads constructor(
|
|||
|
||||
// Set better frame rate using modern APIs
|
||||
val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||
val display = displayManager.getDisplay(Display.DEFAULT_DISPLAY)
|
||||
val display = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
displayManager.getDisplay(Display.DEFAULT_DISPLAY)
|
||||
} else {
|
||||
displayManager.displays.firstOrNull()
|
||||
}
|
||||
display?.let { disp ->
|
||||
val refreshRate = disp.refreshRate
|
||||
// Set game loop interval based on refresh rate, but don't go faster than the base interval
|
||||
|
@ -289,7 +297,7 @@ class GameView @JvmOverloads constructor(
|
|||
val totalHeight = blockSize * verticalBlocks
|
||||
|
||||
// Log dimensions for debugging
|
||||
android.util.Log.d("GameView", "Board dimensions: width=$width, height=$height, blockSize=$blockSize, boardLeft=$boardLeft, boardTop=$boardTop, totalHeight=$totalHeight")
|
||||
Log.d(TAG, "Board dimensions: width=$width, height=$height, blockSize=$blockSize, boardLeft=$boardLeft, boardTop=$boardTop, totalHeight=$totalHeight")
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
|
@ -739,17 +747,17 @@ class GameView @JvmOverloads constructor(
|
|||
gameBoard.onPieceMove = { onPieceMove?.invoke() }
|
||||
gameBoard.onPieceLock = { onPieceLock?.invoke() }
|
||||
gameBoard.onLineClear = { lineCount, clearedLines ->
|
||||
android.util.Log.d("GameView", "Received line clear from GameBoard: $lineCount lines")
|
||||
Log.d(TAG, "Received line clear from GameBoard: $lineCount lines")
|
||||
try {
|
||||
onLineClear?.invoke(lineCount)
|
||||
// Use the lines that were cleared directly
|
||||
linesToPulse.clear()
|
||||
linesToPulse.addAll(clearedLines)
|
||||
android.util.Log.d("GameView", "Found ${linesToPulse.size} lines to pulse")
|
||||
Log.d(TAG, "Found ${linesToPulse.size} lines to pulse")
|
||||
startPulseAnimation(lineCount)
|
||||
android.util.Log.d("GameView", "Forwarded line clear callback")
|
||||
Log.d(TAG, "Forwarded line clear callback")
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("GameView", "Error forwarding line clear callback", e)
|
||||
Log.e(TAG, "Error forwarding line clear callback", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -785,7 +793,7 @@ class GameView @JvmOverloads constructor(
|
|||
* Start the pulse animation for line clear
|
||||
*/
|
||||
private fun startPulseAnimation(lineCount: Int) {
|
||||
android.util.Log.d("GameView", "Starting pulse animation for $lineCount lines")
|
||||
Log.d(TAG, "Starting pulse animation for $lineCount lines")
|
||||
|
||||
// Cancel any existing animation
|
||||
pulseAnimator?.cancel()
|
||||
|
@ -804,7 +812,7 @@ class GameView @JvmOverloads constructor(
|
|||
pulseAlpha = animation.animatedValue as Float
|
||||
isPulsing = true
|
||||
invalidate()
|
||||
android.util.Log.d("GameView", "Pulse animation update: alpha = $pulseAlpha")
|
||||
Log.d(TAG, "Pulse animation update: alpha = $pulseAlpha")
|
||||
}
|
||||
addListener(object : android.animation.AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: android.animation.Animator) {
|
||||
|
@ -812,7 +820,7 @@ class GameView @JvmOverloads constructor(
|
|||
pulseAlpha = 0f
|
||||
linesToPulse.clear()
|
||||
invalidate()
|
||||
android.util.Log.d("GameView", "Pulse animation ended")
|
||||
Log.d(TAG, "Pulse animation ended")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.mintris.model
|
||||
|
||||
import kotlin.random.Random
|
||||
import android.util.Log
|
||||
|
||||
/**
|
||||
* Represents the game board (grid) and manages game state
|
||||
|
@ -9,6 +9,10 @@ class GameBoard(
|
|||
val width: Int = 10,
|
||||
val height: Int = 20
|
||||
) {
|
||||
companion object {
|
||||
private const val TAG = "GameBoard"
|
||||
}
|
||||
|
||||
// Board grid to track locked pieces
|
||||
// True = occupied, False = empty
|
||||
private val grid = Array(height) { BooleanArray(width) { false } }
|
||||
|
@ -55,6 +59,9 @@ class GameBoard(
|
|||
var onNextPieceChanged: (() -> Unit)? = null
|
||||
var onLineClear: ((Int, List<Int>) -> Unit)? = null
|
||||
|
||||
// Store the last cleared lines
|
||||
private val lastClearedLines = mutableListOf<Int>()
|
||||
|
||||
init {
|
||||
spawnNextPiece()
|
||||
spawnPiece()
|
||||
|
@ -66,7 +73,7 @@ class GameBoard(
|
|||
private fun spawnNextPiece() {
|
||||
// If bag is empty, refill it with all piece types
|
||||
if (bag.isEmpty()) {
|
||||
bag.addAll(TetrominoType.values())
|
||||
bag.addAll(TetrominoType.entries.toTypedArray())
|
||||
bag.shuffle()
|
||||
}
|
||||
|
||||
|
@ -326,18 +333,26 @@ class GameBoard(
|
|||
y--
|
||||
}
|
||||
|
||||
// Store the last cleared lines
|
||||
lastClearedLines.clear()
|
||||
lastClearedLines.addAll(linesToClear)
|
||||
|
||||
// If lines were cleared, calculate score in background and trigger callback
|
||||
if (shiftAmount > 0) {
|
||||
android.util.Log.d("GameBoard", "Lines cleared: $shiftAmount")
|
||||
// Log line clear
|
||||
Log.d(TAG, "Lines cleared: $shiftAmount")
|
||||
|
||||
// Trigger line clear callback on main thread with the lines that were cleared
|
||||
val mainHandler = android.os.Handler(android.os.Looper.getMainLooper())
|
||||
mainHandler.post {
|
||||
android.util.Log.d("GameBoard", "Triggering onLineClear callback with $shiftAmount lines")
|
||||
// Call the line clear callback with the cleared line count
|
||||
try {
|
||||
onLineClear?.invoke(shiftAmount, linesToClear) // Pass the lines that were cleared
|
||||
android.util.Log.d("GameBoard", "onLineClear callback completed successfully")
|
||||
Log.d(TAG, "Triggering onLineClear callback with $shiftAmount lines")
|
||||
val clearedLines = getLastClearedLines()
|
||||
onLineClear?.invoke(shiftAmount, clearedLines)
|
||||
Log.d(TAG, "onLineClear callback completed successfully")
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("GameBoard", "Error in onLineClear callback", e)
|
||||
Log.e(TAG, "Error in onLineClear callback", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -582,4 +597,11 @@ class GameBoard(
|
|||
* Get the current combo count
|
||||
*/
|
||||
fun getCombo(): Int = combo
|
||||
|
||||
/**
|
||||
* Get the list of lines that were most recently cleared
|
||||
*/
|
||||
private fun getLastClearedLines(): List<Int> {
|
||||
return lastClearedLines.toList()
|
||||
}
|
||||
}
|
|
@ -282,13 +282,15 @@
|
|||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
android:layout_weight="1"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
android:gravity="center"
|
||||
android:paddingTop="16dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/pauseStartButton"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue