📖 PrzeglądOverview
Moduł Achievements umożliwia dodanie systemu osiągnięć i gamifikacji
do aplikacji. Wspiera różne typy osiągnięć: jednorazowe, czasowe, streaki i ilościowe.The Achievements module enables adding an achievements and gamification system to your app. Supports various achievement types: one-time, time-based, streaks, and quantitative.
// Definiowanie osiągnięciaof the achievement
ADict.Achievements.define("first_purchase") {
title = "Pierwszy zakup!"
description = "Dokonaj pierwszego zakupu"
points = 100
type = AchievementType.ONE_TIME
}
// Odblokowanie
ADict.Achievements.unlock("first_purchase")
// Inkrementacja postępu
ADict.Achievements.increment("articles_read", 1)
📦 TypyTypes osiągnięć
| TypType | OpisDescription | PrzykładExample |
|---|---|---|
ONE_TIME |
Jednorazowe - odblokuj raz i zostajeOne-time - unlock once and it stays | Pierwszy zakup, Zarejestruj kontoFirst purchase, Register account |
PROGRESSIVE |
Ilościowe - wymaga wykonania N czynnościQuantitative - requires performing N actions | Przeczytaj 100 artykułówRead 100 articles |
STREAK |
Streak - wymaga ciągłościStreak - requires continuity | 7 dni z rzędu7 days in a row |
TIMED |
Czasowe - dostępne tylko przez określony czasTime-limited - available only for a specific time | Świąteczna promocjaHoliday promotion |
📝 Definiowanie osiągnięćDefining Achievements
define(id: String, config: DefinitionBuilder.() -> Unit)
Zdefiniuj nowe osiągnięcie.Define a new achievement.
Właściwości DefinitionBuilderDefinitionBuilder Properties
| WłaściwośćProperty | TypType | OpisDescription |
|---|---|---|
title |
String | Tytuł osiągnięciaof the achievementAchievement title |
description |
String | OpisDescription osiągnięciaof the achievement |
icon |
Int? | ID zasobu ikony (@DrawableRes)Icon resource ID (@DrawableRes) |
points |
Int | Liczba punktów za osiągnięciePoints for achievement |
type |
AchievementType | TypType osiągnięciaof the achievement |
targetCount |
Int | Cel dla PROGRESSIVE/STREAKTarget for PROGRESSIVE/STREAK |
hidden |
Boolean | Czy ukryte do momentu odblokowaniaWhether hidden until unlocked |
expiresAt |
Long? | Timestamp wygaśnięcia (TIMED)Expiration timestamp (TIMED) |
availableFrom |
Long? | Timestamp dostępności (TIMED)Availability timestamp (TIMED) |
category |
String? | Kategoria osiągnięciaof the achievementAchievement category |
tier |
Int | Poziom (1=bronze, 2=silver, 3=gold, 4=platinum)Level (1=bronze, 2=silver, 3=gold, 4=platinum) |
prerequisiteIds |
List<String> | Wymagane wcześniejsze osiągnięciaof the achievementRequired prior achievements |
// Jednorazowe
ADict.Achievements.define("first_purchase") {
title = "Pierwszy zakup!"
description = "Dokonaj pierwszego zakupu w aplikacji"
icon = R.drawable.badge_purchase
points = 100
type = AchievementType.ONE_TIME
category = "shopping"
tier = 1 // Bronze
}
// Ilościowe (progressive)
ADict.Achievements.define("read_100_articles") {
title = "Czytelnik"
description = "Przeczytaj 100 artykułów"
points = 500
type = AchievementType.PROGRESSIVE
targetCount = 100
category = "reading"
tier = 3 // Gold
}
// Streak
ADict.Achievements.define("streak_7") {
title = "Tydzień z rzędu"
description = "Używaj aplikacji 7 dni z rzędu"
points = 200
type = AchievementType.STREAK
targetCount = 7
tier = 2 // Silver
}
// Czasowe (np. świąteczne)
val christmasStart = Calendar.getInstance().apply {
set(2024, Calendar.DECEMBER, 20)
}.timeInMillis
val christmasEnd = Calendar.getInstance().apply {
set(2024, Calendar.DECEMBER, 27)
}.timeInMillis
ADict.Achievements.define("christmas_shopper") {
title = "Świąteczny zakupoholik"
description = "Dokonaj zakupu w okresie świątecznym"
points = 300
type = AchievementType.TIMED
availableFrom = christmasStart
expiresAt = christmasEnd
hidden = true
}
// Z wymaganiami
ADict.Achievements.define("master_shopper") {
title = "Mistrz zakupów"
description = "Dokonaj 50 zakupów"
points = 1000
type = AchievementType.PROGRESSIVE
targetCount = 50
prerequisiteIds = listOf("first_purchase") // Wymaga first_purchase
tier = 4 // Platinum
}
🔓 OdblokowywanieUnlocking
unlock(id: String): Boolean
Odblokuj osiągnięcie (dla ONE_TIME).Unlock achievement (for ONE_TIME).
ZwracaReturns: true jeśli odblokowano, false jeśli już było odblokowane lub nie spełniono wymagań
// Proste odblokowanie
if (ADict.Achievements.unlock("first_purchase")) {
showAchievementUnlockedDialog("first_purchase")
}
// W kontekście zakupu
fun onPurchaseCompleted(product: Product) {
// Sprawdź i odblokuj różne osiągnięciaof the achievement
ADict.Achievements.unlock("first_purchase")
if (product.category == "premium") {
ADict.Achievements.unlock("premium_buyer")
}
// Inkrementuj purchase counter
ADict.Achievements.increment("total_purchases")
}
📈 Postęp i streakiProgress and Streaks
increment(id: String, amount: Int = 1): Boolean
Inkrementuj postęp (dla PROGRESSIVE).Increment progress (for PROGRESSIVE).
ZwracaReturns: true jeśli osiągnięcie zostało odblokowane
setProgress(id: String, count: Int): Boolean
Ustaw postęp bezpośrednio.Set progress directly.
recordStreakDay(id: String): Boolean
Zapisz dzień streaka (dla STREAK).Record streak day (for STREAK).
resetStreak(id: String)
Zresetuj streak.Reset streak.
// Progressive - inkrementacja
fun onArticleRead(articleId: String) {
val unlocked = ADict.Achievements.increment("read_100_articles")
if (unlocked) {
showCongratulations("Przeczytałeś 100 artykułów!")
}
// Możesz też dodać więcej niż 1
ADict.Achievements.increment("total_words_read", article.wordCount)
}
// Streak - zapisz dzień
fun onAppOpened() {
val unlocked = ADict.Achievements.recordStreakDay("streak_7")
if (unlocked) {
showCongratulations("7 dni z rzędu!")
}
// Sprawdź aktualny streak
val state = ADict.Achievements.get("streak_7")?.state
Log.d("Achievements", "Current streak: ${state?.streakDays}")
}
// Ustaw postęp bezpośrednio (np. z serwera)
fun syncProgressFromServer(serverProgress: Int) {
ADict.Achievements.setProgress("read_100_articles", serverProgress)
}
🔍 Pobieranie osiągnięćFetching Achievements
getAll(includeHidden: Boolean = false): List<Achievement>
Pobierz wszystkie osiągnięciaof the achievement.
getUnlocked(): List<Achievement>
Pobierz odblokowane osiągnięciaof the achievement.
getLocked(includeHidden: Boolean = false): List<Achievement>
Pobierz zablokowane osiągnięciaof the achievement.
getByCategory(category: String): List<Achievement>
Pobierz osiągnięciaof the achievement w kategorii.
get(id: String): Achievement?
Pobierz pojedyncze osiągnięcie.Get a single achievement.
isUnlocked(id: String): Boolean
Sprawdź czy osiągnięcie jest odblokowane.Check if achievement is unlocked.
getProgress(id: String): Float
Pobierz postęp (0.0 - 1.0).Get progress (0.0 - 1.0).
getTotalPoints(): Int
Pobierz sumę punktów za odblokowane osiągnięciaof the achievement.
getStats(): Stats
Pobierz statystyki osiągnięć.Get achievement statistics.
// Lista wszystkich osiągnięć
val allAchievements = ADict.Achievements.getAll()
allAchievements.forEach { achievement ->
println("${achievement.title}: ${if (achievement.isUnlocked) "✅" else "🔒"}")
println(" Progress: ${(achievement.progress * 100).toInt()}%")
println(" Points: ${achievement.points}")
}
// Odblokowane
val unlockedCount = ADict.Achievements.getUnlocked().size
// Statystyki
val stats = ADict.Achievements.getStats()
println("Odblokowane: ${stats.unlocked}/${stats.total}")
println("Punkty: ${stats.totalPoints}")
println("Completion: ${(stats.completionRate * 100).toInt()}%")
// W RecyclerView
class AchievementsAdapter : RecyclerView.Adapter<...>() {
private var achievements = listOf()
fun setAchievements(list: List) {
achievements = list.sortedWith(
compareByDescending { it.isUnlocked }
.thenByDescending { it.definition.tier }
)
notifyDataSetChanged()
}
}
// Obserwacja zmian (Flow)
lifecycleScope.launch {
ADict.Achievements.achievements.collect { achievementsMap ->
updateUI(achievementsMap)
}
}
🔔 CallbackiCallbacks
onUnlock(callback: (Achievement) -> Unit)
Callback wywoływany przy odblokowaniu osiągnięciaof the achievement.
// Globalny callback
ADict.Achievements.onUnlock { achievement ->
// Pokaż UI
showAchievementToast(achievement)
// Analytics
ADict.Analytics.log("achievement_unlocked", mapOf(
"achievement_id" to achievement.id,
"achievement_name" to achievement.title,
"points" to achievement.points,
"tier" to achievement.definition.tier
))
// Może nagroda?
if (achievement.definition.tier >= 3) {
grantBonusCoins(achievement.points)
}
}
fun showAchievementToast(achievement: Achievement) {
Toast.makeText(
context,
"🏆 ${achievement.title} (+${achievement.points} pts)",
Toast.LENGTH_LONG
).show()
}
💡 PrzykładyExamples praktycznepractical
Pełna konfiguracjaFull Configuration dla gry
class AchievementsSetup {
fun setup() {
// === ONBOARDING ===
ADict.Achievements.define("first_level") {
title = "Początek przygody"
description = "Ukończ pierwszy poziom"
points = 10
type = AchievementType.ONE_TIME
category = "onboarding"
tier = 1
}
ADict.Achievements.define("tutorial_complete") {
title = "Gotowy do gry"
description = "Ukończ tutorial"
points = 25
type = AchievementType.ONE_TIME
category = "onboarding"
tier = 1
}
// === POSTĘP ===
ADict.Achievements.define("levels_10") {
title = "Podróżnik"
description = "Ukończ 10 poziomów"
points = 100
type = AchievementType.PROGRESSIVE
targetCount = 10
category = "progress"
tier = 2
}
ADict.Achievements.define("levels_50") {
title = "Weteran"
description = "Ukończ 50 poziomów"
points = 500
type = AchievementType.PROGRESSIVE
targetCount = 50
category = "progress"
tier = 3
prerequisiteIds = listOf("levels_10")
}
ADict.Achievements.define("levels_100") {
title = "Legenda"
description = "Ukończ 100 poziomów"
points = 1000
type = AchievementType.PROGRESSIVE
targetCount = 100
category = "progress"
tier = 4
prerequisiteIds = listOf("levels_50")
}
// === STREAK ===
ADict.Achievements.define("daily_3") {
title = "Regularny gracz"
description = "Graj 3 dni z rzędu"
points = 50
type = AchievementType.STREAK
targetCount = 3
category = "engagement"
tier = 1
}
ADict.Achievements.define("daily_7") {
title = "Oddany gracz"
description = "Graj 7 dni z rzędu"
points = 150
type = AchievementType.STREAK
targetCount = 7
category = "engagement"
tier = 2
}
ADict.Achievements.define("daily_30") {
title = "Uzależniony"
description = "Graj 30 dni z rzędu"
points = 500
type = AchievementType.STREAK
targetCount = 30
category = "engagement"
tier = 4
}
// === KOLEKCJONOWANIE ===
ADict.Achievements.define("collect_all_characters") {
title = "Kolekcjoner"
description = "Odblokuj wszystkie postacie"
points = 300
type = AchievementType.ONE_TIME
category = "collection"
tier = 3
hidden = true
}
// === SOCIAL ===
ADict.Achievements.define("first_share") {
title = "Społecznik"
description = "Udostępnij wynik znajomym"
points = 30
type = AchievementType.ONE_TIME
category = "social"
tier = 1
}
// === SKILL ===
ADict.Achievements.define("perfect_level") {
title = "Perfekcja"
description = "Ukończ poziom z 3 gwiazdkami"
points = 50
type = AchievementType.ONE_TIME
category = "skill"
tier = 2
}
ADict.Achievements.define("no_damage") {
title = "Nietykalny"
description = "Ukończ poziom bez obrażeń"
points = 100
type = AchievementType.ONE_TIME
category = "skill"
tier = 3
hidden = true
}
// Callback
ADict.Achievements.onUnlock { achievement ->
GameManager.showAchievementPopup(achievement)
GameManager.addCoins(achievement.points)
}
}
}
// Użycie w grze
class GameManager {
fun onLevelComplete(level: Level, stars: Int, damage: Int) {
// Podstawowe
ADict.Achievements.unlock("first_level")
ADict.Achievements.increment("levels_10")
ADict.Achievements.increment("levels_50")
ADict.Achievements.increment("levels_100")
// Skill-based
if (stars == 3) {
ADict.Achievements.unlock("perfect_level")
}
if (damage == 0) {
ADict.Achievements.unlock("no_damage")
}
}
fun onDailyLogin() {
ADict.Achievements.recordStreakDay("daily_3")
ADict.Achievements.recordStreakDay("daily_7")
ADict.Achievements.recordStreakDay("daily_30")
}
fun onShare() {
ADict.Achievements.unlock("first_share")
}
fun onAllCharactersUnlocked() {
ADict.Achievements.unlock("collect_all_characters")
}
}
📚 Klasy danychData Classes
data class AchievementDefinition
- idString
- titleString
- descriptionString
- iconInt?
- pointsInt
- typeAchievementType
- targetCountInt
- hiddenBoolean
- expiresAtLong?
- availableFromLong?
- categoryString?
- tierInt
- prerequisiteIdsList<String>
data class AchievementState
- idString
- unlockedBoolean
- unlockedAtLong?
- currentCountInt
- streakDaysInt
- lastStreakDateString?
- progressFloat
data class Stats
- totalIntŁączna liczba osiągnięćTotal number of achievements
- unlockedIntLiczba odblokowanychNumber of unlocked
- totalPointsIntSuma punktówTotal points
- completionRateFloatProcent ukończenia (0-1)Completion percentage (0-1)