You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
4.0 KiB
136 lines
4.0 KiB
package org.jetbrains.compose.demo.falling |
|
|
|
import androidx.compose.desktop.ui.tooling.preview.Preview |
|
import androidx.compose.foundation.layout.* |
|
import androidx.compose.material.Button |
|
import androidx.compose.material.Slider |
|
import androidx.compose.material.Text |
|
import androidx.compose.runtime.* |
|
import androidx.compose.ui.Modifier |
|
import androidx.compose.ui.graphics.Color |
|
import androidx.compose.ui.layout.onSizeChanged |
|
import androidx.compose.ui.platform.LocalDensity |
|
import androidx.compose.ui.unit.* |
|
import kotlin.random.Random |
|
|
|
class Game { |
|
private var previousTimeNanos: Long = Long.MAX_VALUE |
|
private val colors = arrayOf( |
|
Color.Red, Color.Blue, Color.Cyan, |
|
Color.Magenta, Color.Yellow, Color.Black |
|
) |
|
private var startTime = 0L |
|
|
|
var size by mutableStateOf(Pair(0.dp, 0.dp)) |
|
|
|
var pieces = mutableStateListOf<PieceData>() |
|
private set |
|
|
|
var elapsed by mutableStateOf(0L) |
|
var score by mutableStateOf(0) |
|
private var clicked by mutableStateOf(0) |
|
|
|
var started by mutableStateOf(false) |
|
var paused by mutableStateOf(false) |
|
var finished by mutableStateOf(false) |
|
|
|
var numBlocks by mutableStateOf(5) |
|
|
|
fun start() { |
|
previousTimeNanos = System.nanoTime() |
|
startTime = previousTimeNanos |
|
clicked = 0 |
|
started = true |
|
finished = false |
|
paused = false |
|
pieces.clear() |
|
repeat(numBlocks) { index -> |
|
pieces.add(PieceData(this, index * 1.5f + 5f, colors[index % colors.size]).also { piece -> |
|
piece.position = Random.nextDouble(0.0, 100.0).toFloat() |
|
}) |
|
} |
|
} |
|
|
|
fun togglePause() { |
|
paused = !paused |
|
previousTimeNanos = System.nanoTime() |
|
} |
|
|
|
fun update(nanos: Long) { |
|
val dt = (nanos - previousTimeNanos).coerceAtLeast(0) |
|
previousTimeNanos = nanos |
|
elapsed = nanos - startTime |
|
pieces.forEach { it.update(dt) } |
|
} |
|
|
|
fun clicked(piece: PieceData) { |
|
score += piece.velocity.toInt() |
|
clicked++ |
|
if (clicked == numBlocks) { |
|
finished = true |
|
} |
|
} |
|
} |
|
|
|
@Composable |
|
@Preview |
|
fun FallingBallsGame() { |
|
val game = remember { Game() } |
|
val density = LocalDensity.current |
|
Column { |
|
Text( |
|
"Catch balls!${if (game.finished) " Game over!" else ""}", |
|
fontSize = 50.sp, |
|
color = Color(218, 120, 91) |
|
) |
|
Text("Score ${game.score} Time ${game.elapsed / 1_000_000} Blocks ${game.numBlocks}", fontSize = 35.sp) |
|
Row { |
|
if (!game.started) { |
|
Slider( |
|
value = game.numBlocks / 20f, |
|
onValueChange = { game.numBlocks = (it * 20f).toInt().coerceAtLeast(1) }, |
|
modifier = Modifier.width(100.dp) |
|
) |
|
} |
|
Button(onClick = { |
|
game.started = !game.started |
|
if (game.started) { |
|
game.start() |
|
} |
|
}) { |
|
Text(if (game.started) "Stop" else "Start", fontSize = 40.sp) |
|
} |
|
if (game.started) { |
|
Spacer(Modifier.padding(5.dp)) |
|
Button(onClick = { |
|
game.togglePause() |
|
}) { |
|
Text(if (game.paused) "Resume" else "Pause", fontSize = 40.sp) |
|
} |
|
} |
|
} |
|
if (game.started) { |
|
Box(modifier = Modifier |
|
.fillMaxWidth() |
|
.fillMaxHeight() |
|
.onSizeChanged { |
|
with(density) { |
|
game.size = it.width.toDp() to it.height.toDp() |
|
} |
|
} |
|
) { |
|
game.pieces.forEachIndexed { index, piece -> Piece(index, piece) } |
|
} |
|
} |
|
|
|
LaunchedEffect(Unit) { |
|
while (true) { |
|
withFrameNanos { |
|
if (game.started && !game.paused && !game.finished) |
|
game.update(it) |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|