androidandroid-jetpack-composeandroid-animationjetpack-compose-animation

How to detect if two views(composables) are overlapping in jetpack compose?


I'm creating a 2D game with simple animations in jetpack compose. The game is simple, a balloon will float on top, a cannon will shoot arrow towards balloon. If the arrow hits balloon, player gets a point. I'm able to animate all of the above things but one. How would I detect when the arrow and balloon areas intersect?

The arrow is moving upwards with animation, also the balloon is animating side-ways. I want to capture the point at which the arrow touches the balloon. How would I proceed with this?

CODE:

Balloon Composable

@Composable
fun Balloon(modifier: Modifier, gameState: GameState) {
    val localConfig = LocalConfiguration.current
    val offsetX by animateDpAsState(
        targetValue = if (gameState == GameState.START) 0.dp else (localConfig.screenWidthDp.dp - 80.dp),
        infiniteRepeatable(
            animation = tween((Math.random() * 1000).toInt(), easing = LinearEasing),
            repeatMode = RepeatMode.Reverse
        )
    )

    val offsetY by animateDpAsState(
        targetValue = if (gameState == GameState.START) 0.dp else (localConfig.screenHeightDp.dp / 3),
        infiniteRepeatable(
            animation = tween((Math.random() * 1000).toInt(), easing = LinearEasing),
            repeatMode = RepeatMode.Reverse
        )
    )

    Box(
        modifier = modifier
            .offset(offsetX, offsetY)
            .size(80.dp)
            .background(Color.Red, shape = CircleShape),
        contentAlignment = Alignment.Center,

        ) {
        Text(text = "D!!")
    }
}

Cannon Composable:

@Composable
fun ShootBalloon(modifier: Modifier) {
    val localConfig = LocalConfiguration.current
    var gameState by remember { mutableStateOf(GameState.START) }
    val offsetX by animateDpAsState(
        targetValue = if (gameState == GameState.START) 0.dp else (localConfig.screenWidthDp.dp - 50.dp),
        infiniteRepeatable(
            animation = tween(2000, easing = LinearEasing),
            repeatMode = RepeatMode.Reverse
        )
    )
    val offsetY by animateDpAsState(
        targetValue = if (gameState == GameState.START) 0.dp else (-(localConfig.screenHeightDp.dp - 50.dp)),
        infiniteRepeatable(
            animation = tween(500, easing = LinearEasing),
            repeatMode = RepeatMode.Restart
        )
    )
    Column(
        verticalArrangement = Arrangement.SpaceBetween,
        horizontalAlignment = Alignment.Start
    ) {
        if (gameState == GameState.START) {
            Button(onClick = { gameState = GameState.PLAYING }) { Text(text = "Play") }
        }
        Box(
            modifier = Modifier
                .size(50.dp)
                .offset(x = offsetX)
                .background(Color.Green),
            contentAlignment = Alignment.Center
        ) {
            Image(
                painter = painterResource(R.drawable.ic_baseline_arrow_upward_24),
                contentDescription = null,
                contentScale = ContentScale.Crop,
                modifier = Modifier
                    .offset(y = offsetY)
                    .onGloballyPositioned { layoutCoordinates ->
                        val offset = layoutCoordinates.positionInRoot()

                    }
            )
        }
    }
}

I'm animating following things,

  1. The cannon box moves side ways, the arrow image moves upwards.
  2. The balloon has random movement.

enter image description here


Solution

  • Add a ticker and on each tick check the global coordinate of arrow and offsets of balloon. If values are same, it means they hit on another