Kotlin – Collision between ball and walls

I could use help how to solve my issue of ball not fully bouncing of walls of my maze. The ball will go inside the walls, but won’t be able to pass through them completely, bouncing from inside. Below I provide code of my app:

MainActivity.kt

`package com.example.mazeball

import android.app.Activity
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import kotlin.math.max
import kotlin.math.min

class MainActivity : Activity(), SensorEventListener {

    private lateinit var sensorManager: SensorManager
    private var accelerometer: Sensor? = null
    private lateinit var btnRestart: Button
    private lateinit var mDrawable: ImageView
    private lateinit var mazeView: MazeView
    private lateinit var darkOverlay: View
    private lateinit var youWonText: TextView
    private lateinit var btnRestartGame: Button
    private lateinit var finishLine: ImageView

    companion object {
        var x = 0
        var y = 0
    }

    private var velocityX = 0f
    private var velocityY = 0f
    private val DAMPING_FACTOR = 0.65f
    private val BOUNCE_REDUCTION_FACTOR = 0.5f
    private val SPEED_REDUCTION_FACTOR = 0.01f
    private var isPaused = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mazeView = findViewById(R.id.mazeView)
        mDrawable = findViewById(R.id.ball)
        btnRestart = findViewById(R.id.btnRestart)
        darkOverlay = findViewById(R.id.darkOverlay)
        youWonText = findViewById(R.id.youWonText)
        btnRestartGame = findViewById(R.id.btnRestartGame)
        finishLine = findViewById(R.id.finish_line)

        btnRestart.setOnClickListener {
            resetBallPosition()
        }

        btnRestartGame.setOnClickListener {
            resumeGame()
        }

        sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
        accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
        initializeMaze()
    }

    private fun resetBallPosition() {
        x = 0
        y = 0
        velocityX = 0f
        velocityY = 0f
        updateImageViewPosition()
        if (isPaused) {
            resumeGame()
        }
    }

    private fun initializeMaze() {
        val maze = arrayOf(
            intArrayOf(0, 0, 0, 0, 0, 1, 0, 0, 0, 1),
            intArrayOf(1, 0, 1, 1, 0, 1, 0, 1, 0, 1),
            intArrayOf(1, 0, 1, 1, 0, 0, 0, 1, 0, 1),
            intArrayOf(0, 0, 1, 1, 0, 1, 1, 1, 0, 1),
            intArrayOf(0, 0, 0, 1, 1, 1, 0, 0, 0, 0),
            intArrayOf(1, 1, 0, 1, 0, 0, 0, 1, 1, 0),
            intArrayOf(0, 0, 0, 1, 1, 1, 1, 1, 0, 0),
            intArrayOf(0, 1, 0, 0, 0, 1, 0, 0, 0, 1),
            intArrayOf(0, 1, 1, 1, 0, 1, 0, 1, 1, 1),
            intArrayOf(0, 0, 1, 0, 0, 1, 0, 0, 0, 0),
            intArrayOf(1, 1, 1, 0, 1, 1, 1, 1, 1, 0),
            intArrayOf(0, 0, 0, 0, 0, 0, 1, 0, 0, 0),
            intArrayOf(0, 1, 1, 0, 1, 0, 1, 0, 1, 1),
            intArrayOf(1, 1, 0, 0, 1, 0, 0, 0, 0, 0),
            intArrayOf(0, 0, 0, 1, 1, 0, 1, 1, 0, 1),
            intArrayOf(0, 1, 1, 1, 0, 0, 1, 1, 0, 1)
        )
        mazeView.maze = maze
    }

    private fun checkMazeCollision() {
        val cellSize = if (mazeView.width > 0 && mazeView.maze!!.isNotEmpty()) mazeView.width / mazeView.maze!![0].size else return

        val futureX = (x + velocityX).toInt()
        val futureY = (y + velocityY).toInt()

        if (isCollision(futureX, y)) {
            velocityX *= -BOUNCE_REDUCTION_FACTOR * DAMPING_FACTOR
        } else {
            x = futureX
        }

        if (isCollision(x, futureY)) {
            velocityY *= -BOUNCE_REDUCTION_FACTOR * DAMPING_FACTOR
        } else {
            y = futureY
        }

        checkFinishCollision()
    }

    private fun checkFinishCollision() {
        val ballRadius = mDrawable.width / 2
        val finishLineX = finishLine.x.toInt()
        val finishLineY = finishLine.y.toInt()
        val finishLineWidth = finishLine.width
        val finishLineHeight = finishLine.height

        if (x + ballRadius >= finishLineX && x - ballRadius <= finishLineX + finishLineWidth &&
            y + ballRadius >= finishLineY && y - ballRadius <= finishLineY + finishLineHeight) {
            pauseGame()
        }
    }

    private fun isCollision(x: Int, y: Int): Boolean {
        val cellSize = mazeView.width / mazeView.maze!![0].size
        val mazeX = x / cellSize
        val mazeY = y / cellSize
        return mazeY in mazeView.maze!!.indices && mazeX in mazeView.maze!![mazeY].indices && mazeView.maze!![mazeY][mazeX] == 1
    }

    private fun updateImageViewPosition() {
        mDrawable.y = y.toFloat()
        mDrawable.x = x.toFloat()
    }

    private fun pauseGame() {
        isPaused = true
        sensorManager.unregisterListener(this)
        darkOverlay.visibility = View.VISIBLE
        youWonText.visibility = View.VISIBLE
        btnRestartGame.visibility = View.VISIBLE
    }

    private fun resumeGame() {
        isPaused = false
        sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME)
        darkOverlay.visibility = View.GONE
        youWonText.visibility = View.GONE
        btnRestartGame.visibility = View.GONE
        resetBallPosition()
    }

    override fun onResume() {
        super.onResume()
        if (!isPaused) {
            sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME)
        }
    }

    override fun onPause() {
        super.onPause()
        sensorManager.unregisterListener(this)
    }

    override fun onSensorChanged(event: SensorEvent?) {
        if (!isPaused) {
            event?.let {
                if (it.sensor.type == Sensor.TYPE_ACCELEROMETER) {
                    velocityX -= it.values[0] * SPEED_REDUCTION_FACTOR
                    velocityY += it.values[1] * SPEED_REDUCTION_FACTOR

                    checkMazeCollision()
                    handleScreenCollisions()
                    updateImageViewPosition()
                }
            }
        }
    }

    private fun handleScreenCollisions() {
        val displayMetrics = DisplayMetrics()
        windowManager.defaultDisplay.getMetrics(displayMetrics)
        val screenWidth = displayMetrics.widthPixels
        val screenHeight = displayMetrics.heightPixels

        val maxX = screenWidth - mDrawable.width
        val maxY = screenHeight - mDrawable.height

        if (x < 0 || x > maxX) {
            x = max(0, min(x, maxX))
            velocityX *= -1 * DAMPING_FACTOR
        }
        if (y < 0 || y > maxY) {
            y = max(0, min(y, maxY))
            velocityY *= -1 * DAMPING_FACTOR
        }
    }

    override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
        // Not needed for this example
    }
}
`

MazeView.kt

package com.example.mazeball

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View

class MazeView: View {
    var maze: Array<IntArray>? = null
        set(value) {
            field = value
            invalidate()
        }
    private val wallPaint = Paint().apply { color = 0xFF000000.toInt() }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        initializeMaze()
    }

    private fun initializeMaze() {
        if (maze == null) {
            maze = arrayOf(
                intArrayOf(1, 0),
                intArrayOf(0, 1)
            )
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        maze?.let {
            val cellSize = width / it[0].size
            for (i in it.indices) {
                for (j in it[i].indices) {
                    if (it[i][j] == 1) {
                        canvas.drawRect(
                            (j * cellSize).toFloat(),
                            (i * cellSize).toFloat(),
                            ((j + 1) * cellSize).toFloat(),
                            ((i + 1) * cellSize).toFloat(),
                            wallPaint
                        )
                    }
                }
            }
        }
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.example.mazeball.MazeView
        android:id="@+id/mazeView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <ImageView
        android:id="@+id/finish_line"
        android:layout_width="39dp"
        android:layout_height="30dp"
        android:layout_gravity="bottom|left"
        android:layout_marginEnd="36dp"
        android:baselineAligned="false"
        android:src="@drawable/black_checkerboard"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <ImageView
        android:id="@+id/ball"
        android:layout_width="42dp"
        android:layout_height="42dp"
        android:layout_marginStart="5dp"
        android:layout_marginTop="5dp"
        android:baselineAligned="false"
        android:src="@drawable/redball"
        android:visibility="visible"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btnRestart"
        android:layout_width="98dp"
        android:layout_height="47dp"
        android:layout_marginTop="17dp"
        android:layout_marginEnd="16dp"
        android:text="@string/restart"
        android:textSize="14sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <!-- Dodane elementy -->
    <View
        android:id="@+id/darkOverlay"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#80000000"
        android:visibility="gone"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <TextView
        android:id="@+id/youWonText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/you_won"
        android:textSize="24sp"
        android:textColor="#FFFFFF"
        android:visibility="gone"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <Button
        android:id="@+id/btnRestartGame"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/restart"
        android:visibility="gone"
        app:layout_constraintTop_toBottomOf="@id/youWonText"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

I hope someone has idea how can I improve the code to have properly bouncing of walls ball

New contributor

Vergil Sparda is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật