Photo by Alexei Maridashvili on Unsplash

The GameScreen composable is the central component responsible for managing the game play logic in the Flappy Musketeer game. This code file defines the behavior of the game during game play, including handling user input, updating the game state, and rendering game elements.

Let’s break down the GameScreen composable step by step and explain each part of the code with relevant code snippets —

1. State Initialization

// Game state and scores initialization
var gameState by remember { mutableStateOf(GameState.NOT_STARTED) }
var score by remember { mutableLongStateOf(0L) }
var lastScore by remember { mutableLongStateOf(preferencesManager.getData("last_score", 0L)) }
var bestScore by remember { mutableLongStateOf(preferencesManager.getData("best_score", 0L)) }
var birdOffset by remember { mutableStateOf(0.dp) }
var birdRect by remember { mutableStateOf(Rect(0f, 0f, 64.dp.value, 64.dp.value)) }

In this section, we initialize various game state variables such as gameState, score, lastScore, bestScore, birdOffset, and birdRect. These variables are used to track the game’s progress and the position of the bird.

2. Pipe (Obstacles) Dimensions Initialization

Pipe Dimensions
var pipeDimensions by remember {
mutableStateOf(Triple(0.1f, 0.4f, 0.5f))

Here, we initialize pipeDimensions as a Triple to store the weights of the top, gap, and bottom pipes. These weights determine the relative sizes of the pipes.

3. Update Score Callback

// Callback function to update the score
val updateScoreCallback: (Long) -> Unit = {
score += it

updateScoreCallback is a callback function used to update the game’s score whenever necessary.

4. Bird Falling Animation

Bird Falling
LaunchedEffect(key1 = birdOffset, gameState) {
while (gameState == GameState.PLAYING) {
birdOffset += 4.dp

In this section, we use a LaunchedEffect to continuously update the birdOffset and simulate the bird falling when the game is in the PLAYING state.

5. Update Bird and Pipe Rectangles

// Callback function to update the bird's rectangle
val updateBirdRect: (birdRect: Rect) -> Unit = {
birdRect = it
pipeDimensions = getPipeDimensions(it, screenHeight)

These callback functions are responsible for updating the bird’s and pipes’ rectangles. These rectangles are crucial for collision detection between the bird and pipes.

6. Collision Detection

Collision Detection
// Callback function to update the pipe's rectangle
val updatePipeRect: (pipeRect: Rect) -> Unit = {
if (!it.intersect(birdRect).isEmpty) {
// Handle collision with pipes
// ...

This callback function handles collision detection between the bird and pipes. When a collision is detected, the game state transitions to COMPLETED and we updated the best score and last scores.

In addition to this we also navigate to the game over screen (see navigation section to learn more)

7. Tap Gesture Handling

// Tap Gesture Handling
modifier = Modifier
.pointerInput(Unit) {
onTap = {
if (gameState == GameState.PLAYING) {
// Handle bird jump
coroutineScope.launch {
var offsetChange = 80.dp
while (offsetChange > 0.dp) {
birdOffset -= 2.dp
offsetChange -= 2.dp

Here, we set up tap gesture handling. When the player taps the screen during game play, the bird’s position is updated to simulate a jump.

8. Game Layout

  1. Box Composable —
modifier = Modifier.fillMaxSize()
) {
// ...
  • The game layout is encapsulated within a Box composable, allowing the placement of multiple components on top of each other.

2. Background —

  • The Background composable renders the game’s background, setting the appropriate background image based on the selected theme.

3. Pipes —

updatePipeRect = updatePipeRect,
updateScoreCallback = updateScoreCallback,
gameState = gameState,
pipeDimensions = pipeDimensions.copy()
  • The Pipes composable manages the generation and movement of pipes in the game. It handles collision detection with the bird and updates the score.

4. GameState Handling —

when (gameState) {
// ...

This section uses a when expression to handle different game states —

  • GameState.PLAYING — Displays the bird, score, and pause button during game play. Tapping the pause button triggers the pause callback.
  • GameState.NOT_STARTED, GameState.COMPLETED — Shows the “Play” button to start or restart the game. Displays the last score and best score if available.
  • GameState.PAUSE — Displays the “Play” button to resume the game.

5. Bird —

Bird(birdOffset, updateBirdRect)
  • The Bird composable renders the bird character on the screen. The birdOffset determines the bird’s vertical position, simulating its movement.

6. Play Button —

  • The Play composable displays the “Play” button, allowing the player to start or resume the game when tapped. It triggers the onPlayCallback when pressed.

7. Ground —

Ground("Flappy Score", score, enablePause = true, onPauseCallback)
  • The Ground composable displays the game’s score and includes an optional pause button when enablePause is set to true. The onPauseCallback is triggered when the pause button is tapped.

Source link