Jetpack Compose Project for Beginners

Jetpack Compose Project  for Beginners

Jetpack Compose Project  for Beginners

This is a small project for learning purpose for the beginners who are learning Android App Development  using Jetpack Compose. This project is build using the Android Studio Ladybug without using any third party dependency. 


Game for Beginners

This Game for Beginners is being build using Jetpack Compose if you are a beginners then you should try to build this game. Learning through project is the best way to understand the basic of development. So, Lets try to build the Game for Beginners.

What are we going to build?

We are going to build a game to learn the basics of Jetpack Compose. The game will feature a screen where balls begin to fall when the play button is pressed and pause when it is pressed again. At the bottom of the screen, we will have a button to play and pause the game and a basket to catch the balls. At the top of the screen, we will have a text display showing how many balls are caught in the basket. For each ball caught, the score will increase by 1.

Steps:

  1. Create a New  Project
  2. Select the Empty Activity(Compose)  from the template and click on Next.
  3. In the Name section write the name of project in my case it is My First App.
  4. Now create two files one for Game Screen and second for data and functions for this game
  5. Connect your real Android device and Run the Game.


MainActivity

Copy this code →

package com.example.myfirstapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.ui.Modifier
import com.example.myfirstapp.ui.theme.MyFirstAppTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            MyFirstAppTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    GameForBeginners(
                        modifier = Modifier.padding(innerPadding)
                    )
                }
            }
        }
    }
}

GameScreen

Copy this code →

package com.example.myfirstapp

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.withFrameNanos
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

@Composable
fun GameForBeginners(modifier: Modifier) {
    val screenWidthPx = with(LocalDensity.current) { 520.dp.toPx() }
    val screenHeightPx = with(LocalDensity.current) { 800.dp.toPx() }
    var isRunning by remember { mutableStateOf(false) }
    var gameState by remember {
        mutableStateOf(
            GameState(
                stars = emptyList(),
                basket = Basket(
                    x = screenWidthPx / 2 - 10f,
                    y = screenHeightPx - 100f,
                    width = 200f,
                    height = 200f
                )
            )
        )
    }
    var lastStarTime by remember { mutableLongStateOf(0L) }
    val starInterval = 1_000_000_000L // 1 second in nanoseconds
    var starId by remember { mutableIntStateOf(0) }

    LaunchedEffect(isRunning) {
        while (isRunning) {
            withFrameNanos { frameTimeNanos ->
                val elapsedTime = frameTimeNanos - lastStarTime
                val deltaTime = 0.016f // Assuming 60 FPS

                val newStars = moveStars(gameState.stars, deltaTime)
                val (remainingStars, scoreIncrement) = checkCatches(newStars, gameState.basket)

                if (elapsedTime >= starInterval) {
                    lastStarTime = frameTimeNanos
                    starId += 1
                    gameState = gameState.copy(
                        stars = addNewStar(remainingStars, screenWidthPx, starId),
                        score = gameState.score + scoreIncrement
                    )
                } else {
                    gameState = gameState.copy(
                        stars = remainingStars,
                        score = gameState.score + scoreIncrement
                    )
                }

            }
        }
    }
    GameScreen(
        gameState = gameState,
        onMoveBasket = { deltaX, deltaY ->
            gameState = gameState.copy(
                basket = moveBasket(
                    gameState.basket,
                    deltaX,
                    deltaY,
                    screenWidthPx,
                    screenHeightPx
                )
            )
        },
        onToggleGame = { isRunning = !isRunning },
        isRunning = isRunning
    )
}


@Composable
fun GameScreen(
    gameState: GameState,
    onMoveBasket: (Float, Float) -> Unit,
    onToggleGame: () -> Unit,
    isRunning: Boolean
) {
    Box(modifier = Modifier.fillMaxSize().background(color = Color.Black)) {
        // Render stars
        gameState.stars.forEach { star ->
            Canvas(
                modifier = Modifier
                    .size(star.size.dp)
                    .offset(star.x.dp, star.y.dp)
            ) {
                drawCircle(color = star.color, radius = star.size / 2)
            }
        }

        // This is basket to catch the balls
        Image(
            painter = painterResource(id = R.drawable.basket),
            contentDescription = "Basket",
            modifier = Modifier
                .size(gameState.basket.width.dp, gameState.basket.height.dp)
                .offset(gameState.basket.x.dp, gameState.basket.y.dp)
                .pointerInput(Unit) {
                    detectDragGestures { change, dragAmount ->
                        change.consume()
                        onMoveBasket(dragAmount.x, dragAmount.y)
                    }
                }
        )
        Button(
            onClick = { onToggleGame() },
            modifier = Modifier
                .align(Alignment.BottomCenter)
                .padding(bottom = 16.dp)
        ) {
            Text(text = if (isRunning) "Pause" else "Play")
        }
Text(text = "Coding Bihar", color = Color.Green,
    fontWeight = FontWeight.Bold, fontSize = 37.sp,
    modifier = Modifier.padding(start = 20.dp, top = 70.dp)
    .align(Alignment.TopStart))
        // Display score
        Text(
            text = "Score: ${gameState.score}",
            fontSize = 45.sp,
            fontWeight = FontWeight.Bold,
            modifier = Modifier
                .align(Alignment.TopCenter)
                .padding(16.dp)
                .background(color = Color.White, shape = RoundedCornerShape(4.dp))
                .padding(8.dp),
            color = Color.Red
        )
    }
}

GameData

Copy this code →

package com.example.myfirstapp

import androidx.compose.ui.graphics.Color
import kotlin.random.Random

data class Basket(
    val x: Float,
    val y: Float,
    val width: Float,
    val height: Float
)

data class GameState(
    val stars: List = emptyList(),
    val basket: Basket,
    val score: Int = 0,
    val speedMultiplier: Float = 1f)

data class Star(
    val id: Int,
    val x: Float,
    val y: Float,
    val size: Float,
    val speed: Float,
    val color: Color
)
fun getRandomColor(): Color {
    val red = Random.nextInt(256)
    val green = Random.nextInt(256)
    val yellow = Random.nextInt(356)
    return Color(red, green, yellow)
}

fun checkCatches(stars: List, basket: Basket): Pair, Int> {
    val caughtStars = stars.filter { star ->
        star.y + star.size >= basket.y &&
                star.y <= basket.y + basket.height &&
                star.x + star.size >= basket.x &&
                star.x <= basket.x + basket.width
    }

    val newStars = stars - caughtStars.toSet()
    val scoreIncrement = caughtStars.size

    return newStars to scoreIncrement
}
fun moveStars(stars: List, deltaTime: Float): List {
    return stars.map { star ->
        star.copy(y = star.y + star.speed * deltaTime)
    }
}

fun addNewStar(stars: List, screenWidth: Float, starId: Int): List {
    val newStar = Star(
        id = starId,
        x = Random.nextFloat() * screenWidth,
        y = 0f,
        size = 80f,
        speed = 320f,
        color = getRandomColor()
    )
    return stars + newStar
}
fun moveBasket(basket: Basket, deltaX: Float, deltaY: Float, screenWidth: Float, screenHeight: Float): Basket {
    val newX = (basket.x + deltaX).coerceIn(0f, screenWidth - basket.width)
    val newY = (basket.y + deltaY).coerceIn(0f, screenHeight - basket.height)
    return basket.copy(x = newX, y = newY)
}
Also Read
Previous Post Next Post