orangy
Update(dt)
function frequently passing time passed since last call, which would update game mutable state and then I want UI to only touch things that actually depend on the change. I’ve searched the net and only found some clocks and Dino game, but they are just animating things directly, not doing this intermediate game state thing. Am I missing something? How I’d go with it?altavir
12/07/2020, 8:09 AMmutableState
.olonho
12/07/2020, 8:19 AMfun main() {
val state = mutableStateOf(0)
GlobalScope.launch {
while (true) {
delay(100)
state.value++
}
}
Window {
Text(state.value.toString())
}
}
Igor Demin
12/07/2020, 8:51 AMhas its own animation/game loopIt is better to use it instead of your own if we want precise animations. Using
LaunchedEffect
and `withFrameNanos`:
import androidx.compose.desktop.Window
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.dispatch.withFrameNanos
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
fun main() = Window {
val game = remember { Game() }
LaunchedEffect(Unit) {
while (true) {
withFrameNanos {
game.update(it)
}
}
}
Box(Modifier.offset(game.position.dp)) {
Box(Modifier.size(20.dp).background(Color.Red))
}
}
class Game {
private val velocityPixelsPerSecond = 100f
private var previousTimeNanos: Long = Long.MAX_VALUE
var position by mutableStateOf(0f)
private set
fun update(nanos: Long) {
val dt = (nanos - previousTimeNanos).coerceAtLeast(0)
previousTimeNanos = nanos
position += (dt / 1E9 * velocityPixelsPerSecond).toFloat()
}
}
orangy
olonho
12/07/2020, 9:25 AMimport androidx.compose.desktop.Window
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.dispatch.withFrameNanos
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
fun main() {
Window {
Column {
val game = remember { Game() }
val effect = remember { mutableStateOf(false) }
val delay = remember { mutableStateOf(10) }
Text("Now using ${if (effect.value) "Effect" else "Timer"}")
Button(onClick = { effect.value = !effect.value }) {
Text("Toggle to ${if (effect.value) "Timer" else "Effect"}")
}
TextField(value = delay.value.toString(), onValueChange = {
newValue -> delay.value = newValue.toIntOrNull() ?: 10
})
if (effect.value) {
LaunchedEffect(Unit) {
while (effect.value) {
withFrameNanos {
game.update(it)
}
}
}
} else {
remember {
GlobalScope.launch(Dispatchers.Main) {
while (!effect.value) {
game.update(System.nanoTime())
delay(delay.value.toLong())
}
}
}
}
Box(Modifier.offset(game.position.dp)) {
Box(Modifier.size(20.dp).background(Color.Red))
}
}
}
}
class Game {
private val velocityPixelsPerSecond = 100f
private var previousTimeNanos: Long = Long.MAX_VALUE
var position by mutableStateOf(0f)
private set
fun update(nanos: Long) {
val dt = (nanos - previousTimeNanos).coerceAtLeast(0)
previousTimeNanos = nanos
position += (dt / 1E9 * velocityPixelsPerSecond).toFloat()
if (position > 500) position = 0f
}
}
orangy
Igor Demin
12/07/2020, 10:24 AMColton Idle
12/07/2020, 12:25 PMorangy
Michael Paus
12/07/2020, 3:45 PMsuresh
12/08/2020, 7:31 AMGlobalScope.launch(Dispatchers.Main)
, wouldn't this launch coroutines for every frame if the effect is false? Sorry if my understanding is wrong, still learning the compose.olonho
12/08/2020, 7:35 AMremember