Game Over screen

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() }

        }

    )

}

}

I think this is where the problem is:

.filter { s

val reach = s.size + lineLength

!(s.x + reach < 0f ||

        s.x - reach > w ||

        s.y + reach < 0f ||

        s.y - reach > h)

}

What did I do wrong? It’s supposed to turn the value of slats to null when the slats exit the screen!

Firstly, it’d really help if you could format your code properly!!

Secondly, if you think the filter is not working, you could add some println statements to debug what’s going on. For example, in your filter, put the result of the expression into a variable, and then print that variable.

val onScreen = <condition goes here>

println("Slat $it is on screen: $onScreen")

onScreen

Also before the check for the collection being empty, put a println:

println("Slats empty: ${slats.isEmpty()}")

I checked already. It never prints.

Is there a code option on the style menu???

Edit: Oh, I see. It says preformatted text.

The way I do it is I just use triple backticks. For example:
```kotlin
code goes here
```

Is there a built in way to check if something is visible (on the screen)

I don’t know, sorry.

I would highly recommend debugging your code and stepping through it. If the println statements are never printing, then you should debug your code to see which parts are actually executing.