mirror of
https://github.com/cmclark00/mintris.git
synced 2025-05-17 19:55:20 +01:00
Enhanced haptic feedback for gamepad users: - Added gamepad connection tracking - Increased vibration intensity by 50% when using gamepad - Adjusted vibration durations and amplitudes for better feedback
This commit is contained in:
parent
dbaebb8b60
commit
c4f103ae1e
3 changed files with 92 additions and 32 deletions
|
@ -269,6 +269,15 @@ class MainActivity : AppCompatActivity(),
|
|||
updateUI(score, level, lines)
|
||||
}
|
||||
|
||||
// Track pieces placed using callback
|
||||
gameBoard.onPieceLock = {
|
||||
// Increment pieces placed counter
|
||||
piecesPlaced++
|
||||
|
||||
// Provide haptic feedback
|
||||
gameHaptics.vibrateForPieceLock()
|
||||
}
|
||||
|
||||
gameView.onGameOver = { finalScore ->
|
||||
// Pause music on game over
|
||||
gameMusic.pause()
|
||||
|
@ -325,15 +334,6 @@ class MainActivity : AppCompatActivity(),
|
|||
}
|
||||
}
|
||||
|
||||
// Track pieces placed using callback
|
||||
gameView.onPieceLock = {
|
||||
// Increment pieces placed counter
|
||||
piecesPlaced++
|
||||
|
||||
// Provide haptic feedback
|
||||
gameHaptics.vibrateForPieceLock()
|
||||
}
|
||||
|
||||
// Set up button click listeners with haptic feedback
|
||||
binding.playAgainButton.setOnClickListener {
|
||||
gameHaptics.performHapticFeedback(it, HapticFeedbackConstants.VIRTUAL_KEY)
|
||||
|
@ -484,6 +484,12 @@ class MainActivity : AppCompatActivity(),
|
|||
Log.d("MainActivity", "Triggering game over animation")
|
||||
gameView.startGameOverAnimation()
|
||||
|
||||
// Hide game UI elements in landscape mode
|
||||
if (resources.configuration.orientation == android.content.res.Configuration.ORIENTATION_LANDSCAPE) {
|
||||
binding.leftControlsPanel?.visibility = View.GONE
|
||||
binding.rightControlsPanel?.visibility = View.GONE
|
||||
}
|
||||
|
||||
// Wait a moment before showing progression screen to let animation be visible
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
// Show progression screen first with XP animation
|
||||
|
@ -660,10 +666,12 @@ class MainActivity : AppCompatActivity(),
|
|||
* Start a new game
|
||||
*/
|
||||
private fun startGame() {
|
||||
// Reset pieces placed counter
|
||||
piecesPlaced = 0
|
||||
|
||||
// Set initial game state
|
||||
currentScore = 0
|
||||
currentLevel = selectedLevel
|
||||
piecesPlaced = 0
|
||||
gameStartTime = System.currentTimeMillis()
|
||||
|
||||
// Update UI to show initial values
|
||||
|
@ -673,8 +681,23 @@ class MainActivity : AppCompatActivity(),
|
|||
binding.comboText.text = "0"
|
||||
|
||||
// Reset game view and game board
|
||||
gameBoard.reset()
|
||||
gameView.reset()
|
||||
|
||||
// Show game elements
|
||||
gameView.visibility = View.VISIBLE
|
||||
binding.gameControlsContainer.visibility = View.VISIBLE
|
||||
binding.gameOverContainer.visibility = View.GONE
|
||||
binding.pauseContainer.visibility = View.GONE
|
||||
titleScreen.visibility = View.GONE
|
||||
progressionScreen.visibility = View.GONE
|
||||
|
||||
// Show game UI elements in landscape mode
|
||||
if (resources.configuration.orientation == android.content.res.Configuration.ORIENTATION_LANDSCAPE) {
|
||||
binding.leftControlsPanel?.visibility = View.VISIBLE
|
||||
binding.rightControlsPanel?.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
// Configure callbacks
|
||||
gameView.onGameStateChanged = { score, level, lines ->
|
||||
currentScore = score
|
||||
|
@ -729,15 +752,19 @@ class MainActivity : AppCompatActivity(),
|
|||
statsManager.recordLineClear(lineCount)
|
||||
}
|
||||
|
||||
// Start the game
|
||||
gameView.start()
|
||||
gameMusic.setEnabled(isMusicEnabled)
|
||||
// Reset pause button state
|
||||
binding.pauseStartButton.visibility = View.VISIBLE
|
||||
binding.resumeButton.visibility = View.GONE
|
||||
|
||||
// Start background music if enabled
|
||||
if (isMusicEnabled) {
|
||||
gameMusic.start()
|
||||
}
|
||||
|
||||
// Start the game
|
||||
gameView.start()
|
||||
gameMusic.setEnabled(isMusicEnabled)
|
||||
|
||||
// Reset session stats
|
||||
statsManager.startNewSession()
|
||||
progressionManager.startNewSession()
|
||||
|
@ -957,6 +984,8 @@ class MainActivity : AppCompatActivity(),
|
|||
override fun onGamepadConnected(gamepadName: String) {
|
||||
runOnUiThread {
|
||||
Toast.makeText(this, "Gamepad connected: $gamepadName", Toast.LENGTH_SHORT).show()
|
||||
// Set gamepad connected state in haptics
|
||||
gameHaptics.setGamepadConnected(true)
|
||||
// Provide haptic feedback for gamepad connection
|
||||
gameHaptics.vibrateForPieceLock()
|
||||
|
||||
|
@ -970,6 +999,8 @@ class MainActivity : AppCompatActivity(),
|
|||
override fun onGamepadDisconnected(gamepadName: String) {
|
||||
runOnUiThread {
|
||||
Toast.makeText(this, "Gamepad disconnected: $gamepadName", Toast.LENGTH_SHORT).show()
|
||||
// Set gamepad disconnected state in haptics
|
||||
gameHaptics.setGamepadConnected(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,19 @@ class GameHaptics(private val context: Context) {
|
|||
context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
}
|
||||
|
||||
// Track if gamepad is connected
|
||||
private var isGamepadConnected = false
|
||||
|
||||
// Set gamepad connection state
|
||||
fun setGamepadConnected(connected: Boolean) {
|
||||
isGamepadConnected = connected
|
||||
}
|
||||
|
||||
// Get vibration multiplier based on input method
|
||||
private fun getVibrationMultiplier(): Float {
|
||||
return if (isGamepadConnected) 1.5f else 1.0f
|
||||
}
|
||||
|
||||
// Vibrate for line clear (more intense for more lines)
|
||||
fun vibrateForLineClear(lineCount: Int) {
|
||||
Log.d(TAG, "Attempting to vibrate for $lineCount lines")
|
||||
|
@ -34,22 +47,22 @@ class GameHaptics(private val context: Context) {
|
|||
// 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
|
||||
// Scale duration and amplitude based on line count and input method
|
||||
val multiplier = getVibrationMultiplier()
|
||||
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
|
||||
1 -> (50L * multiplier).toLong() // Single line: short vibration
|
||||
2 -> (80L * multiplier).toLong() // Double line: slightly longer
|
||||
3 -> (120L * multiplier).toLong() // Triple line: even longer
|
||||
4 -> (200L * multiplier).toLong() // Tetris: longest vibration
|
||||
else -> (50L * multiplier).toLong()
|
||||
}
|
||||
|
||||
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
|
||||
1 -> (80 * multiplier).toInt().coerceAtMost(255) // Single line: mild vibration
|
||||
2 -> (120 * multiplier).toInt().coerceAtMost(255) // Double line: medium vibration
|
||||
3 -> (180 * multiplier).toInt().coerceAtMost(255) // Triple line: strong vibration
|
||||
4 -> 255 // Tetris: maximum vibration
|
||||
else -> (80 * multiplier).toInt().coerceAtMost(255)
|
||||
}
|
||||
|
||||
Log.d(TAG, "Vibration parameters - Duration: ${duration}ms, Amplitude: $amplitude")
|
||||
|
@ -79,15 +92,20 @@ class GameHaptics(private val context: Context) {
|
|||
|
||||
fun vibrateForPieceLock() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val vibrationEffect = VibrationEffect.createOneShot(50L, VibrationEffect.DEFAULT_AMPLITUDE)
|
||||
val multiplier = getVibrationMultiplier()
|
||||
val duration = (50L * multiplier).toLong()
|
||||
val amplitude = (VibrationEffect.DEFAULT_AMPLITUDE * multiplier).toInt().coerceAtMost(255)
|
||||
val vibrationEffect = VibrationEffect.createOneShot(duration, amplitude)
|
||||
vibrator.vibrate(vibrationEffect)
|
||||
}
|
||||
}
|
||||
|
||||
fun vibrateForPieceMove() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val amplitude = (VibrationEffect.DEFAULT_AMPLITUDE * 0.3).toInt().coerceAtLeast(1)
|
||||
val vibrationEffect = VibrationEffect.createOneShot(20L, amplitude)
|
||||
val multiplier = getVibrationMultiplier()
|
||||
val duration = (20L * multiplier).toLong()
|
||||
val amplitude = (VibrationEffect.DEFAULT_AMPLITUDE * 0.3 * multiplier).toInt().coerceAtLeast(1).coerceAtMost(255)
|
||||
val vibrationEffect = VibrationEffect.createOneShot(duration, amplitude)
|
||||
vibrator.vibrate(vibrationEffect)
|
||||
}
|
||||
}
|
||||
|
@ -100,8 +118,10 @@ class GameHaptics(private val context: Context) {
|
|||
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// Create a strong, long vibration effect
|
||||
val vibrationEffect = VibrationEffect.createOneShot(300L, VibrationEffect.DEFAULT_AMPLITUDE)
|
||||
val multiplier = getVibrationMultiplier()
|
||||
val duration = (300L * multiplier).toLong()
|
||||
val amplitude = (VibrationEffect.DEFAULT_AMPLITUDE * multiplier).toInt().coerceAtMost(255)
|
||||
val vibrationEffect = VibrationEffect.createOneShot(duration, amplitude)
|
||||
vibrator.vibrate(vibrationEffect)
|
||||
Log.d(TAG, "Game over vibration triggered successfully")
|
||||
} else {
|
||||
|
|
|
@ -245,6 +245,7 @@ class TitleScreen @JvmOverloads constructor(
|
|||
// Draw high scores using pre-allocated manager
|
||||
val highScores: List<HighScore> = highScoreManager.getHighScores()
|
||||
val highScoreY = height * 0.5f
|
||||
var lastHighScoreY = highScoreY
|
||||
if (highScores.isNotEmpty()) {
|
||||
// Calculate the starting X position to center the entire block of scores
|
||||
val maxScoreWidth = highScorePaint.measureText("99. PLAYER: 999999")
|
||||
|
@ -252,6 +253,7 @@ class TitleScreen @JvmOverloads constructor(
|
|||
|
||||
highScores.forEachIndexed { index: Int, score: HighScore ->
|
||||
val y = highScoreY + (index * 80f)
|
||||
lastHighScoreY = y // Track the last high score's Y position
|
||||
// Pad the rank number to ensure alignment
|
||||
val rank = (index + 1).toString().padStart(2, ' ')
|
||||
// Pad the name to ensure score alignment
|
||||
|
@ -260,8 +262,15 @@ class TitleScreen @JvmOverloads constructor(
|
|||
}
|
||||
}
|
||||
|
||||
// Draw "touch to start" prompt
|
||||
canvas.drawText("touch to start", width / 2f, height * 0.7f, promptPaint)
|
||||
// Draw "touch to start" prompt below the high scores
|
||||
val promptY = if (resources.configuration.orientation == android.content.res.Configuration.ORIENTATION_LANDSCAPE) {
|
||||
// In landscape mode, position below the last high score with some padding
|
||||
lastHighScoreY + 100f
|
||||
} else {
|
||||
// In portrait mode, use the original position
|
||||
height * 0.7f
|
||||
}
|
||||
canvas.drawText("touch to start", width / 2f, promptY, promptPaint)
|
||||
|
||||
// Request another frame
|
||||
invalidate()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue