Detect Collision between two Sprites in an Android Kotlin Game

Collision detection in a 2D Android game means checking whether two game objects occupy the same area on the screen. In this Kotlin Android game tutorial, we detect collision between two sprites represented by bitmaps: Grenade and Player.

To detect collision between two Sprites among many of the existing, we have to maintain a list of all the sprites that are drawn on the Canvas, and in every update (of all Sprites) to the Canvas, we have to run a loop among the available Sprites, if there is an overlap.

In our previous Android Game Development tutorial, we have gone to the extent of making the sprites move and responding the sprites to the user input (touch). In this tutorial, we shall learn how to detect collision between the two Sprites, Grenade and Player. This tutorial is built on the previous tutorial, so you may have a look at it.

The method used here is rectangle-based collision detection. It is also called bounding box collision detection. It is simple, fast, and suitable for many beginner 2D games where each sprite can be approximated by a rectangle.

How rectangle collision detection works for Android sprites

Every bitmap sprite has a position and size. If the sprite is drawn at x and y, and its bitmap has width w and height h, the rectangular area covered by the sprite can be represented as:

  • left: x
  • top: y
  • right: x + w
  • bottom: y + h

Two sprites collide when their rectangles overlap. Android provides the Rect class, so we can create one rectangle for the grenade and one rectangle for the player, then check whether they intersect.

Following is a step by step guide.

Step 1: Get Rectangles from your Sprites.

</>
Copy
val grenadeRect: Rect = Rect(grenade.x, grenade.y, grenade.x+grenade.w, grenade.y+grenade.h)
val playerRect: Rect = Rect(player.x, player.y, player.x+player.w, player.y+player.h)

Step 2: Check for Intersection.

</>
Copy
val grenadeRect: Rect = Rect(grenade.x, grenade.y, grenade.x+grenade.w, grenade.y+grenade.h)
val playerRect: Rect = Rect(player.x, player.y, player.x+player.w, player.y+player.h)
if(grenadeRect.intersect(playerRect)){
    collided=true
    System.out.println("Collided.")
}

Step 3: Update the Sprites based on the “collided” status.

Use Rect.intersects() when you only want to test sprite overlap

The earlier code uses grenadeRect.intersect(playerRect). It detects overlap, but it can also modify the rectangle on which it is called. When you only want a true or false collision result, the static Rect.intersects(rect1, rect2) method is usually clearer.

</>
Copy
val grenadeRect = Rect(
    grenade.x,
    grenade.y,
    grenade.x + grenade.w,
    grenade.y + grenade.h
)

val playerRect = Rect(
    player.x,
    player.y,
    player.x + player.w,
    player.y + player.h
)

val collided = Rect.intersects(grenadeRect, playerRect)

You can then use collided to stop the game, reduce health, play a sound, show a game over screen, or reset the sprite position.

Add sprite collision detection inside the GameView update loop

Collision detection should usually run after the sprites update their positions and before the frame is drawn. In this example, the grenade position is updated first. If the player is being touched, the player position is updated. Then the two rectangles are compared.

</>
Copy
private var collided = false

fun update() {
    grenade?.update()

    if (touched) {
        player?.updateTouch(touched_x, touched_y)
    }

    val currentGrenade = grenade
    val currentPlayer = player

    if (currentGrenade != null && currentPlayer != null) {
        val grenadeRect = Rect(
            currentGrenade.x,
            currentGrenade.y,
            currentGrenade.x + currentGrenade.w,
            currentGrenade.y + currentGrenade.h
        )

        val playerRect = Rect(
            currentPlayer.x,
            currentPlayer.y,
            currentPlayer.x + currentPlayer.w,
            currentPlayer.y + currentPlayer.h
        )

        collided = Rect.intersects(grenadeRect, playerRect)
    }
}

This example uses safe nullable checks instead of force-unwrapping with !!. That makes the game loop less likely to crash if a sprite is not initialized yet.

Expose sprite bounds from each Kotlin game object

As the game grows, it is better to keep rectangle creation inside each sprite class. This keeps GameView cleaner and avoids repeating the same bounds code for every sprite.

</>
Copy
import android.graphics.Rect

fun getBounds(): Rect {
    return Rect(x, y, x + w, y + h)
}

If both Grenade and Player expose a getBounds() function, the collision check becomes shorter.

</>
Copy
val collided = Rect.intersects(
    grenade.getBounds(),
    player.getBounds()
)

This approach also makes it easier to add more enemies, coins, bullets, walls, or power-ups later.

Detect collision among many sprites in a Kotlin Android game

When there are only two sprites, one collision check is enough. When the game has many sprites, you can keep related sprites in a list and loop through them. For example, a player may collide with many grenades.

</>
Copy
for (grenade in grenades) {
    if (Rect.intersects(player.getBounds(), grenade.getBounds())) {
        collided = true
        break
    }
}

This is simple and readable for a small game. For a larger game with hundreds of moving objects, checking every sprite against every other sprite can become expensive. In that case, you can first do a broad check using simple rectangles, and only then do more detailed checks for the objects that are close enough to collide.

Rectangle collision, circle collision, and pixel-perfect collision for bitmaps

There are different ways to detect collision between sprites. The right method depends on the shape of the game objects and how accurate the collision needs to feel.

Collision methodBest used whenTrade-off
Rectangle collisionSprites are box-shaped or approximate accuracy is acceptableFast and simple, but can detect collision in transparent corner areas of a bitmap
Circle collisionObjects are round, such as balls, bubbles, or circular playersSimple and often more natural for round sprites, but not suitable for long or irregular shapes
Pixel-perfect collisionThe visible pixels of the bitmap must match very closelyMore accurate, but slower and more complex than rectangle checks

For the grenade and player example, rectangle collision is enough to learn the concept. If your bitmap has large transparent areas, rectangle collision may feel unfair because the invisible part of the bitmap is still included in the rectangle. In such cases, use a smaller hitbox or consider pixel-level collision after a rectangle check confirms that the sprites overlap.

Circle collision detection for round Android sprites

If both sprites are round, you can compare the distance between their center points. A collision happens when the distance between centers is less than or equal to the sum of the two radii.

</>
Copy
import kotlin.math.sqrt

fun areCirclesColliding(
    x1: Float,
    y1: Float,
    radius1: Float,
    x2: Float,
    y2: Float,
    radius2: Float
): Boolean {
    val dx = x1 - x2
    val dy = y1 - y2
    val distance = sqrt(dx * dx + dy * dy)

    return distance <= radius1 + radius2
}

This method works well when your player sprite is a circle. For faster code, you can compare squared distance and avoid calling sqrt(), but the above version is easier to read for beginners.

Pixel-perfect collision detection for Android bitmaps

Pixel-perfect collision detection checks whether the non-transparent pixels of two bitmaps overlap. This is useful when the sprite shape is irregular and rectangle collision is too rough.

A common practical approach is:

  • First run a fast rectangle collision check.
  • If the rectangles do not overlap, there is no collision.
  • If the rectangles overlap, check only the overlapping area at pixel level.
  • Treat two pixels as colliding only when both pixels are visible enough, usually by checking their alpha values.

Pixel-perfect checks can be expensive if they run for many sprites on every frame. Use them only where the extra accuracy matters.

Show a basic game over response after sprite collision

After detecting collision, decide what should happen in the game. In a simple avoid-the-grenade game, you may stop movement and draw a game over message.

</>
Copy
override fun draw(canvas: Canvas) {
    super.draw(canvas)

    grenade?.draw(canvas)
    player?.draw(canvas)

    if (collided) {
        val paint = Paint()
        paint.textSize = 64f
        canvas.drawText("Game Over", 80f, 120f, paint)
    }
}

If you add this example to your project, import android.graphics.Paint. In a complete game, you may also pause the thread, show a restart button, update score, or navigate to a separate result screen.

Common mistakes in Android sprite collision detection

  • Checking collision before updating positions: This can make the game react one frame late.
  • Using full bitmap bounds for transparent images: The player may appear to collide before visible pixels touch. Use a smaller hitbox if needed.
  • Creating too many objects in every frame: Creating new Rect objects repeatedly is fine for a beginner example, but larger games should reuse objects where practical.
  • Forgetting null checks: Collision code can crash if a sprite has not been created before the update loop runs.
  • Using one collision method for every shape: Rectangles, circles, and pixel-perfect checks each fit different sprite shapes.

QA checklist for this Android bitmap collision tutorial

  • Both Grenade and Player expose their current x, y, width, and height values correctly.
  • The rectangle uses x + w for right and y + h for bottom.
  • The collision check runs after sprite position updates inside the game loop.
  • The project uses Rect.intersects() when the rectangle should not be modified.
  • The player and grenade bitmap resources are loaded before collision checks run.
  • The collision response is visible in the game, such as game over text, score change, or sprite reset.
  • Transparent bitmap padding is considered when testing whether the collision feels correct.
  • The game is tested on a real Android device with touch movement and moving sprites.

FAQ on collision detection between Android sprites

How do you create collision detection in a 2D Android game?

Create a shape that represents each sprite, such as a rectangle or circle. Update the sprite positions, compare the shapes for overlap, and then run a game response if collision is detected.

What is the simplest collision method for two bitmap sprites?

Rectangle collision is the simplest method. Create a Rect around each bitmap and use Rect.intersects() to check whether the two rectangles overlap.

When should I use pixel-perfect collision in an Android game?

Use pixel-perfect collision when the sprite has an irregular shape and rectangle collision feels inaccurate. For performance, first check rectangle overlap and run pixel-level checks only inside the overlapping area.

Why does my sprite collide before the images visually touch?

This usually happens when the bitmap has transparent padding. The rectangle includes the transparent area too. Crop the image, use a smaller hitbox, or use a more accurate collision method.

Should collision detection run in update() or draw()?

Collision detection should generally run in update(), after movement is calculated. The draw() function should mainly render the current game state on the Canvas.

Conclusion: Detecting collision between two bitmap sprites in Kotlin

In this Kotlin Android Tutorial, we have learnt to detect collision between two sprites. We used Android Rect objects to represent the bounds of two bitmaps and checked whether those rectangles overlap.

Rectangle collision detection is a good first method for Android game development because it is easy to understand and fast to run. As your game improves, you can use smaller hitboxes, circle collision, or pixel-perfect collision for sprites that need more accurate interaction.