Hi! I’m working on some code for a game called “Slats”. Most of the code works, you get a start page and you can play the game, but when the slats disappear it’s supposed to have a game over page which never shows up. I’ve tried everything, what do I do?
//SlatsSlatsSlats
package com.example.slatsslatsslats
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay
import kotlin.math.*
data class Slat(val x: Float, val y: Float, var size: Float = 0f, val growthSpeed: Float)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { SlatsGame() }
}
}
@Composable
fun SlatsGame() {
var screen by remember { mutableStateOf("start") }
var score by remember { mutableStateOf(0) }
var slats by remember { mutableStateOf(listOf<Slat>()) }
val lineLength = 40f
val hitDistance = 50f
BoxWithConstraints(modifier = Modifier.fillMaxSize().background(Color.Black)) {
val w = constraints.maxWidth.toFloat()
val h = constraints.maxHeight.toFloat()
// Physics Engine
LaunchedEffect(screen) {
if (screen == "game") {
while (screen == "game") {
val nextSlats = slats.map { it.copy(size = it.size + it.growthSpeed) }
.filter { s ->
val maxReach = s.size + lineLength
(s.x + maxReach > 0 && s.x - maxReach < w && s.y + maxReach > 0 && s.y - maxReach < h)
}
slats = nextSlats
// Force the check within the loop
if (slats.isEmpty()) {
screen = "gameover"
}
delay(16)
}
}
}
when (screen) {
"start" -> StartScreen {
score = 0
slats = listOf(Slat(w / 2, h / 2, 0f, 5f))
screen = "game"
}
"game" -> {
Canvas(modifier = Modifier.fillMaxSize().pointerInput(Unit) {
detectTapGestures { offset ->
val hit = slats.any { s ->
val directions = listOf(1f to -1f, -1f to -1f, 1f to 1f, -1f to 1f, 1f to 0f, -1f to 0f, 0f to 1f, 0f to -1f)
directions.any { (dx, dy) ->
val start = Offset(s.x + dx \* s.size, s.y + dy \* s.size)
val end = Offset(s.x + dx \* (s.size + lineLength), s.y + dy \* (s.size + lineLength))
distToSegment(offset, start, end) < hitDistance
}
}
if (hit) {
val speed = 5f + (5f \* (1f - exp(-score \* 0.06f)))
slats = slats + Slat(offset.x, offset.y, 0f, speed)
score++
}
}
}) {
slats.forEach { s ->
val directions = listOf(1f to -1f, -1f to -1f, 1f to 1f, -1f to 1f, 1f to 0f, -1f to 0f, 0f to 1f, 0f to -1f)
directions.forEach { (dx, dy) ->
drawLine(Color.White, Offset(s.x + dx \* s.size, s.y + dy \* s.size),
Offset(s.x + dx \* (s.size + lineLength), s.y + dy \* (s.size + lineLength)), strokeWidth = 3f)
}
}
}
Text("Points: $score", color = Color.White, fontSize = 20.sp, modifier = Modifier.padding(16.dp))
}
"gameover" -> GameOverScreen(score) { screen = "start" }
}
}
}
fun distToSegment(p: Offset, v: Offset, w: Offset): Float {
val l2 = (v.x - w.x).pow(2) + (v.y - w.y).pow(2)
if (l2 == 0f) return (p - v).getDistance()
var t = ((p.x - v.x) \* (w.x - v.x) + (p.y - v.y) \* (w.y - v.y)) / l2
t = max(0f, min(1f, t))
return (p - Offset(v.x + t \* (w.x - v.x), v.y + t \* (w.y - v.y))).getDistance()
}
@Composable
fun StartScreen(onTap: () → Unit) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Slats", color = Color.White, fontSize = 48.sp)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Tap to Start",
color = Color.White,
fontSize = 20.sp,
modifier = Modifier.pointerInput(Unit) {
detectTapGestures { onTap() }
}
)
}
}
@Composable
fun GameOverScreen(score: Int, onRestart: () → Unit) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("GAME OVER", color = Color.White, fontSize = 48.sp)
Text("Total Score: $score", color = Color.White, fontSize = 24.sp)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Tap to Restart",
color = Color.White,
fontSize = 20.sp,
modifier = Modifier.pointerInput(Unit) {
detectTapGestures { onRestart() }
}
)
}
}