forked from george/snake-compose
Этап 3: сохранение рекорда через DataStore
Рекорд теперь сохраняется между запусками приложения. - ScoreStore.kt — обёртка над Preferences DataStore (Flow на чтение, suspend saveBest на запись; работа с диском вне UI-потока) - загрузка рекорда при старте экрана через LaunchedEffect - сохранение нового рекорда из step() через scope.launch - зависимость androidx.datastore:datastore-preferences:1.1.1 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6a1719c148
commit
d8efaa53a6
@ -74,6 +74,9 @@ app/src/main/
|
|||||||
- **`snake-stage-2`** — ускорение: змейка ускоряется с каждой съеденной едой
|
- **`snake-stage-2`** — ускорение: змейка ускоряется с каждой съеденной едой
|
||||||
(задержка шага 150 → 60 мс), на экране индикатор «Скорость». Смотри
|
(задержка шага 150 → 60 мс), на экране индикатор «Скорость». Смотри
|
||||||
функции `stepDelay()` / `speedLevel()` и игровой цикл в `SnakeGame.kt`.
|
функции `stepDelay()` / `speedLevel()` и игровой цикл в `SnakeGame.kt`.
|
||||||
|
- **`snake-stage-3`** — рекорд сохраняется между запусками через **Jetpack
|
||||||
|
DataStore** (`ScoreStore.kt`): чтение — `Flow`, запись — suspend-функция,
|
||||||
|
диск не блокирует UI. Смотри `LaunchedEffect` для загрузки рекорда и
|
||||||
|
`scope.launch { ScoreStore.saveBest(...) }` при новом рекорде.
|
||||||
|
|
||||||
Идеи для следующих этапов: сохранение рекорда через `DataStore`,
|
Идеи для следующих этапов: экранные кнопки-стрелки, анимации, звук, паузу.
|
||||||
экранные кнопки-стрелки, анимации, звук.
|
|
||||||
|
|||||||
@ -42,4 +42,6 @@ dependencies {
|
|||||||
implementation("androidx.compose.ui:ui")
|
implementation("androidx.compose.ui:ui")
|
||||||
implementation("androidx.compose.foundation:foundation")
|
implementation("androidx.compose.foundation:foundation")
|
||||||
implementation("androidx.compose.material3:material3")
|
implementation("androidx.compose.material3:material3")
|
||||||
|
// Этап 3: сохранение рекорда между запусками
|
||||||
|
implementation("androidx.datastore:datastore-preferences:1.1.1")
|
||||||
}
|
}
|
||||||
|
|||||||
34
app/src/main/java/com/example/snake/ScoreStore.kt
Normal file
34
app/src/main/java/com/example/snake/ScoreStore.kt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package com.example.snake
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import androidx.datastore.preferences.core.edit
|
||||||
|
import androidx.datastore.preferences.core.intPreferencesKey
|
||||||
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
// Один DataStore на всё приложение (файл snake_prefs.preferences_pb во
|
||||||
|
// внутреннем хранилище). Расширение Context — стандартный приём для DataStore.
|
||||||
|
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "snake_prefs")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Хранилище рекорда между запусками приложения.
|
||||||
|
*
|
||||||
|
* DataStore работает асинхронно (на корутинах): чтение — это [Flow],
|
||||||
|
* запись — suspend-функция. Так UI никогда не блокируется обращением к диску.
|
||||||
|
*/
|
||||||
|
object ScoreStore {
|
||||||
|
|
||||||
|
private val BEST_KEY = intPreferencesKey("best_score")
|
||||||
|
|
||||||
|
/** Поток рекорда. Если ещё ничего не сохраняли — отдаёт 0. */
|
||||||
|
fun bestFlow(context: Context): Flow<Int> =
|
||||||
|
context.dataStore.data.map { prefs -> prefs[BEST_KEY] ?: 0 }
|
||||||
|
|
||||||
|
/** Сохранить новый рекорд. */
|
||||||
|
suspend fun saveBest(context: Context, value: Int) {
|
||||||
|
context.dataStore.edit { prefs -> prefs[BEST_KEY] = value }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,7 +14,9 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.geometry.CornerRadius
|
import androidx.compose.ui.geometry.CornerRadius
|
||||||
@ -28,6 +30,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@ -73,6 +77,15 @@ fun SnakeGame() {
|
|||||||
var started by remember { mutableStateOf(false) }
|
var started by remember { mutableStateOf(false) }
|
||||||
var gameOver by remember { mutableStateOf(false) }
|
var gameOver by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
// Этап 3: контекст и scope для работы с DataStore (рекорд хранится на диске)
|
||||||
|
val context = LocalContext.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
// Подгружаем сохранённый рекорд один раз при запуске экрана
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
best = ScoreStore.bestFlow(context).first()
|
||||||
|
}
|
||||||
|
|
||||||
fun reset() {
|
fun reset() {
|
||||||
snake = startSnake
|
snake = startSnake
|
||||||
dir = Cell(0, -1)
|
dir = Cell(0, -1)
|
||||||
@ -99,7 +112,11 @@ fun SnakeGame() {
|
|||||||
snake = if (grew) listOf(nh) + snake else listOf(nh) + snake.dropLast(1)
|
snake = if (grew) listOf(nh) + snake else listOf(nh) + snake.dropLast(1)
|
||||||
if (grew) {
|
if (grew) {
|
||||||
score++
|
score++
|
||||||
if (score > best) best = score
|
if (score > best) {
|
||||||
|
best = score
|
||||||
|
// новый рекорд — сохраняем на диск (асинхронно, UI не блокируется)
|
||||||
|
scope.launch { ScoreStore.saveBest(context, best) }
|
||||||
|
}
|
||||||
food = randomFreeCell(snake)
|
food = randomFreeCell(snake)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user