Royss
Royss

Reputation: 33

Pieces Moving Under Cells with Offset Animations in Jetpack Compose

I am implementing a chess game using Jetpack Compose in Kotlin. The chess board has 8x8 cells, and I have both white and black pieces. When white pieces are moving from the bottom towards the top, everything seems to work normally. However, when black pieces move from the top to the bottom, they go under the cells of the first row, and as they continue to move, they go under the cells of the second row and so on.

Here's the relevant code for rendering the chessboard and pieces:

@Composable
fun RenderChessBoard(
    chessBoardState: ChessBoard,
    composableScope: CoroutineScope = rememberCoroutineScope(),
    onPieceMove: (ChessPiece, Position) -> Unit
) {
    var cellSize by remember { mutableStateOf(IntSize(0, 0)) }
    val selectedPiece = remember { mutableStateOf<ChessPiece?>(null) }
    var pieceOffsets by remember { mutableStateOf<Map<ChessPiece, IntOffset>>(emptyMap()) }

    Column {
        chessBoardState.cells.forEachIndexed { rowIndex, row ->
            Row {
                row.forEachIndexed { columnIndex, piece ->
                    val currentPosition = Position(rowIndex, columnIndex)
                    val cellColor =
                        determineCellColor(selectedPiece.value, currentPosition, chessBoardState)

                    Box(
                        modifier = Modifier
                            .weight(1f)
                            .onSizeChanged { if (cellSize.width == 0) cellSize = it }
                            .aspectRatio(1f)
                            .background(cellColor),
                        contentAlignment = Alignment.Center
                    ) {
                        DrawAvailableMoves(
                            selectedPiece = selectedPiece.value,
                            currentPosition = currentPosition,
                            chessBoard = chessBoardState,
                            onPieceMove = { chessPiece, targetPosition ->
                                pieceOffsets = pieceOffsets + (chessPiece to IntOffset(
                                    ((targetPosition.column - chessPiece.position.column) * cellSize.width),
                                    ((targetPosition.row - chessPiece.position.row) * cellSize.height)
                                ))
                                composableScope.launch {
                                    delay(ANIMATION_DURATION_MS)
                                    onPieceMove.invoke(chessPiece, targetPosition)
                                }
                                selectedPiece.value = null
                            }
                        )

                        piece?.let { currentPiece ->
                            val targetPosition = pieceOffsets[currentPiece] ?: IntOffset(0, 0)
                            val offsetState by animateIntOffsetAsState(
                                targetValue = targetPosition,
                                animationSpec = tween(durationMillis = ANIMATION_DURATION_MS.toInt()),
                            )

                            PieceView(
                                modifier = Modifier
                                    .offset { offsetState }
                                    .fillMaxSize(),
                                piece = currentPiece,
                                
                                    ...
                        }
                    }
                }
            }
        }
    }
}

I've noticed that the issue seems to be with the rendering of the pieces, but I am not able to pinpoint the exact problem. here's screen from layout inspector

How can I prevent the pieces from going under the cells while moving up (and sometimes right)? What am I missing here? Any guidance or suggestions are appreciated!

Upvotes: 1

Views: 439

Answers (1)

Thracian
Thracian

Reputation: 67248

When child Composables of a parent are laid out the last one is drawn on top as you can see this clearly with Box that stacks or uses Placeable.placeRelative(0,0). With Row, or Column you can use offset to observe this behavior as in your case.

For instance with Column when you place 2 Composables as

enter image description here

@Preview
@Composable
private fun Test() {
    Column(
        modifier = Modifier
            .padding(30.dp)
            .fillMaxSize()
    ) {
        Box(
            modifier = Modifier
                .size(100.dp)
                .background(Color.Yellow)
        )

        Box(
            modifier = Modifier
                .offset(0.dp, (-50).dp)
                .size(100.dp)
                .background(Color.Red)
        )
    }
}

However you can use Modifier.zIndex to change order of drawing of children composables.

Creates a modifier that controls the drawing order for the children of the same layout parent. A child with larger zIndex will be drawn on top of all the children with smaller zIndex. When children have the same zIndex the original order in which the parent placed the children is used. Note that if there would be multiple zIndex modifiers applied for the same layout the sum of their values will be used as the final zIndex. If no zIndex were applied for the layout then the default zIndex is 0.

enter image description here

@Preview
@Composable
private fun Test2() {
    Column(
        modifier = Modifier
            .padding(30.dp)
            .fillMaxSize()
    ) {
        Box(
            modifier = Modifier
                .zIndex(1f)
                .size(100.dp)
                .background(Color.Yellow)
        )

        Box(
            modifier = Modifier
                .offset(0.dp, (-50).dp)
                .size(100.dp)
                .background(Color.Red)
        )
    }
}

Upvotes: 0

Related Questions