PPB G - Cake Clicker App
Kode : https://github.com/Hfdrsyd/Tugas-PPB/tree/main/DessertClicker
🍰 Membangun Aplikasi Dessert Clicker dengan Kotlin & Jetpack Compose
Dessert Clicker adalah aplikasi game sederhana di mana pengguna mengetuk gambar makanan penutup (dessert) untuk mendapatkan uang virtual. Setiap klik meningkatkan jumlah dessert yang terjual dan total pendapatan. Aplikasi ini menyenangkan, ringan, dan sangat bagus untuk mempelajari dasar pengembangan Android modern.
🎮 Gambaran Umum Aplikasi
Mekanisme Dasar Permainan:
-
Setiap klik pada gambar dessert akan:
-
✅ Menambah jumlah dessert yang terjual
-
✅ Menambah pendapatan berdasarkan harga dessert saat ini
-
-
Seiring peningkatan penjualan:
-
🎂 Dessert akan berubah (misalnya: dari cupcake menjadi donut hingga oreo)
-
💰 Harga per klik juga akan meningkat
-
✨ Fitur Utama
Fitur | Penjelasan |
---|---|
🖱️ Klik untuk bermain | Tap pada gambar dessert untuk menambah penjualan dan pendapatan |
📈 Sistem progresif | Dessert berganti otomatis sesuai total penjualan |
📊 Statistik real-time | Tampilkan jumlah dessert terjual dan total pendapatan |
📤 Share hasil permainan | Bagikan pencapaian lewat aplikasi lain (WA, IG, dsb.) |
🌗 Light & Dark Theme | Mendukung tema gelap dan terang sesuai sistem |
🧩 UI Material 3 | Gunakan Material Design 3 untuk tampilan modern |
🏗️ Arsitektur dan Struktur Kode
1. Model Data (Dessert.kt
)
data class Dessert(
val imageId: Int,
val price: Int,
val startProductionAmount: Int
)
Setiap Dessert
menyimpan:
-
imageId
: ID gambar -
price
: Harga per klik -
startProductionAmount
: Total penjualan minimum agar dessert ini muncul
2. Data Source (Datasource.kt
)
Berisi daftar dessert secara berurutan berdasarkan startProductionAmount
:
val dessertList = listOf(
Dessert(R.drawable.cupcake, price = 5, startProductionAmount = 0),
Dessert(R.drawable.donut, price = 50, startProductionAmount = 5),
Dessert(R.drawable.eclair, price = 100, startProductionAmount = 10),
...
Dessert(R.drawable.oreo, price = 6000, startProductionAmount = 20000)
)
3. Activity Lifecycle (MainActivity.kt
)
MainActivity
mencatat siklus hidup aplikasi menggunakan log:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("DessertClicker", "onCreate")
setContent { DessertClickerApp() }
}
Lifecycle seperti onStart
, onResume
, onPause
, dan lainnya juga dicatat. Ini berguna untuk debugging dan pemahaman perilaku aplikasi saat berpindah state.
🎨 Komponen UI dengan Jetpack Compose
1. Manajemen State
var revenue by rememberSaveable { mutableStateOf(0) }
var dessertsSold by rememberSaveable { mutableStateOf(0) }
-
rememberSaveable
menjaga data tetap aman saat rotasi layar (configuration change). -
State ini menjadi sumber kebenaran (single source of truth) untuk tampilan.
2. Struktur UI Utama
DessertClickerApp()
Menjadi root composable yang menyatukan semua bagian UI:
@Composable
fun DessertClickerApp() {
var revenue by rememberSaveable { mutableStateOf(0) }
var dessertsSold by rememberSaveable { mutableStateOf(0) }
val currentDessert = determineDessertToShow(dessertList, dessertsSold)
DessertClickerScreen(
revenue = revenue,
dessertsSold = dessertsSold,
dessertImageId = currentDessert.imageId,
onDessertClicked = {
revenue += currentDessert.price
dessertsSold++
}
)
}
DessertClickerScreen()
Tampilan utama berisi:
-
🧁 Gambar dessert (klik untuk bermain)
-
📊 Statistik transaksi (penjualan & pendapatan)
-
🎨 Layout background bakery
@Composable
fun DessertClickerScreen(...) {
Column {
AppBarWithShareButton(...)
Box {
Image(painterResource(R.drawable.bakery_background), ...)
Image(painterResource(dessertImageId), Modifier.clickable { onDessertClicked() })
}
TransactionInfo(revenue, dessertsSold)
}
}
TransactionInfo()
Menampilkan statistik secara visual dengan MaterialTheme.typography
:
@Composable
fun TransactionInfo(revenue: Int, dessertsSold: Int) {
Column(modifier = Modifier.padding(16.dp)) {
Text("Total Revenue: $$revenue", style = MaterialTheme.typography.headlineSmall)
Text("Desserts Sold: $dessertsSold", style = MaterialTheme.typography.bodyLarge)
}
}
🔁 Logika Permainan
Fungsi: determineDessertToShow()
fun determineDessertToShow(desserts: List<Dessert>, dessertsSold: Int): Dessert {
var result = desserts.first()
for (dessert in desserts) {
if (dessertsSold >= dessert.startProductionAmount) {
result = dessert
} else break
}
return result
}
-
Loop dari awal hingga menemukan dessert yang belum bisa diakses
-
Dessert berubah secara otomatis ketika penjualan mencapai batas baru
🎨 Theming & Desain
Material Design 3
-
Warna adaptif dengan dukungan light & dark mode
-
Tipografi terstruktur:
headlineSmall
,bodyLarge
, dll -
Elevasi, padding, dan spacing konsisten
Responsive Design
-
Gunakan
Modifier.padding(WindowInsets...)
untuk mendukung layar dengan notch -
ContentScale.Crop
menjaga proporsi gambar tetap estetis di berbagai ukuran layar
🧠 Best Practices & Pembelajaran
Area | Praktik |
---|---|
✅ State Management | Gunakan rememberSaveable untuk data penting |
🎯 Separation of Concerns | Pisahkan logika, UI, dan data |
📦 Modular Components | Komponen seperti DessertClickerScreen , TransactionInfo , dan AppBar dibuat terpisah |
🐞 Logging | Manfaatkan Log.d untuk memantau lifecycle |
🧱 Scaffold & Layout | Gunakan Scaffold untuk struktur aplikasi yang rapi |
📲 Share Intent Handling | Tangani kasus saat share tidak tersedia menggunakan Toast |
Komentar
Posting Komentar