Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Compose ❤️ Dino: Building Chrome’s T-Rex Game in Jetpack Compose - Kotlin Mumbai

Wajahat Karim
September 26, 2020

Compose ❤️ Dino: Building Chrome’s T-Rex Game in Jetpack Compose - Kotlin Mumbai

NOTE: This slide contains animated GIFs which are not supported SpeakerDeck. You can watch the original animated version at http://slides.com/wajahatkarim/trex

Although Jetpack Compose is a toolkit to create Android UI in a declarative form with the power of Kotlin language. But it can be used as a canvas for generative art, animations, or even games.
In this session, we'll take a look at the capabilities of Canvas API for Jetpack Compose and how can we implement Chrome's T-Rex Dino game. We'll discuss some challenges such as game loop, game state management, infinite parallax scrolling etc.

By the end of this talk, you'll be more familiar with the concepts of Canvas in Jetpack Compose and how you can use it for games.

💻 Code available at: https://github.com/wajahatkarim3/DinoCompose

Wajahat Karim

September 26, 2020
Tweet

More Decks by Wajahat Karim

Other Decks in Education

Transcript

  1. Compose ❤ Dino
    Building Chrome’s T-Rex Game in Jetpack
    Compose
    Wajahat Karim
    wajahatkarim.com
    WajahatKarim
    Google Dev Expert (GDE) in Android .
    Android Dev. Open Source Contributor .
    Technical Writer . Public Speaker
    1

    View full-size slide

  2. Compose ❤ Dino
    Building Chrome’s T-Rex Game in Jetpack
    Compose
    2

    View full-size slide

  3. Jetpack Compose
    “ a modern UI toolkit which simplifies and
    accelerates UI development on Android with
    less code, powerful tools, and intuitive Kotlin
    APIs.
    d.android.com/jetpack/compose
    3

    View full-size slide

  4. But... wait?
    “ a modern UI toolkit which simplifies and
    accelerates UI development on Android with
    less code, powerful tools, and intuitive Kotlin
    APIs.
    4

    View full-size slide

  5. Why Games? Why not UI?
    Games are more fun.
    Canvas is more friendly in Compose.
    Will help in making custom views
    Perfect for animations.
    https://github.com/alexjlockwood/bees-and-bombs-compose/
    5

    View full-size slide

  6. Game Development 101
    Games have different structure than software/apps
    Startup
    Intro Movie
    Main Menu & Settings
    Loading
    Main Game
    Intro
    Gameplay
    Pause Options
    Game Over / Outro
    End Game / Levels
    Credits
    6

    View full-size slide

  7. Game Development 101
    Game Loop
    // A Simple Game Loop
    int main()
    {
    initialize();
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    shutdown();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    7

    View full-size slide

  8. Game Development 101
    Game Loop
    // A Simple Game Loop
    int main()
    {
    initialize();
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    shutdown();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    initialize();
    shutdown();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    updateGameData();
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    12
    }
    13
    7

    View full-size slide

  9. Game Development 101
    Game Loop
    // A Simple Game Loop
    int main()
    {
    initialize();
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    shutdown();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    initialize();
    shutdown();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    updateGameData();
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    12
    }
    13
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    5
    6
    7
    8
    9
    10
    11
    shutdown();
    12
    }
    13
    7

    View full-size slide

  10. Game Development 101
    Game Loop
    // A Simple Game Loop
    int main()
    {
    initialize();
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    shutdown();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    initialize();
    shutdown();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    updateGameData();
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    12
    }
    13
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    5
    6
    7
    8
    9
    10
    11
    shutdown();
    12
    }
    13
    processInputs();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    while(true)
    5
    {
    6
    7
    updateGameData();
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    shutdown();
    12
    }
    13
    7

    View full-size slide

  11. Game Development 101
    Game Loop
    // A Simple Game Loop
    int main()
    {
    initialize();
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    shutdown();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    initialize();
    shutdown();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    updateGameData();
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    12
    }
    13
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    5
    6
    7
    8
    9
    10
    11
    shutdown();
    12
    }
    13
    processInputs();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    while(true)
    5
    {
    6
    7
    updateGameData();
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    shutdown();
    12
    }
    13
    updateGameData();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    shutdown();
    12
    }
    13
    7

    View full-size slide

  12. Game Development 101
    Game Loop
    // A Simple Game Loop
    int main()
    {
    initialize();
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    shutdown();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    initialize();
    shutdown();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    updateGameData();
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    12
    }
    13
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    5
    6
    7
    8
    9
    10
    11
    shutdown();
    12
    }
    13
    processInputs();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    while(true)
    5
    {
    6
    7
    updateGameData();
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    shutdown();
    12
    }
    13
    updateGameData();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    shutdown();
    12
    }
    13
    renderGame();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    updateGameData();
    8
    9
    checkShutdown();
    10
    }
    11
    shutdown();
    12
    }
    13
    7

    View full-size slide

  13. Game Development 101
    Game Loop
    // A Simple Game Loop
    int main()
    {
    initialize();
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    shutdown();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    initialize();
    shutdown();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    updateGameData();
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    12
    }
    13
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    5
    6
    7
    8
    9
    10
    11
    shutdown();
    12
    }
    13
    processInputs();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    while(true)
    5
    {
    6
    7
    updateGameData();
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    shutdown();
    12
    }
    13
    updateGameData();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    shutdown();
    12
    }
    13
    renderGame();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    updateGameData();
    8
    9
    checkShutdown();
    10
    }
    11
    shutdown();
    12
    }
    13
    checkShutdown();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    updateGameData();
    8
    renderGame();
    9
    10
    }
    11
    shutdown();
    12
    }
    13
    7

    View full-size slide

  14. Game Development 101
    Game Loop
    // A Simple Game Loop
    int main()
    {
    initialize();
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    shutdown();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    initialize();
    shutdown();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    updateGameData();
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    12
    }
    13
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    5
    6
    7
    8
    9
    10
    11
    shutdown();
    12
    }
    13
    processInputs();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    while(true)
    5
    {
    6
    7
    updateGameData();
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    shutdown();
    12
    }
    13
    updateGameData();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    8
    renderGame();
    9
    checkShutdown();
    10
    }
    11
    shutdown();
    12
    }
    13
    renderGame();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    updateGameData();
    8
    9
    checkShutdown();
    10
    }
    11
    shutdown();
    12
    }
    13
    checkShutdown();
    // A Simple Game Loop
    1
    int main()
    2
    {
    3
    initialize();
    4
    while(true)
    5
    {
    6
    processInputs();
    7
    updateGameData();
    8
    renderGame();
    9
    10
    }
    11
    shutdown();
    12
    }
    13
    // A Simple Game Loop
    int main()
    {
    initialize();
    while(true)
    {
    processInputs();
    updateGameData();
    renderGame();
    checkShutdown();
    }
    shutdown();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    7

    View full-size slide

  15. Compose Canvas 101
    A simple Spacer() to allow you to draw anything on it
    @Composable
    fun Canvas
    (
    modifier: Modifier,
    onDraw: DrawScope.() -> Unit
    ) = Spacer(modifier.drawBehind(onDraw))
    1
    2
    3
    4
    5
    6
    8

    View full-size slide

  16. Compose Canvas 101
    A simple Spacer() to allow you to draw anything on it
    @Composable
    fun Canvas
    (
    modifier: Modifier,
    onDraw: DrawScope.() -> Unit
    ) = Spacer(modifier.drawBehind(onDraw))
    1
    2
    3
    4
    5
    6 ) = Spacer(modifier.drawBehind(onDraw))
    @Composable
    1
    fun Canvas
    2
    (
    3
    modifier: Modifier,
    4
    onDraw: DrawScope.() -> Unit
    5
    6
    8

    View full-size slide

  17. Compose Canvas 101
    A simple Spacer() to allow you to draw anything on it
    @Composable
    fun Canvas
    (
    modifier: Modifier,
    onDraw: DrawScope.() -> Unit
    ) = Spacer(modifier.drawBehind(onDraw))
    1
    2
    3
    4
    5
    6 ) = Spacer(modifier.drawBehind(onDraw))
    @Composable
    1
    fun Canvas
    2
    (
    3
    modifier: Modifier,
    4
    onDraw: DrawScope.() -> Unit
    5
    6
    modifier: Modifier,
    @Composable
    1
    fun Canvas
    2
    (
    3
    4
    onDraw: DrawScope.() -> Unit
    5
    ) = Spacer(modifier.drawBehind(onDraw))
    6
    8

    View full-size slide

  18. Compose Canvas 101
    A simple Spacer() to allow you to draw anything on it
    @Composable
    fun Canvas
    (
    modifier: Modifier,
    onDraw: DrawScope.() -> Unit
    ) = Spacer(modifier.drawBehind(onDraw))
    1
    2
    3
    4
    5
    6 ) = Spacer(modifier.drawBehind(onDraw))
    @Composable
    1
    fun Canvas
    2
    (
    3
    modifier: Modifier,
    4
    onDraw: DrawScope.() -> Unit
    5
    6
    modifier: Modifier,
    @Composable
    1
    fun Canvas
    2
    (
    3
    4
    onDraw: DrawScope.() -> Unit
    5
    ) = Spacer(modifier.drawBehind(onDraw))
    6
    onDraw: DrawScope.() -> Unit
    @Composable
    1
    fun Canvas
    2
    (
    3
    modifier: Modifier,
    4
    5
    ) = Spacer(modifier.drawBehind(onDraw))
    6
    8

    View full-size slide

  19. Compose Canvas 101
    A simple Spacer() to allow you to draw anything on it
    @Composable
    fun Canvas
    (
    modifier: Modifier,
    onDraw: DrawScope.() -> Unit
    ) = Spacer(modifier.drawBehind(onDraw))
    1
    2
    3
    4
    5
    6 ) = Spacer(modifier.drawBehind(onDraw))
    @Composable
    1
    fun Canvas
    2
    (
    3
    modifier: Modifier,
    4
    onDraw: DrawScope.() -> Unit
    5
    6
    modifier: Modifier,
    @Composable
    1
    fun Canvas
    2
    (
    3
    4
    onDraw: DrawScope.() -> Unit
    5
    ) = Spacer(modifier.drawBehind(onDraw))
    6
    onDraw: DrawScope.() -> Unit
    @Composable
    1
    fun Canvas
    2
    (
    3
    modifier: Modifier,
    4
    5
    ) = Spacer(modifier.drawBehind(onDraw))
    6
    @Composable
    fun Canvas
    (
    modifier: Modifier,
    onDraw: DrawScope.() -> Unit
    ) = Spacer(modifier.drawBehind(onDraw))
    1
    2
    3
    4
    5
    6
    8

    View full-size slide

  20. Compose Canvas 101
    DrawScope - Handles the drawing API
    drawRect() drawOval() drawLine() drawImage()
    drawRoundRect() drawCircle() drawArc() drawPath()
    fun DrawScope.drawMyShape() { }
    9

    View full-size slide

  21. Compose Canvas 101
    Custom View Example in Canvas
    Canvas(modifier = Modifier.fillMaxSize()) {
    drawCircle(
    color = Color.Red,
    radius = 300f
    )
    drawCircle(
    color = Color.Green,
    radius = 200f
    )
    drawCircle(
    color = Color.Blue,
    radius = 100f
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    10

    View full-size slide

  22. Compose Canvas 101
    Custom View Example in Canvas
    Canvas(modifier = Modifier.fillMaxSize()) {
    drawCircle(
    color = Color.Red,
    radius = 300f
    )
    drawCircle(
    color = Color.Green,
    radius = 200f
    )
    drawCircle(
    color = Color.Blue,
    radius = 100f
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    Canvas(modifier = Modifier.fillMaxSize()) {
    1
    drawCircle(
    2
    color = Color.Red,
    3
    radius = 300f
    4
    )
    5
    6
    drawCircle(
    7
    color = Color.Green,
    8
    radius = 200f
    9
    )
    10
    11
    drawCircle(
    12
    color = Color.Blue,
    13
    radius = 100f
    14
    )
    15
    }
    16
    10

    View full-size slide

  23. Compose Canvas 101
    Custom View Example in Canvas
    Canvas(modifier = Modifier.fillMaxSize()) {
    drawCircle(
    color = Color.Red,
    radius = 300f
    )
    drawCircle(
    color = Color.Green,
    radius = 200f
    )
    drawCircle(
    color = Color.Blue,
    radius = 100f
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    Canvas(modifier = Modifier.fillMaxSize()) {
    1
    drawCircle(
    2
    color = Color.Red,
    3
    radius = 300f
    4
    )
    5
    6
    drawCircle(
    7
    color = Color.Green,
    8
    radius = 200f
    9
    )
    10
    11
    drawCircle(
    12
    color = Color.Blue,
    13
    radius = 100f
    14
    )
    15
    }
    16
    drawCircle(
    color = Color.Red,
    radius = 300f
    )
    Canvas(modifier = Modifier.fillMaxSize()) {
    1
    2
    3
    4
    5
    6
    drawCircle(
    7
    color = Color.Green,
    8
    radius = 200f
    9
    )
    10
    11
    drawCircle(
    12
    color = Color.Blue,
    13
    radius = 100f
    14
    )
    15
    }
    16
    10

    View full-size slide

  24. Compose Canvas 101
    Custom View Example in Canvas
    Canvas(modifier = Modifier.fillMaxSize()) {
    drawCircle(
    color = Color.Red,
    radius = 300f
    )
    drawCircle(
    color = Color.Green,
    radius = 200f
    )
    drawCircle(
    color = Color.Blue,
    radius = 100f
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    Canvas(modifier = Modifier.fillMaxSize()) {
    1
    drawCircle(
    2
    color = Color.Red,
    3
    radius = 300f
    4
    )
    5
    6
    drawCircle(
    7
    color = Color.Green,
    8
    radius = 200f
    9
    )
    10
    11
    drawCircle(
    12
    color = Color.Blue,
    13
    radius = 100f
    14
    )
    15
    }
    16
    drawCircle(
    color = Color.Red,
    radius = 300f
    )
    Canvas(modifier = Modifier.fillMaxSize()) {
    1
    2
    3
    4
    5
    6
    drawCircle(
    7
    color = Color.Green,
    8
    radius = 200f
    9
    )
    10
    11
    drawCircle(
    12
    color = Color.Blue,
    13
    radius = 100f
    14
    )
    15
    }
    16
    drawCircle(
    color = Color.Green,
    radius = 200f
    )
    Canvas(modifier = Modifier.fillMaxSize()) {
    1
    drawCircle(
    2
    color = Color.Red,
    3
    radius = 300f
    4
    )
    5
    6
    7
    8
    9
    10
    11
    drawCircle(
    12
    color = Color.Blue,
    13
    radius = 100f
    14
    )
    15
    }
    16
    10

    View full-size slide

  25. Compose Canvas 101
    Custom View Example in Canvas
    Canvas(modifier = Modifier.fillMaxSize()) {
    drawCircle(
    color = Color.Red,
    radius = 300f
    )
    drawCircle(
    color = Color.Green,
    radius = 200f
    )
    drawCircle(
    color = Color.Blue,
    radius = 100f
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    Canvas(modifier = Modifier.fillMaxSize()) {
    1
    drawCircle(
    2
    color = Color.Red,
    3
    radius = 300f
    4
    )
    5
    6
    drawCircle(
    7
    color = Color.Green,
    8
    radius = 200f
    9
    )
    10
    11
    drawCircle(
    12
    color = Color.Blue,
    13
    radius = 100f
    14
    )
    15
    }
    16
    drawCircle(
    color = Color.Red,
    radius = 300f
    )
    Canvas(modifier = Modifier.fillMaxSize()) {
    1
    2
    3
    4
    5
    6
    drawCircle(
    7
    color = Color.Green,
    8
    radius = 200f
    9
    )
    10
    11
    drawCircle(
    12
    color = Color.Blue,
    13
    radius = 100f
    14
    )
    15
    }
    16
    drawCircle(
    color = Color.Green,
    radius = 200f
    )
    Canvas(modifier = Modifier.fillMaxSize()) {
    1
    drawCircle(
    2
    color = Color.Red,
    3
    radius = 300f
    4
    )
    5
    6
    7
    8
    9
    10
    11
    drawCircle(
    12
    color = Color.Blue,
    13
    radius = 100f
    14
    )
    15
    }
    16
    Canvas(modifier = Modifier.fillMaxSize()) {
    drawCircle(
    color = Color.Red,
    radius = 300f
    )
    drawCircle(
    color = Color.Green,
    radius = 200f
    )
    drawCircle(
    color = Color.Blue,
    radius = 100f
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    10

    View full-size slide

  26. Wanna make Dino Game?
    11

    View full-size slide

  27. Drawing Game Scene
    Basics
    12

    View full-size slide

  28. Drawing Game Scene
    Basics
    X
    12

    View full-size slide

  29. Drawing Game Scene
    Basics
    X
    Y
    12

    View full-size slide

  30. Drawing Game Scene
    Basics
    deviceWithInPixels
    X
    Y
    12

    View full-size slide

  31. Drawing Game Scene
    Basics
    deviceWithInPixels
    X
    Y
    Earth Y
    12

    View full-size slide

  32. Drawing Game Scene
    Basics
    // Device Width in Pixels
    var deviceMetrics = DisplayMetrics()
    windowManager.defaultDisplay.getMetrics(deviceMetrics)
    deviceWidthInPixels = deviceMetrics.widthPixels
    1
    2
    3
    4
    5
    // Earth Y position
    6
    const val EARTH_Y_POSITION = 500f
    7
    deviceWithInPixels
    X
    Y
    Earth Y
    12

    View full-size slide

  33. Drawing Game Scene
    Basics
    // Device Width in Pixels
    var deviceMetrics = DisplayMetrics()
    windowManager.defaultDisplay.getMetrics(deviceMetrics)
    deviceWidthInPixels = deviceMetrics.widthPixels
    1
    2
    3
    4
    5
    // Earth Y position
    6
    const val EARTH_Y_POSITION = 500f
    7
    // Earth Y position
    const val EARTH_Y_POSITION = 500f
    // Device Width in Pixels
    1
    var deviceMetrics = DisplayMetrics()
    2
    windowManager.defaultDisplay.getMetrics(deviceMetrics)
    3
    deviceWidthInPixels = deviceMetrics.widthPixels
    4
    5
    6
    7
    deviceWithInPixels
    X
    Y
    Earth Y
    12

    View full-size slide

  34. Drawing Game Scene
    Basics
    // Device Width in Pixels
    var deviceMetrics = DisplayMetrics()
    windowManager.defaultDisplay.getMetrics(deviceMetrics)
    deviceWidthInPixels = deviceMetrics.widthPixels
    1
    2
    3
    4
    5
    // Earth Y position
    6
    const val EARTH_Y_POSITION = 500f
    7
    // Earth Y position
    const val EARTH_Y_POSITION = 500f
    // Device Width in Pixels
    1
    var deviceMetrics = DisplayMetrics()
    2
    windowManager.defaultDisplay.getMetrics(deviceMetrics)
    3
    deviceWidthInPixels = deviceMetrics.widthPixels
    4
    5
    6
    7
    // Device Width in Pixels
    var deviceMetrics = DisplayMetrics()
    windowManager.defaultDisplay.getMetrics(deviceMetrics)
    deviceWidthInPixels = deviceMetrics.widthPixels
    // Earth Y position
    const val EARTH_Y_POSITION = 500f
    1
    2
    3
    4
    5
    6
    7
    deviceWithInPixels
    X
    Y
    Earth Y
    12

    View full-size slide

  35. Drawing Game Scene
    Earth
    13

    View full-size slide

  36. Drawing Game Scene
    Earth
    fun DrawScope.EarthView(earthState: EarthState)
    {
    // Ground Line
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION),
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH
    )
    // Dirt Line 1
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 20),
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 20),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    pathEffect = DashPathEffect(floatArrayOf(20f, 40f), 0f)
    )
    // Dirt Line 2
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 30),
    end = Offset(x = x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 30),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    pathEffect = DashPathEffect(floatArrayOf(15f, 50f), 40f)
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    13

    View full-size slide

  37. Drawing Game Scene
    Earth
    fun DrawScope.EarthView(earthState: EarthState)
    {
    // Ground Line
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION),
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH
    )
    // Dirt Line 1
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 20),
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 20),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    pathEffect = DashPathEffect(floatArrayOf(20f, 40f), 0f)
    )
    // Dirt Line 2
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 30),
    end = Offset(x = x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 30),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    pathEffect = DashPathEffect(floatArrayOf(15f, 50f), 40f)
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    // Ground Line
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION),
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH
    )
    fun DrawScope.EarthView(earthState: EarthState)
    1
    {
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Dirt Line 1
    11
    drawLine(
    12
    color = Color.DarkGray,
    13
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 20),
    14
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 20),
    15
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    16
    pathEffect = DashPathEffect(floatArrayOf(20f, 40f), 0f)
    17
    )
    18
    19
    // Dirt Line 2
    20
    drawLine(
    21
    color = Color.DarkGray,
    22
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 30),
    23
    end = Offset(x = x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 30),
    24
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    25
    pathEffect = DashPathEffect(floatArrayOf(15f, 50f), 40f)
    26
    )
    27
    }
    28
    13

    View full-size slide

  38. Drawing Game Scene
    Earth
    fun DrawScope.EarthView(earthState: EarthState)
    {
    // Ground Line
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION),
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH
    )
    // Dirt Line 1
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 20),
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 20),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    pathEffect = DashPathEffect(floatArrayOf(20f, 40f), 0f)
    )
    // Dirt Line 2
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 30),
    end = Offset(x = x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 30),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    pathEffect = DashPathEffect(floatArrayOf(15f, 50f), 40f)
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    // Ground Line
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION),
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH
    )
    fun DrawScope.EarthView(earthState: EarthState)
    1
    {
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Dirt Line 1
    11
    drawLine(
    12
    color = Color.DarkGray,
    13
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 20),
    14
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 20),
    15
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    16
    pathEffect = DashPathEffect(floatArrayOf(20f, 40f), 0f)
    17
    )
    18
    19
    // Dirt Line 2
    20
    drawLine(
    21
    color = Color.DarkGray,
    22
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 30),
    23
    end = Offset(x = x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 30),
    24
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    25
    pathEffect = DashPathEffect(floatArrayOf(15f, 50f), 40f)
    26
    )
    27
    }
    28
    // Dirt Line 1
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 20),
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 20),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    pathEffect = DashPathEffect(floatArrayOf(20f, 40f), 0f)
    )
    fun DrawScope.EarthView(earthState: EarthState)
    1
    {
    2
    // Ground Line
    3
    drawLine(
    4
    color = Color.DarkGray,
    5
    start = Offset(x = 0f, y = EARTH_Y_POSITION),
    6
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION),
    7
    strokeWidth = EARTH_GROUND_STROKE_WIDTH
    8
    )
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // Dirt Line 2
    20
    drawLine(
    21
    color = Color.DarkGray,
    22
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 30),
    23
    end = Offset(x = x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 30),
    24
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    25
    pathEffect = DashPathEffect(floatArrayOf(15f, 50f), 40f)
    26
    )
    27
    }
    28
    13

    View full-size slide

  39. Drawing Game Scene
    Earth
    fun DrawScope.EarthView(earthState: EarthState)
    {
    // Ground Line
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION),
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH
    )
    // Dirt Line 1
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 20),
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 20),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    pathEffect = DashPathEffect(floatArrayOf(20f, 40f), 0f)
    )
    // Dirt Line 2
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 30),
    end = Offset(x = x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 30),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    pathEffect = DashPathEffect(floatArrayOf(15f, 50f), 40f)
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    // Ground Line
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION),
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH
    )
    fun DrawScope.EarthView(earthState: EarthState)
    1
    {
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Dirt Line 1
    11
    drawLine(
    12
    color = Color.DarkGray,
    13
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 20),
    14
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 20),
    15
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    16
    pathEffect = DashPathEffect(floatArrayOf(20f, 40f), 0f)
    17
    )
    18
    19
    // Dirt Line 2
    20
    drawLine(
    21
    color = Color.DarkGray,
    22
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 30),
    23
    end = Offset(x = x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 30),
    24
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    25
    pathEffect = DashPathEffect(floatArrayOf(15f, 50f), 40f)
    26
    )
    27
    }
    28
    // Dirt Line 1
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 20),
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 20),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    pathEffect = DashPathEffect(floatArrayOf(20f, 40f), 0f)
    )
    fun DrawScope.EarthView(earthState: EarthState)
    1
    {
    2
    // Ground Line
    3
    drawLine(
    4
    color = Color.DarkGray,
    5
    start = Offset(x = 0f, y = EARTH_Y_POSITION),
    6
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION),
    7
    strokeWidth = EARTH_GROUND_STROKE_WIDTH
    8
    )
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // Dirt Line 2
    20
    drawLine(
    21
    color = Color.DarkGray,
    22
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 30),
    23
    end = Offset(x = x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 30),
    24
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    25
    pathEffect = DashPathEffect(floatArrayOf(15f, 50f), 40f)
    26
    )
    27
    }
    28
    // Dirt Line 2
    drawLine(
    color = Color.DarkGray,
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 30),
    end = Offset(x = x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 30),
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    pathEffect = DashPathEffect(floatArrayOf(15f, 50f), 40f)
    )
    fun DrawScope.EarthView(earthState: EarthState)
    1
    {
    2
    // Ground Line
    3
    drawLine(
    4
    color = Color.DarkGray,
    5
    start = Offset(x = 0f, y = EARTH_Y_POSITION),
    6
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION),
    7
    strokeWidth = EARTH_GROUND_STROKE_WIDTH
    8
    )
    9
    10
    // Dirt Line 1
    11
    drawLine(
    12
    color = Color.DarkGray,
    13
    start = Offset(x = 0f, y = EARTH_Y_POSITION + 20),
    14
    end = Offset(x = deviceWidthInPixels.toFloat(), y = EARTH_Y_POSITION + 20),
    15
    strokeWidth = EARTH_GROUND_STROKE_WIDTH / 5,
    16
    pathEffect = DashPathEffect(floatArrayOf(20f, 40f), 0f)
    17
    )
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    }
    28
    13

    View full-size slide

  40. Drawing Game Scene
    Vector Icons (SVG)
    Icons made by from
    Freepik www.flaticon.com
    14

    View full-size slide

  41. Drawing Game Scene
    Vector Icons Issues
    All icons are of different sizes
    Canvas uses Vector icon's coordinates as
    pixels
    Icons need to be of size width/height at least
    for uniformity
    I resized all to 200x200 using this website
    https://www.iloveimg.com/resize-image/resize-svg
    15

    View full-size slide

  42. Drawing Game Scene
    Clouds
    Text
    16

    View full-size slide

  43. Drawing Game Scene
    Clouds
    Text
    No method to draw VectorDrawables in Canvas yet!
    16

    View full-size slide

  44. Drawing Game Scene
    Clouds
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    fun CloudPath(): Path {
    var path = cloudPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Draw Cloud
    fun DrawScope.CloudsView(cloudState: CloudState)
    {
    drawPath(
    path = cloud.path,
    color = Color(0xFFC5C5C5),
    style = Stroke(2f)
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    17

    View full-size slide

  45. Drawing Game Scene
    Clouds
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    fun CloudPath(): Path {
    var path = cloudPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Draw Cloud
    fun DrawScope.CloudsView(cloudState: CloudState)
    {
    drawPath(
    path = cloud.path,
    color = Color(0xFFC5C5C5),
    style = Stroke(2f)
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    1
    2
    3
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    17

    View full-size slide

  46. Drawing Game Scene
    Clouds
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    fun CloudPath(): Path {
    var path = cloudPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Draw Cloud
    fun DrawScope.CloudsView(cloudState: CloudState)
    {
    drawPath(
    path = cloud.path,
    color = Color(0xFFC5C5C5),
    style = Stroke(2f)
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    1
    2
    3
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    fun CloudPath(): Path {
    var path = cloudPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    17

    View full-size slide

  47. Drawing Game Scene
    Clouds
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    fun CloudPath(): Path {
    var path = cloudPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Draw Cloud
    fun DrawScope.CloudsView(cloudState: CloudState)
    {
    drawPath(
    path = cloud.path,
    color = Color(0xFFC5C5C5),
    style = Stroke(2f)
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    1
    2
    3
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    fun CloudPath(): Path {
    var path = cloudPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    17

    View full-size slide

  48. Drawing Game Scene
    Clouds
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    fun CloudPath(): Path {
    var path = cloudPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Draw Cloud
    fun DrawScope.CloudsView(cloudState: CloudState)
    {
    drawPath(
    path = cloud.path,
    color = Color(0xFFC5C5C5),
    style = Stroke(2f)
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    1
    2
    3
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    fun CloudPath(): Path {
    var path = cloudPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    androidPath.transform(scaleMatrix)
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    17

    View full-size slide

  49. Drawing Game Scene
    Clouds
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    fun CloudPath(): Path {
    var path = cloudPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Draw Cloud
    fun DrawScope.CloudsView(cloudState: CloudState)
    {
    drawPath(
    path = cloud.path,
    color = Color(0xFFC5C5C5),
    style = Stroke(2f)
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    1
    2
    3
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    fun CloudPath(): Path {
    var path = cloudPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    androidPath.transform(scaleMatrix)
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    return androidPath.asComposePath()
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    17

    View full-size slide

  50. Drawing Game Scene
    Clouds
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    fun CloudPath(): Path {
    var path = cloudPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Draw Cloud
    fun DrawScope.CloudsView(cloudState: CloudState)
    {
    drawPath(
    path = cloud.path,
    color = Color(0xFFC5C5C5),
    style = Stroke(2f)
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    1
    2
    3
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    fun CloudPath(): Path {
    var path = cloudPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    androidPath.transform(scaleMatrix)
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    return androidPath.asComposePath()
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    fun DrawScope.CloudsView(cloudState: CloudState)
    {
    drawPath(
    path = cloud.path,
    color = Color(0xFFC5C5C5),
    style = Stroke(2f)
    )
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    15
    16
    17
    18
    19
    20
    21
    }
    22
    17

    View full-size slide

  51. Drawing Game Scene
    Clouds
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    fun CloudPath(): Path {
    var path = cloudPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Draw Cloud
    fun DrawScope.CloudsView(cloudState: CloudState)
    {
    drawPath(
    path = cloud.path,
    color = Color(0xFFC5C5C5),
    style = Stroke(2f)
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Cloud Path
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    fun CloudPathNodes() = cloudPath.toNodes()
    1
    2
    3
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    fun CloudPath(): Path {
    var path = cloudPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    androidPath.transform(scaleMatrix)
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    return androidPath.asComposePath()
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    path = cloud.path,
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    fun DrawScope.CloudsView(cloudState: CloudState)
    {
    drawPath(
    path = cloud.path,
    color = Color(0xFFC5C5C5),
    style = Stroke(2f)
    )
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    15
    16
    17
    18
    19
    20
    21
    }
    22
    path = cloud.path,
    // Cloud Path
    1
    private var CLOUD_PATH_STR = "M169.895,88.699C167.27,71.887 152.695,58.984 135.156,58
    2
    private var cloudPath = PathParser().parsePathString(CLOUD_PATH_STR)
    3
    fun CloudPathNodes() = cloudPath.toNodes()
    4
    fun CloudPath(): Path {
    5
    var path = cloudPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cloud
    14
    fun DrawScope.CloudsView(cloudState: CloudState)
    15
    {
    16
    drawPath(
    17
    18
    color = Color(0xFFC5C5C5),
    19
    style = Stroke(2f)
    20
    )
    21
    }
    22
    17

    View full-size slide

  52. Drawing Game Scene
    Cactus
    // Cactus Path
    private var CACTUS_PATH_STR = "M57.449,111.191L85.246,111.191L85.246,200.145L118.605,2
    private var cactusPath = PathParser().parsePathString(CACTUS_PATH_STR)
    fun CactusPathNodes() = cactusPath.toNodes()
    fun CactusPath(): Path {
    var path = cactusPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Draw Cactus
    fun DrawScope.CactusView(cactusState: CactusState)
    {
    drawPath(
    path = cactus.path,
    color = Color(0xFF000000),
    style = Fill
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    18

    View full-size slide

  53. Drawing Game Scene
    Cactus
    // Cactus Path
    private var CACTUS_PATH_STR = "M57.449,111.191L85.246,111.191L85.246,200.145L118.605,2
    private var cactusPath = PathParser().parsePathString(CACTUS_PATH_STR)
    fun CactusPathNodes() = cactusPath.toNodes()
    fun CactusPath(): Path {
    var path = cactusPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Draw Cactus
    fun DrawScope.CactusView(cactusState: CactusState)
    {
    drawPath(
    path = cactus.path,
    color = Color(0xFF000000),
    style = Fill
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    fun DrawScope.CactusView(cactusState: CactusState)
    {
    drawPath(
    path = cactus.path,
    color = Color(0xFF000000),
    style = Fill
    )
    // Cactus Path
    1
    private var CACTUS_PATH_STR = "M57.449,111.191L85.246,111.191L85.246,200.145L118.605,2
    2
    private var cactusPath = PathParser().parsePathString(CACTUS_PATH_STR)
    3
    fun CactusPathNodes() = cactusPath.toNodes()
    4
    fun CactusPath(): Path {
    5
    var path = cactusPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Cactus
    14
    15
    16
    17
    18
    19
    20
    21
    }
    22
    18

    View full-size slide

  54. Drawing Game Scene
    T-Rex
    // T-Rex Dino
    private var TREX_DINO_PATH_STR = "M93.027,18.996L173.41,18.996L173.41,60.836L93.027,60
    private var trexPath = PathParser().parsePathString(TREX_DINO_PATH_STR)
    fun DinoPathNodes() = trexPath.toNodes()
    fun DinoPath(): Path {
    var path = trexPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Draw Dino
    fun DrawScope.DinoView(dinoState: DinoState) {
    {
    drawPath(
    path = dinoState.path
    color = Color(0xFF000000),
    style = Fill
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    19

    View full-size slide

  55. Drawing Game Scene
    T-Rex
    // T-Rex Dino
    private var TREX_DINO_PATH_STR = "M93.027,18.996L173.41,18.996L173.41,60.836L93.027,60
    private var trexPath = PathParser().parsePathString(TREX_DINO_PATH_STR)
    fun DinoPathNodes() = trexPath.toNodes()
    fun DinoPath(): Path {
    var path = trexPath.toPath()
    var scaleMatrix = Matrix()
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    var androidPath = path.asAndroidPath()
    androidPath.transform(scaleMatrix)
    return androidPath.asComposePath()
    }
    // Draw Dino
    fun DrawScope.DinoView(dinoState: DinoState) {
    {
    drawPath(
    path = dinoState.path
    color = Color(0xFF000000),
    style = Fill
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    fun DrawScope.DinoView(dinoState: DinoState) {
    {
    drawPath(
    path = dinoState.path
    color = Color(0xFF000000),
    style = Fill
    )
    // T-Rex Dino
    1
    private var TREX_DINO_PATH_STR = "M93.027,18.996L173.41,18.996L173.41,60.836L93.027,60
    2
    private var trexPath = PathParser().parsePathString(TREX_DINO_PATH_STR)
    3
    fun DinoPathNodes() = trexPath.toNodes()
    4
    fun DinoPath(): Path {
    5
    var path = trexPath.toPath()
    6
    var scaleMatrix = Matrix()
    7
    scaleMatrix.setScale(BASE_SCALE, BASE_SCALE, 0f, 0f)
    8
    var androidPath = path.asAndroidPath()
    9
    androidPath.transform(scaleMatrix)
    10
    return androidPath.asComposePath()
    11
    }
    12
    13
    // Draw Dino
    14
    15
    16
    17
    18
    19
    20
    21
    }
    22
    19

    View full-size slide

  56. Drawing Game Scene
    We need to set positions for each element dynamically
    @Composable
    fun DinoGameScene()
    {
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    20

    View full-size slide

  57. Game Loop in Compose
    I assumed this
    Compose automatically updates when state
    changes
    Why we even need game loop?
    Compose State can do our job.
    21

    View full-size slide

  58. Game Loop in Compose
    Attempt # 1 - Playing with states
    // Cloud Model
    data class CloudModel(
    var xPos: Int = 0,
    var yPos: Int = 0,
    var path: Path = CloudPath()
    )
    // Cloud State
    data class CloudState(
    val cloudsList: ArrayList = arrayListOf(),
    val maxClouds: Int = 3,
    val speed: Int = 1
    )
    // Setting Cloud State
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    // Game Loop
    while(true) {
    cloudsState.cloudsList.forEach { cloud ->
    cloud.x++
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    22

    View full-size slide

  59. Game Loop in Compose
    Attempt # 1 - Playing with states
    // Cloud Model
    data class CloudModel(
    var xPos: Int = 0,
    var yPos: Int = 0,
    var path: Path = CloudPath()
    )
    // Cloud State
    data class CloudState(
    val cloudsList: ArrayList = arrayListOf(),
    val maxClouds: Int = 3,
    val speed: Int = 1
    )
    // Setting Cloud State
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    // Game Loop
    while(true) {
    cloudsState.cloudsList.forEach { cloud ->
    cloud.x++
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // Cloud Model
    data class CloudModel(
    var xPos: Int = 0,
    var yPos: Int = 0,
    var path: Path = CloudPath()
    )
    1
    2
    3
    4
    5
    6
    7
    // Cloud State
    8
    data class CloudState(
    9
    val cloudsList: ArrayList = arrayListOf(),
    10
    val maxClouds: Int = 3,
    11
    val speed: Int = 1
    12
    )
    13
    14
    // Setting Cloud State
    15
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    16
    17
    // Game Loop
    18
    while(true) {
    19
    cloudsState.cloudsList.forEach { cloud ->
    20
    cloud.x++
    21
    }
    22
    }
    23
    22

    View full-size slide

  60. Game Loop in Compose
    Attempt # 1 - Playing with states
    // Cloud Model
    data class CloudModel(
    var xPos: Int = 0,
    var yPos: Int = 0,
    var path: Path = CloudPath()
    )
    // Cloud State
    data class CloudState(
    val cloudsList: ArrayList = arrayListOf(),
    val maxClouds: Int = 3,
    val speed: Int = 1
    )
    // Setting Cloud State
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    // Game Loop
    while(true) {
    cloudsState.cloudsList.forEach { cloud ->
    cloud.x++
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // Cloud Model
    data class CloudModel(
    var xPos: Int = 0,
    var yPos: Int = 0,
    var path: Path = CloudPath()
    )
    1
    2
    3
    4
    5
    6
    7
    // Cloud State
    8
    data class CloudState(
    9
    val cloudsList: ArrayList = arrayListOf(),
    10
    val maxClouds: Int = 3,
    11
    val speed: Int = 1
    12
    )
    13
    14
    // Setting Cloud State
    15
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    16
    17
    // Game Loop
    18
    while(true) {
    19
    cloudsState.cloudsList.forEach { cloud ->
    20
    cloud.x++
    21
    }
    22
    }
    23
    // Cloud State
    data class CloudState(
    val cloudsList: ArrayList = arrayListOf(),
    val maxClouds: Int = 3,
    val speed: Int = 1
    )
    // Cloud Model
    1
    data class CloudModel(
    2
    var xPos: Int = 0,
    3
    var yPos: Int = 0,
    4
    var path: Path = CloudPath()
    5
    )
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Setting Cloud State
    15
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    16
    17
    // Game Loop
    18
    while(true) {
    19
    cloudsState.cloudsList.forEach { cloud ->
    20
    cloud.x++
    21
    }
    22
    }
    23
    22

    View full-size slide

  61. Game Loop in Compose
    Attempt # 1 - Playing with states
    // Cloud Model
    data class CloudModel(
    var xPos: Int = 0,
    var yPos: Int = 0,
    var path: Path = CloudPath()
    )
    // Cloud State
    data class CloudState(
    val cloudsList: ArrayList = arrayListOf(),
    val maxClouds: Int = 3,
    val speed: Int = 1
    )
    // Setting Cloud State
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    // Game Loop
    while(true) {
    cloudsState.cloudsList.forEach { cloud ->
    cloud.x++
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // Cloud Model
    data class CloudModel(
    var xPos: Int = 0,
    var yPos: Int = 0,
    var path: Path = CloudPath()
    )
    1
    2
    3
    4
    5
    6
    7
    // Cloud State
    8
    data class CloudState(
    9
    val cloudsList: ArrayList = arrayListOf(),
    10
    val maxClouds: Int = 3,
    11
    val speed: Int = 1
    12
    )
    13
    14
    // Setting Cloud State
    15
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    16
    17
    // Game Loop
    18
    while(true) {
    19
    cloudsState.cloudsList.forEach { cloud ->
    20
    cloud.x++
    21
    }
    22
    }
    23
    // Cloud State
    data class CloudState(
    val cloudsList: ArrayList = arrayListOf(),
    val maxClouds: Int = 3,
    val speed: Int = 1
    )
    // Cloud Model
    1
    data class CloudModel(
    2
    var xPos: Int = 0,
    3
    var yPos: Int = 0,
    4
    var path: Path = CloudPath()
    5
    )
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Setting Cloud State
    15
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    16
    17
    // Game Loop
    18
    while(true) {
    19
    cloudsState.cloudsList.forEach { cloud ->
    20
    cloud.x++
    21
    }
    22
    }
    23
    // Setting Cloud State
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    // Cloud Model
    1
    data class CloudModel(
    2
    var xPos: Int = 0,
    3
    var yPos: Int = 0,
    4
    var path: Path = CloudPath()
    5
    )
    6
    7
    // Cloud State
    8
    data class CloudState(
    9
    val cloudsList: ArrayList = arrayListOf(),
    10
    val maxClouds: Int = 3,
    11
    val speed: Int = 1
    12
    )
    13
    14
    15
    16
    17
    // Game Loop
    18
    while(true) {
    19
    cloudsState.cloudsList.forEach { cloud ->
    20
    cloud.x++
    21
    }
    22
    }
    23
    22

    View full-size slide

  62. Game Loop in Compose
    Attempt # 1 - Playing with states
    // Cloud Model
    data class CloudModel(
    var xPos: Int = 0,
    var yPos: Int = 0,
    var path: Path = CloudPath()
    )
    // Cloud State
    data class CloudState(
    val cloudsList: ArrayList = arrayListOf(),
    val maxClouds: Int = 3,
    val speed: Int = 1
    )
    // Setting Cloud State
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    // Game Loop
    while(true) {
    cloudsState.cloudsList.forEach { cloud ->
    cloud.x++
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // Cloud Model
    data class CloudModel(
    var xPos: Int = 0,
    var yPos: Int = 0,
    var path: Path = CloudPath()
    )
    1
    2
    3
    4
    5
    6
    7
    // Cloud State
    8
    data class CloudState(
    9
    val cloudsList: ArrayList = arrayListOf(),
    10
    val maxClouds: Int = 3,
    11
    val speed: Int = 1
    12
    )
    13
    14
    // Setting Cloud State
    15
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    16
    17
    // Game Loop
    18
    while(true) {
    19
    cloudsState.cloudsList.forEach { cloud ->
    20
    cloud.x++
    21
    }
    22
    }
    23
    // Cloud State
    data class CloudState(
    val cloudsList: ArrayList = arrayListOf(),
    val maxClouds: Int = 3,
    val speed: Int = 1
    )
    // Cloud Model
    1
    data class CloudModel(
    2
    var xPos: Int = 0,
    3
    var yPos: Int = 0,
    4
    var path: Path = CloudPath()
    5
    )
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Setting Cloud State
    15
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    16
    17
    // Game Loop
    18
    while(true) {
    19
    cloudsState.cloudsList.forEach { cloud ->
    20
    cloud.x++
    21
    }
    22
    }
    23
    // Setting Cloud State
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    // Cloud Model
    1
    data class CloudModel(
    2
    var xPos: Int = 0,
    3
    var yPos: Int = 0,
    4
    var path: Path = CloudPath()
    5
    )
    6
    7
    // Cloud State
    8
    data class CloudState(
    9
    val cloudsList: ArrayList = arrayListOf(),
    10
    val maxClouds: Int = 3,
    11
    val speed: Int = 1
    12
    )
    13
    14
    15
    16
    17
    // Game Loop
    18
    while(true) {
    19
    cloudsState.cloudsList.forEach { cloud ->
    20
    cloud.x++
    21
    }
    22
    }
    23
    // Game Loop
    while(true) {
    cloudsState.cloudsList.forEach { cloud ->
    cloud.x++
    }
    }
    // Cloud Model
    1
    data class CloudModel(
    2
    var xPos: Int = 0,
    3
    var yPos: Int = 0,
    4
    var path: Path = CloudPath()
    5
    )
    6
    7
    // Cloud State
    8
    data class CloudState(
    9
    val cloudsList: ArrayList = arrayListOf(),
    10
    val maxClouds: Int = 3,
    11
    val speed: Int = 1
    12
    )
    13
    14
    // Setting Cloud State
    15
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    16
    17
    18
    19
    20
    21
    22
    23
    22

    View full-size slide

  63. Game Loop in Compose
    Attempt # 1 - Playing with states
    // Cloud Model
    data class CloudModel(
    var xPos: Int = 0,
    var yPos: Int = 0,
    var path: Path = CloudPath()
    )
    // Cloud State
    data class CloudState(
    val cloudsList: ArrayList = arrayListOf(),
    val maxClouds: Int = 3,
    val speed: Int = 1
    )
    // Setting Cloud State
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    // Game Loop
    while(true) {
    cloudsState.cloudsList.forEach { cloud ->
    cloud.x++
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    Failed. No Cloud
    Anim
    ation
    23

    View full-size slide

  64. Game Loop in Compose
    Attempt # 2 - FrameCallback
    // Cloud State
    data class CloudState( /* ... */ ) {
    fun moveForward()
    {
    cactusList.forEach { cactus ->
    cactus.xPos -= cactusSpeed
    }
    }
    // Setting Cloud State
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    // Game Loop
    val gameLoopCallback = object : Choreographer.FrameCallback {
    override fun doFrame(frameTimeNanos: Long) {
    cloudsState.value = cloudsState.value.moveForward()
    Choreographer.getInstance().postFrameCallback(this)
    }
    }
    Choreographer.getInstance().postFrameCallback(gameLoopCallback)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    24

    View full-size slide

  65. Game Loop in Compose
    Attempt # 2 - FrameCallback
    // Cloud State
    data class CloudState( /* ... */ ) {
    fun moveForward()
    {
    cactusList.forEach { cactus ->
    cactus.xPos -= cactusSpeed
    }
    }
    // Setting Cloud State
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED) }
    // Game Loop
    val gameLoopCallback = object : Choreographer.FrameCallback {
    override fun doFrame(frameTimeNanos: Long) {
    cloudsState.value = cloudsState.value.moveForward()
    Choreographer.getInstance().postFrameCallback(this)
    }
    }
    Choreographer.getInstance().postFrameCallback(gameLoopCallback)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Failed. No Cloud
    Anim
    ation
    24

    View full-size slide

  66. Game Loop in Compose
    Attempt # 3 - Compose Couroutines
    @Composable
    fun animationTimeMillis(gameloopCallback: () -> Unit): State
    {
    val millisState = remember { mutableStateOf(0L) }
    val lifecycleOwner = LifecycleOwnerAmbient.current
    launchInComposition {
    val startTime = withFrameMillis { it }
    lifecycleOwner.whenStarted {
    while(true) {
    withFrameMillis { frameTimeMillis: Long ->
    millisState.value = frameTimeMillis - startTime
    }
    gameloopCallback.invoke()
    }
    }
    }
    return millisState
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    https://github.com/JorgeCastilloPrz/
    25

    View full-size slide

  67. Game Loop in Compose
    Attempt # 3 - Compose Couroutines
    @Composable
    fun animationTimeMillis(gameloopCallback: () -> Unit): State
    {
    val millisState = remember { mutableStateOf(0L) }
    val lifecycleOwner = LifecycleOwnerAmbient.current
    launchInComposition {
    val startTime = withFrameMillis { it }
    lifecycleOwner.whenStarted {
    while(true) {
    withFrameMillis { frameTimeMillis: Long ->
    millisState.value = frameTimeMillis - startTime
    }
    gameloopCallback.invoke()
    }
    }
    }
    return millisState
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    val millisState = remember { mutableStateOf(0L) }
    @Composable
    1
    fun animationTimeMillis(gameloopCallback: () -> Unit): State
    2
    {
    3
    4
    val lifecycleOwner = LifecycleOwnerAmbient.current
    5
    launchInComposition {
    6
    val startTime = withFrameMillis { it }
    7
    lifecycleOwner.whenStarted {
    8
    while(true) {
    9
    withFrameMillis { frameTimeMillis: Long ->
    10
    millisState.value = frameTimeMillis - startTime
    11
    }
    12
    gameloopCallback.invoke()
    13
    }
    14
    }
    15
    }
    16
    return millisState
    17
    }
    18
    https://github.com/JorgeCastilloPrz/
    25

    View full-size slide

  68. Game Loop in Compose
    Attempt # 3 - Compose Couroutines
    @Composable
    fun animationTimeMillis(gameloopCallback: () -> Unit): State
    {
    val millisState = remember { mutableStateOf(0L) }
    val lifecycleOwner = LifecycleOwnerAmbient.current
    launchInComposition {
    val startTime = withFrameMillis { it }
    lifecycleOwner.whenStarted {
    while(true) {
    withFrameMillis { frameTimeMillis: Long ->
    millisState.value = frameTimeMillis - startTime
    }
    gameloopCallback.invoke()
    }
    }
    }
    return millisState
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    val millisState = remember { mutableStateOf(0L) }
    @Composable
    1
    fun animationTimeMillis(gameloopCallback: () -> Unit): State
    2
    {
    3
    4
    val lifecycleOwner = LifecycleOwnerAmbient.current
    5
    launchInComposition {
    6
    val startTime = withFrameMillis { it }
    7
    lifecycleOwner.whenStarted {
    8
    while(true) {
    9
    withFrameMillis { frameTimeMillis: Long ->
    10
    millisState.value = frameTimeMillis - startTime
    11
    }
    12
    gameloopCallback.invoke()
    13
    }
    14
    }
    15
    }
    16
    return millisState
    17
    }
    18
    while(true) {
    withFrameMillis { frameTimeMillis: Long ->
    millisState.value = frameTimeMillis - startTime
    }
    gameloopCallback.invoke()
    }
    @Composable
    1
    fun animationTimeMillis(gameloopCallback: () -> Unit): State
    2
    {
    3
    val millisState = remember { mutableStateOf(0L) }
    4
    val lifecycleOwner = LifecycleOwnerAmbient.current
    5
    launchInComposition {
    6
    val startTime = withFrameMillis { it }
    7
    lifecycleOwner.whenStarted {
    8
    9
    10
    11
    12
    13
    14
    }
    15
    }
    16
    return millisState
    17
    }
    18
    https://github.com/JorgeCastilloPrz/
    25

    View full-size slide

  69. Game Loop in Compose
    Attempt # 3 - Compose Couroutines
    @Composable
    fun animationTimeMillis(gameloopCallback: () -> Unit): State
    {
    val millisState = remember { mutableStateOf(0L) }
    val lifecycleOwner = LifecycleOwnerAmbient.current
    launchInComposition {
    val startTime = withFrameMillis { it }
    lifecycleOwner.whenStarted {
    while(true) {
    withFrameMillis { frameTimeMillis: Long ->
    millisState.value = frameTimeMillis - startTime
    }
    gameloopCallback.invoke()
    }
    }
    }
    return millisState
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    val millisState = remember { mutableStateOf(0L) }
    @Composable
    1
    fun animationTimeMillis(gameloopCallback: () -> Unit): State
    2
    {
    3
    4
    val lifecycleOwner = LifecycleOwnerAmbient.current
    5
    launchInComposition {
    6
    val startTime = withFrameMillis { it }
    7
    lifecycleOwner.whenStarted {
    8
    while(true) {
    9
    withFrameMillis { frameTimeMillis: Long ->
    10
    millisState.value = frameTimeMillis - startTime
    11
    }
    12
    gameloopCallback.invoke()
    13
    }
    14
    }
    15
    }
    16
    return millisState
    17
    }
    18
    while(true) {
    withFrameMillis { frameTimeMillis: Long ->
    millisState.value = frameTimeMillis - startTime
    }
    gameloopCallback.invoke()
    }
    @Composable
    1
    fun animationTimeMillis(gameloopCallback: () -> Unit): State
    2
    {
    3
    val millisState = remember { mutableStateOf(0L) }
    4
    val lifecycleOwner = LifecycleOwnerAmbient.current
    5
    launchInComposition {
    6
    val startTime = withFrameMillis { it }
    7
    lifecycleOwner.whenStarted {
    8
    9
    10
    11
    12
    13
    14
    }
    15
    }
    16
    return millisState
    17
    }
    18
    return millisState
    @Composable
    1
    fun animationTimeMillis(gameloopCallback: () -> Unit): State
    2
    {
    3
    val millisState = remember { mutableStateOf(0L) }
    4
    val lifecycleOwner = LifecycleOwnerAmbient.current
    5
    launchInComposition {
    6
    val startTime = withFrameMillis { it }
    7
    lifecycleOwner.whenStarted {
    8
    while(true) {
    9
    withFrameMillis { frameTimeMillis: Long ->
    10
    millisState.value = frameTimeMillis - startTime
    11
    }
    12
    gameloopCallback.invoke()
    13
    }
    14
    }
    15
    }
    16
    17
    }
    18
    https://github.com/JorgeCastilloPrz/
    25

    View full-size slide

  70. Game Loop in Compose
    Attempt # 3 - Compose Couroutines
    @Composable
    fun DinoGameScene()
    {
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED)
    val timeState = animationTimeMillis {
    cloudsState.moveForward()
    }
    var millis = timeState.millis
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    26

    View full-size slide

  71. Game Loop in Compose
    Attempt # 3 - Compose Couroutines
    @Composable
    fun DinoGameScene()
    {
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED)
    val timeState = animationTimeMillis {
    cloudsState.moveForward()
    }
    var millis = timeState.millis
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    val timeState = animationTimeMillis {
    cloudsState.moveForward()
    }
    @Composable
    1
    fun DinoGameScene()
    2
    {
    3
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED)
    4
    5
    6
    7
    8
    9
    var millis = timeState.millis
    10
    11
    Canvas(modifier = Modifier.weight(1f)) {
    12
    EarthView(earthState)
    13
    CloudsView(cloudsState)
    14
    DinoView(dinoState)
    15
    CactusView(cactusState)
    16
    }
    17
    }
    18
    26

    View full-size slide

  72. Game Loop in Compose
    Attempt # 3 - Compose Couroutines
    @Composable
    fun DinoGameScene()
    {
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED)
    val timeState = animationTimeMillis {
    cloudsState.moveForward()
    }
    var millis = timeState.millis
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    val timeState = animationTimeMillis {
    cloudsState.moveForward()
    }
    @Composable
    1
    fun DinoGameScene()
    2
    {
    3
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED)
    4
    5
    6
    7
    8
    9
    var millis = timeState.millis
    10
    11
    Canvas(modifier = Modifier.weight(1f)) {
    12
    EarthView(earthState)
    13
    CloudsView(cloudsState)
    14
    DinoView(dinoState)
    15
    CactusView(cactusState)
    16
    }
    17
    }
    18
    var millis = timeState.millis
    @Composable
    1
    fun DinoGameScene()
    2
    {
    3
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED)
    4
    5
    val timeState = animationTimeMillis {
    6
    cloudsState.moveForward()
    7
    }
    8
    9
    10
    11
    Canvas(modifier = Modifier.weight(1f)) {
    12
    EarthView(earthState)
    13
    CloudsView(cloudsState)
    14
    DinoView(dinoState)
    15
    CactusView(cactusState)
    16
    }
    17
    }
    18
    26

    View full-size slide

  73. Game Loop in Compose
    Attempt # 3 - Compose Couroutines
    @Composable
    fun DinoGameScene()
    {
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED)
    val timeState = animationTimeMillis {
    cloudsState.moveForward()
    }
    var millis = timeState.millis
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    val timeState = animationTimeMillis {
    cloudsState.moveForward()
    }
    @Composable
    1
    fun DinoGameScene()
    2
    {
    3
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED)
    4
    5
    6
    7
    8
    9
    var millis = timeState.millis
    10
    11
    Canvas(modifier = Modifier.weight(1f)) {
    12
    EarthView(earthState)
    13
    CloudsView(cloudsState)
    14
    DinoView(dinoState)
    15
    CactusView(cactusState)
    16
    }
    17
    }
    18
    var millis = timeState.millis
    @Composable
    1
    fun DinoGameScene()
    2
    {
    3
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED)
    4
    5
    val timeState = animationTimeMillis {
    6
    cloudsState.moveForward()
    7
    }
    8
    9
    10
    11
    Canvas(modifier = Modifier.weight(1f)) {
    12
    EarthView(earthState)
    13
    CloudsView(cloudsState)
    14
    DinoView(dinoState)
    15
    CactusView(cactusState)
    16
    }
    17
    }
    18
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    @Composable
    1
    fun DinoGameScene()
    2
    {
    3
    var cloudsState = remember { CloudState(maxClouds = MAX_CLOUDS, speed = CLOUDS_SPEED)
    4
    5
    val timeState = animationTimeMillis {
    6
    cloudsState.moveForward()
    7
    }
    8
    9
    var millis = timeState.millis
    10
    11
    12
    13
    14
    15
    16
    17
    }
    18
    26

    View full-size slide

  74. Game Loop in Compose
    Wait... One more thing!
    fun DrawScope.CloudsView(cloudState: CloudState)
    {
    cloudState.cloudsList.forEach {cloud ->
    withTransform({
    translate(
    left = cloud.xPos.toFloat(),
    top = cloud.yPos.toFloat()
    )
    })
    {
    drawPath(
    path = cloudState.cloudsList.first().path,
    color = Color(0xFFC5C5C5),
    style = Stroke(2f)
    )
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    27

    View full-size slide

  75. Game Loop in Compose
    Wait... One more thing!
    fun DrawScope.CloudsView(cloudState: CloudState)
    {
    cloudState.cloudsList.forEach {cloud ->
    withTransform({
    translate(
    left = cloud.xPos.toFloat(),
    top = cloud.yPos.toFloat()
    )
    })
    {
    drawPath(
    path = cloudState.cloudsList.first().path,
    color = Color(0xFFC5C5C5),
    style = Stroke(2f)
    )
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    withTransform({
    fun DrawScope.CloudsView(cloudState: CloudState)
    1
    {
    2
    cloudState.cloudsList.forEach {cloud ->
    3
    4
    translate(
    5
    left = cloud.xPos.toFloat(),
    6
    top = cloud.yPos.toFloat()
    7
    )
    8
    })
    9
    {
    10
    drawPath(
    11
    path = cloudState.cloudsList.first().path,
    12
    color = Color(0xFFC5C5C5),
    13
    style = Stroke(2f)
    14
    )
    15
    }
    16
    }
    17
    }
    18
    27

    View full-size slide

  76. Game Loop in Compose
    Wait... One more thing!
    fun DrawScope.CloudsView(cloudState: CloudState)
    {
    cloudState.cloudsList.forEach {cloud ->
    withTransform({
    translate(
    left = cloud.xPos.toFloat(),
    top = cloud.yPos.toFloat()
    )
    })
    {
    drawPath(
    path = cloudState.cloudsList.first().path,
    color = Color(0xFFC5C5C5),
    style = Stroke(2f)
    )
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    withTransform({
    fun DrawScope.CloudsView(cloudState: CloudState)
    1
    {
    2
    cloudState.cloudsList.forEach {cloud ->
    3
    4
    translate(
    5
    left = cloud.xPos.toFloat(),
    6
    top = cloud.yPos.toFloat()
    7
    )
    8
    })
    9
    {
    10
    drawPath(
    11
    path = cloudState.cloudsList.first().path,
    12
    color = Color(0xFFC5C5C5),
    13
    style = Stroke(2f)
    14
    )
    15
    }
    16
    }
    17
    }
    18
    translate(
    left = cloud.xPos.toFloat(),
    top = cloud.yPos.toFloat()
    )
    fun DrawScope.CloudsView(cloudState: CloudState)
    1
    {
    2
    cloudState.cloudsList.forEach {cloud ->
    3
    withTransform({
    4
    5
    6
    7
    8
    })
    9
    {
    10
    drawPath(
    11
    path = cloudState.cloudsList.first().path,
    12
    color = Color(0xFFC5C5C5),
    13
    style = Stroke(2f)
    14
    )
    15
    }
    16
    }
    17
    }
    18
    27

    View full-size slide

  77. Scrolling Earth
    Two blocks chasing each other
    28

    View full-size slide

  78. Scrolling Earth
    Two blocks chasing each other
    data class EarthState( /* ... */ ) {
    fun moveForward()
    {
    var endPos = blocksList[maxBlocks-1].xPos
    + blocksList[maxBlocks-1].size
    for (i in 0 until maxBlocks)
    {
    var block = blocksList[i]
    block.xPos -= speed
    // If first block reached end,
    // move it after second
    if ((block.xPos + block.size) < -EARTH_OFFSET ) {
    block.xPos = endPos
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    29

    View full-size slide

  79. Scrolling Earth
    Two blocks chasing each other
    data class EarthState( /* ... */ ) {
    fun moveForward()
    {
    var endPos = blocksList[maxBlocks-1].xPos
    + blocksList[maxBlocks-1].size
    for (i in 0 until maxBlocks)
    {
    var block = blocksList[i]
    block.xPos -= speed
    // If first block reached end,
    // move it after second
    if ((block.xPos + block.size) < -EARTH_OFFSET ) {
    block.xPos = endPos
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    var endPos = blocksList[maxBlocks-1].xPos
    + blocksList[maxBlocks-1].size
    data class EarthState( /* ... */ ) {
    1
    fun moveForward()
    2
    {
    3
    4
    5
    6
    for (i in 0 until maxBlocks)
    7
    {
    8
    var block = blocksList[i]
    9
    block.xPos -= speed
    10
    11
    // If first block reached end,
    12
    // move it after second
    13
    if ((block.xPos + block.size) < -EARTH_OFFSET ) {
    14
    block.xPos = endPos
    15
    }
    16
    }
    17
    }
    18
    }
    19
    29

    View full-size slide

  80. Scrolling Earth
    Two blocks chasing each other
    data class EarthState( /* ... */ ) {
    fun moveForward()
    {
    var endPos = blocksList[maxBlocks-1].xPos
    + blocksList[maxBlocks-1].size
    for (i in 0 until maxBlocks)
    {
    var block = blocksList[i]
    block.xPos -= speed
    // If first block reached end,
    // move it after second
    if ((block.xPos + block.size) < -EARTH_OFFSET ) {
    block.xPos = endPos
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    var endPos = blocksList[maxBlocks-1].xPos
    + blocksList[maxBlocks-1].size
    data class EarthState( /* ... */ ) {
    1
    fun moveForward()
    2
    {
    3
    4
    5
    6
    for (i in 0 until maxBlocks)
    7
    {
    8
    var block = blocksList[i]
    9
    block.xPos -= speed
    10
    11
    // If first block reached end,
    12
    // move it after second
    13
    if ((block.xPos + block.size) < -EARTH_OFFSET ) {
    14
    block.xPos = endPos
    15
    }
    16
    }
    17
    }
    18
    }
    19
    block.xPos -= speed
    data class EarthState( /* ... */ ) {
    1
    fun moveForward()
    2
    {
    3
    var endPos = blocksList[maxBlocks-1].xPos
    4
    + blocksList[maxBlocks-1].size
    5
    6
    for (i in 0 until maxBlocks)
    7
    {
    8
    var block = blocksList[i]
    9
    10
    11
    // If first block reached end,
    12
    // move it after second
    13
    if ((block.xPos + block.size) < -EARTH_OFFSET ) {
    14
    block.xPos = endPos
    15
    }
    16
    }
    17
    }
    18
    }
    19
    29

    View full-size slide

  81. Scrolling Earth
    Two blocks chasing each other
    data class EarthState( /* ... */ ) {
    fun moveForward()
    {
    var endPos = blocksList[maxBlocks-1].xPos
    + blocksList[maxBlocks-1].size
    for (i in 0 until maxBlocks)
    {
    var block = blocksList[i]
    block.xPos -= speed
    // If first block reached end,
    // move it after second
    if ((block.xPos + block.size) < -EARTH_OFFSET ) {
    block.xPos = endPos
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    var endPos = blocksList[maxBlocks-1].xPos
    + blocksList[maxBlocks-1].size
    data class EarthState( /* ... */ ) {
    1
    fun moveForward()
    2
    {
    3
    4
    5
    6
    for (i in 0 until maxBlocks)
    7
    {
    8
    var block = blocksList[i]
    9
    block.xPos -= speed
    10
    11
    // If first block reached end,
    12
    // move it after second
    13
    if ((block.xPos + block.size) < -EARTH_OFFSET ) {
    14
    block.xPos = endPos
    15
    }
    16
    }
    17
    }
    18
    }
    19
    block.xPos -= speed
    data class EarthState( /* ... */ ) {
    1
    fun moveForward()
    2
    {
    3
    var endPos = blocksList[maxBlocks-1].xPos
    4
    + blocksList[maxBlocks-1].size
    5
    6
    for (i in 0 until maxBlocks)
    7
    {
    8
    var block = blocksList[i]
    9
    10
    11
    // If first block reached end,
    12
    // move it after second
    13
    if ((block.xPos + block.size) < -EARTH_OFFSET ) {
    14
    block.xPos = endPos
    15
    }
    16
    }
    17
    }
    18
    }
    19
    // If first block reached end,
    // move it after second
    if ((block.xPos + block.size) < -EARTH_OFFSET ) {
    block.xPos = endPos
    }
    data class EarthState( /* ... */ ) {
    1
    fun moveForward()
    2
    {
    3
    var endPos = blocksList[maxBlocks-1].xPos
    4
    + blocksList[maxBlocks-1].size
    5
    6
    for (i in 0 until maxBlocks)
    7
    {
    8
    var block = blocksList[i]
    9
    block.xPos -= speed
    10
    11
    12
    13
    14
    15
    16
    }
    17
    }
    18
    }
    19
    29

    View full-size slide

  82. Clouds & Cactus Generation
    Getting spawned one by one
    data class CloudState( /* ... */ ) {
    fun moveForward()
    {
    for (i in 0 until maxClouds)
    {
    var cloud = cloudsList[i]
    cloud.xPos -= speed
    // If cloud is out of screen,
    // Reposition it at start
    if (cloud.xPos < -100) {
    cloud.xPos = rand(deviceWidthInPixels,
    deviceWidthInPixels * rand(1,2))
    cloud.yPos = rand(0, 100)
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    30

    View full-size slide

  83. Clouds & Cactus Generation
    Getting spawned one by one
    data class CloudState( /* ... */ ) {
    fun moveForward()
    {
    for (i in 0 until maxClouds)
    {
    var cloud = cloudsList[i]
    cloud.xPos -= speed
    // If cloud is out of screen,
    // Reposition it at start
    if (cloud.xPos < -100) {
    cloud.xPos = rand(deviceWidthInPixels,
    deviceWidthInPixels * rand(1,2))
    cloud.yPos = rand(0, 100)
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    cloud.xPos -= speed
    data class CloudState( /* ... */ ) {
    1
    fun moveForward()
    2
    {
    3
    for (i in 0 until maxClouds)
    4
    {
    5
    var cloud = cloudsList[i]
    6
    7
    8
    // If cloud is out of screen,
    9
    // Reposition it at start
    10
    if (cloud.xPos < -100) {
    11
    cloud.xPos = rand(deviceWidthInPixels,
    12
    deviceWidthInPixels * rand(1,2))
    13
    cloud.yPos = rand(0, 100)
    14
    }
    15
    }
    16
    }
    17
    }
    18
    30

    View full-size slide

  84. Clouds & Cactus Generation
    Getting spawned one by one
    data class CloudState( /* ... */ ) {
    fun moveForward()
    {
    for (i in 0 until maxClouds)
    {
    var cloud = cloudsList[i]
    cloud.xPos -= speed
    // If cloud is out of screen,
    // Reposition it at start
    if (cloud.xPos < -100) {
    cloud.xPos = rand(deviceWidthInPixels,
    deviceWidthInPixels * rand(1,2))
    cloud.yPos = rand(0, 100)
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    cloud.xPos -= speed
    data class CloudState( /* ... */ ) {
    1
    fun moveForward()
    2
    {
    3
    for (i in 0 until maxClouds)
    4
    {
    5
    var cloud = cloudsList[i]
    6
    7
    8
    // If cloud is out of screen,
    9
    // Reposition it at start
    10
    if (cloud.xPos < -100) {
    11
    cloud.xPos = rand(deviceWidthInPixels,
    12
    deviceWidthInPixels * rand(1,2))
    13
    cloud.yPos = rand(0, 100)
    14
    }
    15
    }
    16
    }
    17
    }
    18
    // If cloud is out of screen,
    // Reposition it at start
    if (cloud.xPos < -100) {
    cloud.xPos = rand(deviceWidthInPixels,
    deviceWidthInPixels * rand(1,2))
    cloud.yPos = rand(0, 100)
    }
    data class CloudState( /* ... */ ) {
    1
    fun moveForward()
    2
    {
    3
    for (i in 0 until maxClouds)
    4
    {
    5
    var cloud = cloudsList[i]
    6
    cloud.xPos -= speed
    7
    8
    9
    10
    11
    12
    13
    14
    15
    }
    16
    }
    17
    }
    18
    30

    View full-size slide

  85. Dino Jump
    Detecting Tap first
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = {
    if (!gameState.isGameOver)
    dinoState.jump()
    else
    {
    cactusState.initCactus()
    dinoState.init()
    gameState.replay()
    }
    },
    indication = null)
    ) {
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    31

    View full-size slide

  86. Dino Jump
    Detecting Tap first
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = {
    if (!gameState.isGameOver)
    dinoState.jump()
    else
    {
    cactusState.initCactus()
    dinoState.init()
    gameState.replay()
    }
    },
    indication = null)
    ) {
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = {
    2
    if (!gameState.isGameOver)
    3
    dinoState.jump()
    4
    else
    5
    {
    6
    cactusState.initCactus()
    7
    dinoState.init()
    8
    gameState.replay()
    9
    }
    10
    },
    11
    indication = null)
    12
    ) {
    13
    14
    15
    16
    17
    18
    19
    }
    20
    31

    View full-size slide

  87. Dino Jump
    Detecting Tap first
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = {
    if (!gameState.isGameOver)
    dinoState.jump()
    else
    {
    cactusState.initCactus()
    dinoState.init()
    gameState.replay()
    }
    },
    indication = null)
    ) {
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = {
    2
    if (!gameState.isGameOver)
    3
    dinoState.jump()
    4
    else
    5
    {
    6
    cactusState.initCactus()
    7
    dinoState.init()
    8
    gameState.replay()
    9
    }
    10
    },
    11
    indication = null)
    12
    ) {
    13
    14
    15
    16
    17
    18
    19
    }
    20
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = {
    indication = null)
    1
    2
    if (!gameState.isGameOver)
    3
    dinoState.jump()
    4
    else
    5
    {
    6
    cactusState.initCactus()
    7
    dinoState.init()
    8
    gameState.replay()
    9
    }
    10
    },
    11
    12
    ) {
    13
    Canvas(modifier = Modifier.weight(1f)) {
    14
    EarthView(earthState)
    15
    CloudsView(cloudsState)
    16
    DinoView(dinoState)
    17
    CactusView(cactusState)
    18
    }
    19
    }
    20
    31

    View full-size slide

  88. Dino Jump
    Detecting Tap first
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = {
    if (!gameState.isGameOver)
    dinoState.jump()
    else
    {
    cactusState.initCactus()
    dinoState.init()
    gameState.replay()
    }
    },
    indication = null)
    ) {
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = {
    2
    if (!gameState.isGameOver)
    3
    dinoState.jump()
    4
    else
    5
    {
    6
    cactusState.initCactus()
    7
    dinoState.init()
    8
    gameState.replay()
    9
    }
    10
    },
    11
    indication = null)
    12
    ) {
    13
    14
    15
    16
    17
    18
    19
    }
    20
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = {
    indication = null)
    1
    2
    if (!gameState.isGameOver)
    3
    dinoState.jump()
    4
    else
    5
    {
    6
    cactusState.initCactus()
    7
    dinoState.init()
    8
    gameState.replay()
    9
    }
    10
    },
    11
    12
    ) {
    13
    Canvas(modifier = Modifier.weight(1f)) {
    14
    EarthView(earthState)
    15
    CloudsView(cloudsState)
    16
    DinoView(dinoState)
    17
    CactusView(cactusState)
    18
    }
    19
    }
    20
    if (!gameState.isGameOver)
    dinoState.jump()
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = {
    2
    3
    4
    else
    5
    {
    6
    cactusState.initCactus()
    7
    dinoState.init()
    8
    gameState.replay()
    9
    }
    10
    },
    11
    indication = null)
    12
    ) {
    13
    Canvas(modifier = Modifier.weight(1f)) {
    14
    EarthView(earthState)
    15
    CloudsView(cloudsState)
    16
    DinoView(dinoState)
    17
    CactusView(cactusState)
    18
    }
    19
    }
    20
    31

    View full-size slide

  89. Dino Jump
    Detecting Tap first
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = {
    if (!gameState.isGameOver)
    dinoState.jump()
    else
    {
    cactusState.initCactus()
    dinoState.init()
    gameState.replay()
    }
    },
    indication = null)
    ) {
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = {
    2
    if (!gameState.isGameOver)
    3
    dinoState.jump()
    4
    else
    5
    {
    6
    cactusState.initCactus()
    7
    dinoState.init()
    8
    gameState.replay()
    9
    }
    10
    },
    11
    indication = null)
    12
    ) {
    13
    14
    15
    16
    17
    18
    19
    }
    20
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = {
    indication = null)
    1
    2
    if (!gameState.isGameOver)
    3
    dinoState.jump()
    4
    else
    5
    {
    6
    cactusState.initCactus()
    7
    dinoState.init()
    8
    gameState.replay()
    9
    }
    10
    },
    11
    12
    ) {
    13
    Canvas(modifier = Modifier.weight(1f)) {
    14
    EarthView(earthState)
    15
    CloudsView(cloudsState)
    16
    DinoView(dinoState)
    17
    CactusView(cactusState)
    18
    }
    19
    }
    20
    if (!gameState.isGameOver)
    dinoState.jump()
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = {
    2
    3
    4
    else
    5
    {
    6
    cactusState.initCactus()
    7
    dinoState.init()
    8
    gameState.replay()
    9
    }
    10
    },
    11
    indication = null)
    12
    ) {
    13
    Canvas(modifier = Modifier.weight(1f)) {
    14
    EarthView(earthState)
    15
    CloudsView(cloudsState)
    16
    DinoView(dinoState)
    17
    CactusView(cactusState)
    18
    }
    19
    }
    20
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = {
    if (!gameState.isGameOver)
    dinoState.jump()
    else
    {
    cactusState.initCactus()
    dinoState.init()
    gameState.replay()
    }
    },
    indication = null)
    ) {
    Canvas(modifier = Modifier.weight(1f)) {
    EarthView(earthState)
    CloudsView(cloudsState)
    DinoView(dinoState)
    CactusView(cactusState)
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    31

    View full-size slide

  90. Dino Jump
    Making Dino Jump
    data class DinoState( /* ... */ ) {
    fun move() {
    yPos += velocityY
    velocityY += gravity
    if (yPos > EARTH_Y_POSITION) {
    yPos = EARTH_Y_POSITION
    gravity = 0f
    velocityY = 0f
    isJumping = false
    }
    }
    fun jump() {
    // Adding negative force
    if (yPos == EARTH_Y_POSITION) {
    isJumping = true
    velocityY = -40f
    gravity = 3f
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    32

    View full-size slide

  91. Dino Jump
    Making Dino Jump
    data class DinoState( /* ... */ ) {
    fun move() {
    yPos += velocityY
    velocityY += gravity
    if (yPos > EARTH_Y_POSITION) {
    yPos = EARTH_Y_POSITION
    gravity = 0f
    velocityY = 0f
    isJumping = false
    }
    }
    fun jump() {
    // Adding negative force
    if (yPos == EARTH_Y_POSITION) {
    isJumping = true
    velocityY = -40f
    gravity = 3f
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    fun jump() {
    // Adding negative force
    if (yPos == EARTH_Y_POSITION) {
    isJumping = true
    velocityY = -40f
    gravity = 3f
    data class DinoState( /* ... */ ) {
    1
    fun move() {
    2
    yPos += velocityY
    3
    velocityY += gravity
    4
    5
    if (yPos > EARTH_Y_POSITION) {
    6
    yPos = EARTH_Y_POSITION
    7
    gravity = 0f
    8
    velocityY = 0f
    9
    isJumping = false
    10
    }
    11
    }
    12
    13
    14
    15
    16
    17
    18
    19
    }
    20
    }
    21
    }
    22
    32

    View full-size slide

  92. Dino Jump
    Making Dino Jump
    data class DinoState( /* ... */ ) {
    fun move() {
    yPos += velocityY
    velocityY += gravity
    if (yPos > EARTH_Y_POSITION) {
    yPos = EARTH_Y_POSITION
    gravity = 0f
    velocityY = 0f
    isJumping = false
    }
    }
    fun jump() {
    // Adding negative force
    if (yPos == EARTH_Y_POSITION) {
    isJumping = true
    velocityY = -40f
    gravity = 3f
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    fun jump() {
    // Adding negative force
    if (yPos == EARTH_Y_POSITION) {
    isJumping = true
    velocityY = -40f
    gravity = 3f
    data class DinoState( /* ... */ ) {
    1
    fun move() {
    2
    yPos += velocityY
    3
    velocityY += gravity
    4
    5
    if (yPos > EARTH_Y_POSITION) {
    6
    yPos = EARTH_Y_POSITION
    7
    gravity = 0f
    8
    velocityY = 0f
    9
    isJumping = false
    10
    }
    11
    }
    12
    13
    14
    15
    16
    17
    18
    19
    }
    20
    }
    21
    }
    22
    data class DinoState( /* ... */ ) {
    fun move() {
    }
    1
    2
    yPos += velocityY
    3
    velocityY += gravity
    4
    5
    if (yPos > EARTH_Y_POSITION) {
    6
    yPos = EARTH_Y_POSITION
    7
    gravity = 0f
    8
    velocityY = 0f
    9
    isJumping = false
    10
    }
    11
    12
    13
    fun jump() {
    14
    // Adding negative force
    15
    if (yPos == EARTH_Y_POSITION) {
    16
    isJumping = true
    17
    velocityY = -40f
    18
    gravity = 3f
    19
    }
    20
    }
    21
    }
    22
    32

    View full-size slide

  93. Dino Jump
    Making Dino Jump
    data class DinoState( /* ... */ ) {
    fun move() {
    yPos += velocityY
    velocityY += gravity
    if (yPos > EARTH_Y_POSITION) {
    yPos = EARTH_Y_POSITION
    gravity = 0f
    velocityY = 0f
    isJumping = false
    }
    }
    fun jump() {
    // Adding negative force
    if (yPos == EARTH_Y_POSITION) {
    isJumping = true
    velocityY = -40f
    gravity = 3f
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    fun jump() {
    // Adding negative force
    if (yPos == EARTH_Y_POSITION) {
    isJumping = true
    velocityY = -40f
    gravity = 3f
    data class DinoState( /* ... */ ) {
    1
    fun move() {
    2
    yPos += velocityY
    3
    velocityY += gravity
    4
    5
    if (yPos > EARTH_Y_POSITION) {
    6
    yPos = EARTH_Y_POSITION
    7
    gravity = 0f
    8
    velocityY = 0f
    9
    isJumping = false
    10
    }
    11
    }
    12
    13
    14
    15
    16
    17
    18
    19
    }
    20
    }
    21
    }
    22
    data class DinoState( /* ... */ ) {
    fun move() {
    }
    1
    2
    yPos += velocityY
    3
    velocityY += gravity
    4
    5
    if (yPos > EARTH_Y_POSITION) {
    6
    yPos = EARTH_Y_POSITION
    7
    gravity = 0f
    8
    velocityY = 0f
    9
    isJumping = false
    10
    }
    11
    12
    13
    fun jump() {
    14
    // Adding negative force
    15
    if (yPos == EARTH_Y_POSITION) {
    16
    isJumping = true
    17
    velocityY = -40f
    18
    gravity = 3f
    19
    }
    20
    }
    21
    }
    22
    yPos += velocityY
    velocityY += gravity
    data class DinoState( /* ... */ ) {
    1
    fun move() {
    2
    3
    4
    5
    if (yPos > EARTH_Y_POSITION) {
    6
    yPos = EARTH_Y_POSITION
    7
    gravity = 0f
    8
    velocityY = 0f
    9
    isJumping = false
    10
    }
    11
    }
    12
    13
    fun jump() {
    14
    // Adding negative force
    15
    if (yPos == EARTH_Y_POSITION) {
    16
    isJumping = true
    17
    velocityY = -40f
    18
    gravity = 3f
    19
    }
    20
    }
    21
    }
    22
    32

    View full-size slide

  94. Dino Jump
    Making Dino Jump
    data class DinoState( /* ... */ ) {
    fun move() {
    yPos += velocityY
    velocityY += gravity
    if (yPos > EARTH_Y_POSITION) {
    yPos = EARTH_Y_POSITION
    gravity = 0f
    velocityY = 0f
    isJumping = false
    }
    }
    fun jump() {
    // Adding negative force
    if (yPos == EARTH_Y_POSITION) {
    isJumping = true
    velocityY = -40f
    gravity = 3f
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    fun jump() {
    // Adding negative force
    if (yPos == EARTH_Y_POSITION) {
    isJumping = true
    velocityY = -40f
    gravity = 3f
    data class DinoState( /* ... */ ) {
    1
    fun move() {
    2
    yPos += velocityY
    3
    velocityY += gravity
    4
    5
    if (yPos > EARTH_Y_POSITION) {
    6
    yPos = EARTH_Y_POSITION
    7
    gravity = 0f
    8
    velocityY = 0f
    9
    isJumping = false
    10
    }
    11
    }
    12
    13
    14
    15
    16
    17
    18
    19
    }
    20
    }
    21
    }
    22
    data class DinoState( /* ... */ ) {
    fun move() {
    }
    1
    2
    yPos += velocityY
    3
    velocityY += gravity
    4
    5
    if (yPos > EARTH_Y_POSITION) {
    6
    yPos = EARTH_Y_POSITION
    7
    gravity = 0f
    8
    velocityY = 0f
    9
    isJumping = false
    10
    }
    11
    12
    13
    fun jump() {
    14
    // Adding negative force
    15
    if (yPos == EARTH_Y_POSITION) {
    16
    isJumping = true
    17
    velocityY = -40f
    18
    gravity = 3f
    19
    }
    20
    }
    21
    }
    22
    yPos += velocityY
    velocityY += gravity
    data class DinoState( /* ... */ ) {
    1
    fun move() {
    2
    3
    4
    5
    if (yPos > EARTH_Y_POSITION) {
    6
    yPos = EARTH_Y_POSITION
    7
    gravity = 0f
    8
    velocityY = 0f
    9
    isJumping = false
    10
    }
    11
    }
    12
    13
    fun jump() {
    14
    // Adding negative force
    15
    if (yPos == EARTH_Y_POSITION) {
    16
    isJumping = true
    17
    velocityY = -40f
    18
    gravity = 3f
    19
    }
    20
    }
    21
    }
    22
    data class DinoState( /* ... */ ) {
    fun move() {
    yPos += velocityY
    velocityY += gravity
    if (yPos > EARTH_Y_POSITION) {
    yPos = EARTH_Y_POSITION
    gravity = 0f
    velocityY = 0f
    isJumping = false
    }
    }
    fun jump() {
    // Adding negative force
    if (yPos == EARTH_Y_POSITION) {
    isJumping = true
    velocityY = -40f
    gravity = 3f
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    32

    View full-size slide

  95. Dino Running Steps
    Keyframes in Jetpack Compose
    data class DinoState(
    // ....
    var keyframe: Int = 0,
    private var pathList: ArrayList = arrayListOf(),
    ) {
    // Current Vector Path of keyframe
    val path: Path
    get() = if (keyframe == 0) pathList[0] else pathList[1]
    fun changeKeyframe()
    {
    keyframe++
    if (keyframe == 1)
    keyframe = 0
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    33

    View full-size slide

  96. Dino Running Steps
    Keyframes in Jetpack Compose
    data class DinoState(
    // ....
    var keyframe: Int = 0,
    private var pathList: ArrayList = arrayListOf(),
    ) {
    // Current Vector Path of keyframe
    val path: Path
    get() = if (keyframe == 0) pathList[0] else pathList[1]
    fun changeKeyframe()
    {
    keyframe++
    if (keyframe == 1)
    keyframe = 0
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var keyframe: Int = 0,
    private var pathList: ArrayList = arrayListOf(),
    data class DinoState(
    1
    // ....
    2
    3
    4
    ) {
    5
    // Current Vector Path of keyframe
    6
    val path: Path
    7
    get() = if (keyframe == 0) pathList[0] else pathList[1]
    8
    9
    fun changeKeyframe()
    10
    {
    11
    keyframe++
    12
    if (keyframe == 1)
    13
    keyframe = 0
    14
    }
    15
    }
    16
    33

    View full-size slide

  97. Dino Running Steps
    Keyframes in Jetpack Compose
    data class DinoState(
    // ....
    var keyframe: Int = 0,
    private var pathList: ArrayList = arrayListOf(),
    ) {
    // Current Vector Path of keyframe
    val path: Path
    get() = if (keyframe == 0) pathList[0] else pathList[1]
    fun changeKeyframe()
    {
    keyframe++
    if (keyframe == 1)
    keyframe = 0
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var keyframe: Int = 0,
    private var pathList: ArrayList = arrayListOf(),
    data class DinoState(
    1
    // ....
    2
    3
    4
    ) {
    5
    // Current Vector Path of keyframe
    6
    val path: Path
    7
    get() = if (keyframe == 0) pathList[0] else pathList[1]
    8
    9
    fun changeKeyframe()
    10
    {
    11
    keyframe++
    12
    if (keyframe == 1)
    13
    keyframe = 0
    14
    }
    15
    }
    16
    val path: Path
    get() = if (keyframe == 0) pathList[0] else pathList[1]
    data class DinoState(
    1
    // ....
    2
    var keyframe: Int = 0,
    3
    private var pathList: ArrayList = arrayListOf(),
    4
    ) {
    5
    // Current Vector Path of keyframe
    6
    7
    8
    9
    fun changeKeyframe()
    10
    {
    11
    keyframe++
    12
    if (keyframe == 1)
    13
    keyframe = 0
    14
    }
    15
    }
    16
    33

    View full-size slide

  98. Dino Running Steps
    Keyframes in Jetpack Compose
    data class DinoState(
    // ....
    var keyframe: Int = 0,
    private var pathList: ArrayList = arrayListOf(),
    ) {
    // Current Vector Path of keyframe
    val path: Path
    get() = if (keyframe == 0) pathList[0] else pathList[1]
    fun changeKeyframe()
    {
    keyframe++
    if (keyframe == 1)
    keyframe = 0
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var keyframe: Int = 0,
    private var pathList: ArrayList = arrayListOf(),
    data class DinoState(
    1
    // ....
    2
    3
    4
    ) {
    5
    // Current Vector Path of keyframe
    6
    val path: Path
    7
    get() = if (keyframe == 0) pathList[0] else pathList[1]
    8
    9
    fun changeKeyframe()
    10
    {
    11
    keyframe++
    12
    if (keyframe == 1)
    13
    keyframe = 0
    14
    }
    15
    }
    16
    val path: Path
    get() = if (keyframe == 0) pathList[0] else pathList[1]
    data class DinoState(
    1
    // ....
    2
    var keyframe: Int = 0,
    3
    private var pathList: ArrayList = arrayListOf(),
    4
    ) {
    5
    // Current Vector Path of keyframe
    6
    7
    8
    9
    fun changeKeyframe()
    10
    {
    11
    keyframe++
    12
    if (keyframe == 1)
    13
    keyframe = 0
    14
    }
    15
    }
    16
    fun changeKeyframe()
    {
    keyframe++
    if (keyframe == 1)
    keyframe = 0
    }
    data class DinoState(
    1
    // ....
    2
    var keyframe: Int = 0,
    3
    private var pathList: ArrayList = arrayListOf(),
    4
    ) {
    5
    // Current Vector Path of keyframe
    6
    val path: Path
    7
    get() = if (keyframe == 0) pathList[0] else pathList[1]
    8
    9
    10
    11
    12
    13
    14
    15
    }
    16
    33

    View full-size slide

  99. Collision Detection
    Debugging with Bounding Box
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    if (showBounds.value)
    {
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    rect.deflate(DOUBT_FACTOR).size,
    style = Stroke(width = 3f,
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    )
    }
    }
    fun DrawScope.CactusView(cactusState: CactusState) {
    // ....
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    }
    fun DrawScope.DinoView(dinoState: DinoState) {
    // ....
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    34

    View full-size slide

  100. Collision Detection
    Debugging with Bounding Box
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    if (showBounds.value)
    {
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    rect.deflate(DOUBT_FACTOR).size,
    style = Stroke(width = 3f,
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    )
    }
    }
    fun DrawScope.CactusView(cactusState: CactusState) {
    // ....
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    }
    fun DrawScope.DinoView(dinoState: DinoState) {
    // ....
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    1
    if (showBounds.value)
    2
    {
    3
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    4
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    5
    rect.deflate(DOUBT_FACTOR).size,
    6
    style = Stroke(width = 3f,
    7
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    8
    )
    9
    }
    10
    }
    11
    12
    fun DrawScope.CactusView(cactusState: CactusState) {
    13
    // ....
    14
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    15
    }
    16
    17
    fun DrawScope.DinoView(dinoState: DinoState) {
    18
    // ....
    19
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    20
    }
    21
    34

    View full-size slide

  101. Collision Detection
    Debugging with Bounding Box
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    if (showBounds.value)
    {
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    rect.deflate(DOUBT_FACTOR).size,
    style = Stroke(width = 3f,
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    )
    }
    }
    fun DrawScope.CactusView(cactusState: CactusState) {
    // ....
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    }
    fun DrawScope.DinoView(dinoState: DinoState) {
    // ....
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    1
    if (showBounds.value)
    2
    {
    3
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    4
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    5
    rect.deflate(DOUBT_FACTOR).size,
    6
    style = Stroke(width = 3f,
    7
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    8
    )
    9
    }
    10
    }
    11
    12
    fun DrawScope.CactusView(cactusState: CactusState) {
    13
    // ....
    14
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    15
    }
    16
    17
    fun DrawScope.DinoView(dinoState: DinoState) {
    18
    // ....
    19
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    20
    }
    21
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    1
    if (showBounds.value)
    2
    {
    3
    4
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    5
    rect.deflate(DOUBT_FACTOR).size,
    6
    style = Stroke(width = 3f,
    7
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    8
    )
    9
    }
    10
    }
    11
    12
    fun DrawScope.CactusView(cactusState: CactusState) {
    13
    // ....
    14
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    15
    }
    16
    17
    fun DrawScope.DinoView(dinoState: DinoState) {
    18
    // ....
    19
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    20
    }
    21
    34

    View full-size slide

  102. Collision Detection
    Debugging with Bounding Box
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    if (showBounds.value)
    {
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    rect.deflate(DOUBT_FACTOR).size,
    style = Stroke(width = 3f,
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    )
    }
    }
    fun DrawScope.CactusView(cactusState: CactusState) {
    // ....
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    }
    fun DrawScope.DinoView(dinoState: DinoState) {
    // ....
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    1
    if (showBounds.value)
    2
    {
    3
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    4
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    5
    rect.deflate(DOUBT_FACTOR).size,
    6
    style = Stroke(width = 3f,
    7
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    8
    )
    9
    }
    10
    }
    11
    12
    fun DrawScope.CactusView(cactusState: CactusState) {
    13
    // ....
    14
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    15
    }
    16
    17
    fun DrawScope.DinoView(dinoState: DinoState) {
    18
    // ....
    19
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    20
    }
    21
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    1
    if (showBounds.value)
    2
    {
    3
    4
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    5
    rect.deflate(DOUBT_FACTOR).size,
    6
    style = Stroke(width = 3f,
    7
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    8
    )
    9
    }
    10
    }
    11
    12
    fun DrawScope.CactusView(cactusState: CactusState) {
    13
    // ....
    14
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    15
    }
    16
    17
    fun DrawScope.DinoView(dinoState: DinoState) {
    18
    // ....
    19
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    20
    }
    21
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    rect.deflate(DOUBT_FACTOR).size,
    style = Stroke(width = 3f,
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    )
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    1
    if (showBounds.value)
    2
    {
    3
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    4
    5
    6
    7
    8
    9
    }
    10
    }
    11
    12
    fun DrawScope.CactusView(cactusState: CactusState) {
    13
    // ....
    14
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    15
    }
    16
    17
    fun DrawScope.DinoView(dinoState: DinoState) {
    18
    // ....
    19
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    20
    }
    21
    34

    View full-size slide

  103. Collision Detection
    Debugging with Bounding Box
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    if (showBounds.value)
    {
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    rect.deflate(DOUBT_FACTOR).size,
    style = Stroke(width = 3f,
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    )
    }
    }
    fun DrawScope.CactusView(cactusState: CactusState) {
    // ....
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    }
    fun DrawScope.DinoView(dinoState: DinoState) {
    // ....
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    1
    if (showBounds.value)
    2
    {
    3
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    4
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    5
    rect.deflate(DOUBT_FACTOR).size,
    6
    style = Stroke(width = 3f,
    7
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    8
    )
    9
    }
    10
    }
    11
    12
    fun DrawScope.CactusView(cactusState: CactusState) {
    13
    // ....
    14
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    15
    }
    16
    17
    fun DrawScope.DinoView(dinoState: DinoState) {
    18
    // ....
    19
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    20
    }
    21
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    1
    if (showBounds.value)
    2
    {
    3
    4
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    5
    rect.deflate(DOUBT_FACTOR).size,
    6
    style = Stroke(width = 3f,
    7
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    8
    )
    9
    }
    10
    }
    11
    12
    fun DrawScope.CactusView(cactusState: CactusState) {
    13
    // ....
    14
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    15
    }
    16
    17
    fun DrawScope.DinoView(dinoState: DinoState) {
    18
    // ....
    19
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    20
    }
    21
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    rect.deflate(DOUBT_FACTOR).size,
    style = Stroke(width = 3f,
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    )
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    1
    if (showBounds.value)
    2
    {
    3
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    4
    5
    6
    7
    8
    9
    }
    10
    }
    11
    12
    fun DrawScope.CactusView(cactusState: CactusState) {
    13
    // ....
    14
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    15
    }
    16
    17
    fun DrawScope.DinoView(dinoState: DinoState) {
    18
    // ....
    19
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    20
    }
    21
    fun DrawScope.CactusView(cactusState: CactusState) {
    // ....
    drawBoundingBox(color = Color.Red, rect = cactus.path.getBounds())
    }
    fun DrawScope.drawBoundingBox(color: Color, rect: Rect) {
    1
    if (showBounds.value)
    2
    {
    3
    drawRect(color, rect.topLeft, rect.size, style = Stroke(3f))
    4
    drawRect(color, rect.deflate(DOUBT_FACTOR).topLeft,
    5
    rect.deflate(DOUBT_FACTOR).size,
    6
    style = Stroke(width = 3f,
    7
    pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f))
    8
    )
    9
    }
    10
    }
    11
    12
    13
    14
    15
    16
    17
    fun DrawScope.DinoView(dinoState: DinoState) {
    18
    // ....
    19
    drawBoundingBox(color = Color.Green, rect = dino.path.getBounds())
    20
    }
    21
    34

    View full-size slide

  104. Collision Detection
    Calculating Overlap
    val state = animationTimeMillis {
    if (!gameState.isGameOver)
    {
    // Game Loop
    gameState.increaseScore()
    cloudsState.moveForward()
    earthState.moveForward()
    cactusState.moveForward()
    dinoState.move()
    // Collision Check
    cactusState.cactusList.forEach {
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    it.getBounds().deflate(DOUBT_FACTOR))) {
    gameState.isGameOver = true
    return@forEach
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    35

    View full-size slide

  105. Collision Detection
    Calculating Overlap
    val state = animationTimeMillis {
    if (!gameState.isGameOver)
    {
    // Game Loop
    gameState.increaseScore()
    cloudsState.moveForward()
    earthState.moveForward()
    cactusState.moveForward()
    dinoState.move()
    // Collision Check
    cactusState.cactusList.forEach {
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    it.getBounds().deflate(DOUBT_FACTOR))) {
    gameState.isGameOver = true
    return@forEach
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    val state = animationTimeMillis {
    1
    if (!gameState.isGameOver)
    2
    {
    3
    // Game Loop
    4
    gameState.increaseScore()
    5
    cloudsState.moveForward()
    6
    earthState.moveForward()
    7
    cactusState.moveForward()
    8
    dinoState.move()
    9
    10
    // Collision Check
    11
    cactusState.cactusList.forEach {
    12
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    13
    it.getBounds().deflate(DOUBT_FACTOR))) {
    14
    gameState.isGameOver = true
    15
    return@forEach
    16
    }
    17
    }
    18
    }
    19
    }
    20
    35

    View full-size slide

  106. Collision Detection
    Calculating Overlap
    val state = animationTimeMillis {
    if (!gameState.isGameOver)
    {
    // Game Loop
    gameState.increaseScore()
    cloudsState.moveForward()
    earthState.moveForward()
    cactusState.moveForward()
    dinoState.move()
    // Collision Check
    cactusState.cactusList.forEach {
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    it.getBounds().deflate(DOUBT_FACTOR))) {
    gameState.isGameOver = true
    return@forEach
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    val state = animationTimeMillis {
    1
    if (!gameState.isGameOver)
    2
    {
    3
    // Game Loop
    4
    gameState.increaseScore()
    5
    cloudsState.moveForward()
    6
    earthState.moveForward()
    7
    cactusState.moveForward()
    8
    dinoState.move()
    9
    10
    // Collision Check
    11
    cactusState.cactusList.forEach {
    12
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    13
    it.getBounds().deflate(DOUBT_FACTOR))) {
    14
    gameState.isGameOver = true
    15
    return@forEach
    16
    }
    17
    }
    18
    }
    19
    }
    20
    // Game Loop
    val state = animationTimeMillis {
    1
    if (!gameState.isGameOver)
    2
    {
    3
    4
    gameState.increaseScore()
    5
    cloudsState.moveForward()
    6
    earthState.moveForward()
    7
    cactusState.moveForward()
    8
    dinoState.move()
    9
    10
    // Collision Check
    11
    cactusState.cactusList.forEach {
    12
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    13
    it.getBounds().deflate(DOUBT_FACTOR))) {
    14
    gameState.isGameOver = true
    15
    return@forEach
    16
    }
    17
    }
    18
    }
    19
    }
    20
    35

    View full-size slide

  107. Collision Detection
    Calculating Overlap
    val state = animationTimeMillis {
    if (!gameState.isGameOver)
    {
    // Game Loop
    gameState.increaseScore()
    cloudsState.moveForward()
    earthState.moveForward()
    cactusState.moveForward()
    dinoState.move()
    // Collision Check
    cactusState.cactusList.forEach {
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    it.getBounds().deflate(DOUBT_FACTOR))) {
    gameState.isGameOver = true
    return@forEach
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    val state = animationTimeMillis {
    1
    if (!gameState.isGameOver)
    2
    {
    3
    // Game Loop
    4
    gameState.increaseScore()
    5
    cloudsState.moveForward()
    6
    earthState.moveForward()
    7
    cactusState.moveForward()
    8
    dinoState.move()
    9
    10
    // Collision Check
    11
    cactusState.cactusList.forEach {
    12
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    13
    it.getBounds().deflate(DOUBT_FACTOR))) {
    14
    gameState.isGameOver = true
    15
    return@forEach
    16
    }
    17
    }
    18
    }
    19
    }
    20
    // Game Loop
    val state = animationTimeMillis {
    1
    if (!gameState.isGameOver)
    2
    {
    3
    4
    gameState.increaseScore()
    5
    cloudsState.moveForward()
    6
    earthState.moveForward()
    7
    cactusState.moveForward()
    8
    dinoState.move()
    9
    10
    // Collision Check
    11
    cactusState.cactusList.forEach {
    12
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    13
    it.getBounds().deflate(DOUBT_FACTOR))) {
    14
    gameState.isGameOver = true
    15
    return@forEach
    16
    }
    17
    }
    18
    }
    19
    }
    20
    gameState.increaseScore()
    cloudsState.moveForward()
    earthState.moveForward()
    cactusState.moveForward()
    dinoState.move()
    val state = animationTimeMillis {
    1
    if (!gameState.isGameOver)
    2
    {
    3
    // Game Loop
    4
    5
    6
    7
    8
    9
    10
    // Collision Check
    11
    cactusState.cactusList.forEach {
    12
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    13
    it.getBounds().deflate(DOUBT_FACTOR))) {
    14
    gameState.isGameOver = true
    15
    return@forEach
    16
    }
    17
    }
    18
    }
    19
    }
    20
    35

    View full-size slide

  108. Collision Detection
    Calculating Overlap
    val state = animationTimeMillis {
    if (!gameState.isGameOver)
    {
    // Game Loop
    gameState.increaseScore()
    cloudsState.moveForward()
    earthState.moveForward()
    cactusState.moveForward()
    dinoState.move()
    // Collision Check
    cactusState.cactusList.forEach {
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    it.getBounds().deflate(DOUBT_FACTOR))) {
    gameState.isGameOver = true
    return@forEach
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    val state = animationTimeMillis {
    1
    if (!gameState.isGameOver)
    2
    {
    3
    // Game Loop
    4
    gameState.increaseScore()
    5
    cloudsState.moveForward()
    6
    earthState.moveForward()
    7
    cactusState.moveForward()
    8
    dinoState.move()
    9
    10
    // Collision Check
    11
    cactusState.cactusList.forEach {
    12
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    13
    it.getBounds().deflate(DOUBT_FACTOR))) {
    14
    gameState.isGameOver = true
    15
    return@forEach
    16
    }
    17
    }
    18
    }
    19
    }
    20
    // Game Loop
    val state = animationTimeMillis {
    1
    if (!gameState.isGameOver)
    2
    {
    3
    4
    gameState.increaseScore()
    5
    cloudsState.moveForward()
    6
    earthState.moveForward()
    7
    cactusState.moveForward()
    8
    dinoState.move()
    9
    10
    // Collision Check
    11
    cactusState.cactusList.forEach {
    12
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    13
    it.getBounds().deflate(DOUBT_FACTOR))) {
    14
    gameState.isGameOver = true
    15
    return@forEach
    16
    }
    17
    }
    18
    }
    19
    }
    20
    gameState.increaseScore()
    cloudsState.moveForward()
    earthState.moveForward()
    cactusState.moveForward()
    dinoState.move()
    val state = animationTimeMillis {
    1
    if (!gameState.isGameOver)
    2
    {
    3
    // Game Loop
    4
    5
    6
    7
    8
    9
    10
    // Collision Check
    11
    cactusState.cactusList.forEach {
    12
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    13
    it.getBounds().deflate(DOUBT_FACTOR))) {
    14
    gameState.isGameOver = true
    15
    return@forEach
    16
    }
    17
    }
    18
    }
    19
    }
    20
    if (dinoState.getBounds().deflate(DOUBT_FACTOR).overlaps(
    it.getBounds().deflate(DOUBT_FACTOR))) {
    gameState.isGameOver = true
    return@forEach
    val state = animationTimeMillis {
    1
    if (!gameState.isGameOver)
    2
    {
    3
    // Game Loop
    4
    gameState.increaseScore()
    5
    cloudsState.moveForward()
    6
    earthState.moveForward()
    7
    cactusState.moveForward()
    8
    dinoState.move()
    9
    10
    // Collision Check
    11
    cactusState.cactusList.forEach {
    12
    13
    14
    15
    16
    }
    17
    }
    18
    }
    19
    }
    20
    35

    View full-size slide

  109. Scoring
    Updating Scores
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = { /* ... */ },
    indication = null)
    ) {
    HighScoreTextViews(gameState)
    Canvas(modifier = Modifier.weight(1f)) {
    // Game drawing
    }
    }
    @Composable
    fun HighScoreTextViews(gameState: GameState)
    {
    Spacer(modifier = Modifier.padding(top = 50.dp))
    Row(
    modifier = Modifier.fillMaxWidth().padding(end = 20.dp),
    horizontalArrangement = Arrangement.End
    ) {
    Text(text = "HI")
    Spacer(modifier = Modifier.padding(start = 10.dp))
    Text(text = "${gameState.highScore}".padStart(5, '0'))
    Spacer(modifier = Modifier.padding(start = 10.dp))
    Text(text = "${gameState.currentScore}".padStart(5, '0'))
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    36

    View full-size slide

  110. Scoring
    Updating Scores
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = { /* ... */ },
    indication = null)
    ) {
    HighScoreTextViews(gameState)
    Canvas(modifier = Modifier.weight(1f)) {
    // Game drawing
    }
    }
    @Composable
    fun HighScoreTextViews(gameState: GameState)
    {
    Spacer(modifier = Modifier.padding(top = 50.dp))
    Row(
    modifier = Modifier.fillMaxWidth().padding(end = 20.dp),
    horizontalArrangement = Arrangement.End
    ) {
    Text(text = "HI")
    Spacer(modifier = Modifier.padding(start = 10.dp))
    Text(text = "${gameState.highScore}".padStart(5, '0'))
    Spacer(modifier = Modifier.padding(start = 10.dp))
    Text(text = "${gameState.currentScore}".padStart(5, '0'))
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    HighScoreTextViews(gameState)
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = { /* ... */ },
    2
    indication = null)
    3
    ) {
    4
    5
    Canvas(modifier = Modifier.weight(1f)) {
    6
    // Game drawing
    7
    }
    8
    }
    9
    10
    @Composable
    11
    fun HighScoreTextViews(gameState: GameState)
    12
    {
    13
    Spacer(modifier = Modifier.padding(top = 50.dp))
    14
    Row(
    15
    modifier = Modifier.fillMaxWidth().padding(end = 20.dp),
    16
    horizontalArrangement = Arrangement.End
    17
    ) {
    18
    Text(text = "HI")
    19
    Spacer(modifier = Modifier.padding(start = 10.dp))
    20
    Text(text = "${gameState.highScore}".padStart(5, '0'))
    21
    Spacer(modifier = Modifier.padding(start = 10.dp))
    22
    Text(text = "${gameState.currentScore}".padStart(5, '0'))
    23
    }
    24
    }
    25
    36

    View full-size slide

  111. Scoring
    Updating Scores
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = { /* ... */ },
    indication = null)
    ) {
    HighScoreTextViews(gameState)
    Canvas(modifier = Modifier.weight(1f)) {
    // Game drawing
    }
    }
    @Composable
    fun HighScoreTextViews(gameState: GameState)
    {
    Spacer(modifier = Modifier.padding(top = 50.dp))
    Row(
    modifier = Modifier.fillMaxWidth().padding(end = 20.dp),
    horizontalArrangement = Arrangement.End
    ) {
    Text(text = "HI")
    Spacer(modifier = Modifier.padding(start = 10.dp))
    Text(text = "${gameState.highScore}".padStart(5, '0'))
    Spacer(modifier = Modifier.padding(start = 10.dp))
    Text(text = "${gameState.currentScore}".padStart(5, '0'))
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    HighScoreTextViews(gameState)
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = { /* ... */ },
    2
    indication = null)
    3
    ) {
    4
    5
    Canvas(modifier = Modifier.weight(1f)) {
    6
    // Game drawing
    7
    }
    8
    }
    9
    10
    @Composable
    11
    fun HighScoreTextViews(gameState: GameState)
    12
    {
    13
    Spacer(modifier = Modifier.padding(top = 50.dp))
    14
    Row(
    15
    modifier = Modifier.fillMaxWidth().padding(end = 20.dp),
    16
    horizontalArrangement = Arrangement.End
    17
    ) {
    18
    Text(text = "HI")
    19
    Spacer(modifier = Modifier.padding(start = 10.dp))
    20
    Text(text = "${gameState.highScore}".padStart(5, '0'))
    21
    Spacer(modifier = Modifier.padding(start = 10.dp))
    22
    Text(text = "${gameState.currentScore}".padStart(5, '0'))
    23
    }
    24
    }
    25
    fun HighScoreTextViews(gameState: GameState)
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = { /* ... */ },
    2
    indication = null)
    3
    ) {
    4
    HighScoreTextViews(gameState)
    5
    Canvas(modifier = Modifier.weight(1f)) {
    6
    // Game drawing
    7
    }
    8
    }
    9
    10
    @Composable
    11
    12
    {
    13
    Spacer(modifier = Modifier.padding(top = 50.dp))
    14
    Row(
    15
    modifier = Modifier.fillMaxWidth().padding(end = 20.dp),
    16
    horizontalArrangement = Arrangement.End
    17
    ) {
    18
    Text(text = "HI")
    19
    Spacer(modifier = Modifier.padding(start = 10.dp))
    20
    Text(text = "${gameState.highScore}".padStart(5, '0'))
    21
    Spacer(modifier = Modifier.padding(start = 10.dp))
    22
    Text(text = "${gameState.currentScore}".padStart(5, '0'))
    23
    }
    24
    }
    25
    36

    View full-size slide

  112. Scoring
    Updating Scores
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = { /* ... */ },
    indication = null)
    ) {
    HighScoreTextViews(gameState)
    Canvas(modifier = Modifier.weight(1f)) {
    // Game drawing
    }
    }
    @Composable
    fun HighScoreTextViews(gameState: GameState)
    {
    Spacer(modifier = Modifier.padding(top = 50.dp))
    Row(
    modifier = Modifier.fillMaxWidth().padding(end = 20.dp),
    horizontalArrangement = Arrangement.End
    ) {
    Text(text = "HI")
    Spacer(modifier = Modifier.padding(start = 10.dp))
    Text(text = "${gameState.highScore}".padStart(5, '0'))
    Spacer(modifier = Modifier.padding(start = 10.dp))
    Text(text = "${gameState.currentScore}".padStart(5, '0'))
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    HighScoreTextViews(gameState)
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = { /* ... */ },
    2
    indication = null)
    3
    ) {
    4
    5
    Canvas(modifier = Modifier.weight(1f)) {
    6
    // Game drawing
    7
    }
    8
    }
    9
    10
    @Composable
    11
    fun HighScoreTextViews(gameState: GameState)
    12
    {
    13
    Spacer(modifier = Modifier.padding(top = 50.dp))
    14
    Row(
    15
    modifier = Modifier.fillMaxWidth().padding(end = 20.dp),
    16
    horizontalArrangement = Arrangement.End
    17
    ) {
    18
    Text(text = "HI")
    19
    Spacer(modifier = Modifier.padding(start = 10.dp))
    20
    Text(text = "${gameState.highScore}".padStart(5, '0'))
    21
    Spacer(modifier = Modifier.padding(start = 10.dp))
    22
    Text(text = "${gameState.currentScore}".padStart(5, '0'))
    23
    }
    24
    }
    25
    fun HighScoreTextViews(gameState: GameState)
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = { /* ... */ },
    2
    indication = null)
    3
    ) {
    4
    HighScoreTextViews(gameState)
    5
    Canvas(modifier = Modifier.weight(1f)) {
    6
    // Game drawing
    7
    }
    8
    }
    9
    10
    @Composable
    11
    12
    {
    13
    Spacer(modifier = Modifier.padding(top = 50.dp))
    14
    Row(
    15
    modifier = Modifier.fillMaxWidth().padding(end = 20.dp),
    16
    horizontalArrangement = Arrangement.End
    17
    ) {
    18
    Text(text = "HI")
    19
    Spacer(modifier = Modifier.padding(start = 10.dp))
    20
    Text(text = "${gameState.highScore}".padStart(5, '0'))
    21
    Spacer(modifier = Modifier.padding(start = 10.dp))
    22
    Text(text = "${gameState.currentScore}".padStart(5, '0'))
    23
    }
    24
    }
    25
    Spacer(modifier = Modifier.padding(top = 50.dp))
    Row(
    modifier = Modifier.fillMaxWidth().padding(end = 20.dp),
    horizontalArrangement = Arrangement.End
    ) {
    Text(text = "HI")
    Spacer(modifier = Modifier.padding(start = 10.dp))
    Text(text = "${gameState.highScore}".padStart(5, '0'))
    Spacer(modifier = Modifier.padding(start = 10.dp))
    Text(text = "${gameState.currentScore}".padStart(5, '0'))
    }
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = { /* ... */ },
    2
    indication = null)
    3
    ) {
    4
    HighScoreTextViews(gameState)
    5
    Canvas(modifier = Modifier.weight(1f)) {
    6
    // Game drawing
    7
    }
    8
    }
    9
    10
    @Composable
    11
    fun HighScoreTextViews(gameState: GameState)
    12
    {
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    }
    25
    36

    View full-size slide

  113. Game Over
    Finally...
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = { /* ... */ },
    indication = null)
    ) {
    HighScoreTextViews(gameState)
    Canvas(modifier = Modifier.weight(1f)) {
    // Game drawing
    }
    GameOverTextView(gameState.isGameOver,
    modifier = Modifier.align(Alignment.Center))
    }
    @Composable
    fun GameOverTextView(isGameOver: Boolean = true, modifier: Modifier = Mod
    {
    Column(modifier = modifier) {
    Text(
    text = if (isGameOver) "GAME OVER" else "",
    // ... More attributes
    )
    if (isGameOver) {
    Image(
    asset = vectorResource(id = R.drawable.ic_replay),
    modifier = Modifier.preferredSize(40.dp)
    .align(alignment = Alignment.CenterHorizontally)
    )
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    37

    View full-size slide

  114. Game Over
    Finally...
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = { /* ... */ },
    indication = null)
    ) {
    HighScoreTextViews(gameState)
    Canvas(modifier = Modifier.weight(1f)) {
    // Game drawing
    }
    GameOverTextView(gameState.isGameOver,
    modifier = Modifier.align(Alignment.Center))
    }
    @Composable
    fun GameOverTextView(isGameOver: Boolean = true, modifier: Modifier = Mod
    {
    Column(modifier = modifier) {
    Text(
    text = if (isGameOver) "GAME OVER" else "",
    // ... More attributes
    )
    if (isGameOver) {
    Image(
    asset = vectorResource(id = R.drawable.ic_replay),
    modifier = Modifier.preferredSize(40.dp)
    .align(alignment = Alignment.CenterHorizontally)
    )
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    GameOverTextView(gameState.isGameOver,
    modifier = Modifier.align(Alignment.Center))
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = { /* ... */ },
    2
    indication = null)
    3
    ) {
    4
    HighScoreTextViews(gameState)
    5
    Canvas(modifier = Modifier.weight(1f)) {
    6
    // Game drawing
    7
    }
    8
    9
    10
    }
    11
    12
    @Composable
    13
    fun GameOverTextView(isGameOver: Boolean = true, modifier: Modifier = Mod
    14
    {
    15
    Column(modifier = modifier) {
    16
    Text(
    17
    text = if (isGameOver) "GAME OVER" else "",
    18
    // ... More attributes
    19
    )
    20
    if (isGameOver) {
    21
    Image(
    22
    asset = vectorResource(id = R.drawable.ic_replay),
    23
    modifier = Modifier.preferredSize(40.dp)
    24
    .align(alignment = Alignment.CenterHorizontally)
    25
    )
    26
    }
    27
    }
    28
    }
    29
    37

    View full-size slide

  115. Game Over
    Finally...
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = { /* ... */ },
    indication = null)
    ) {
    HighScoreTextViews(gameState)
    Canvas(modifier = Modifier.weight(1f)) {
    // Game drawing
    }
    GameOverTextView(gameState.isGameOver,
    modifier = Modifier.align(Alignment.Center))
    }
    @Composable
    fun GameOverTextView(isGameOver: Boolean = true, modifier: Modifier = Mod
    {
    Column(modifier = modifier) {
    Text(
    text = if (isGameOver) "GAME OVER" else "",
    // ... More attributes
    )
    if (isGameOver) {
    Image(
    asset = vectorResource(id = R.drawable.ic_replay),
    modifier = Modifier.preferredSize(40.dp)
    .align(alignment = Alignment.CenterHorizontally)
    )
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    GameOverTextView(gameState.isGameOver,
    modifier = Modifier.align(Alignment.Center))
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = { /* ... */ },
    2
    indication = null)
    3
    ) {
    4
    HighScoreTextViews(gameState)
    5
    Canvas(modifier = Modifier.weight(1f)) {
    6
    // Game drawing
    7
    }
    8
    9
    10
    }
    11
    12
    @Composable
    13
    fun GameOverTextView(isGameOver: Boolean = true, modifier: Modifier = Mod
    14
    {
    15
    Column(modifier = modifier) {
    16
    Text(
    17
    text = if (isGameOver) "GAME OVER" else "",
    18
    // ... More attributes
    19
    )
    20
    if (isGameOver) {
    21
    Image(
    22
    asset = vectorResource(id = R.drawable.ic_replay),
    23
    modifier = Modifier.preferredSize(40.dp)
    24
    .align(alignment = Alignment.CenterHorizontally)
    25
    )
    26
    }
    27
    }
    28
    }
    29
    fun GameOverTextView(isGameOver: Boolean = true, modifier: Modifier = Mod
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = { /* ... */ },
    2
    indication = null)
    3
    ) {
    4
    HighScoreTextViews(gameState)
    5
    Canvas(modifier = Modifier.weight(1f)) {
    6
    // Game drawing
    7
    }
    8
    GameOverTextView(gameState.isGameOver,
    9
    modifier = Modifier.align(Alignment.Center))
    10
    }
    11
    12
    @Composable
    13
    14
    {
    15
    Column(modifier = modifier) {
    16
    Text(
    17
    text = if (isGameOver) "GAME OVER" else "",
    18
    // ... More attributes
    19
    )
    20
    if (isGameOver) {
    21
    Image(
    22
    asset = vectorResource(id = R.drawable.ic_replay),
    23
    modifier = Modifier.preferredSize(40.dp)
    24
    .align(alignment = Alignment.CenterHorizontally)
    25
    )
    26
    }
    27
    }
    28
    }
    29
    37

    View full-size slide

  116. Game Over
    Finally...
    Column(modifier = Modifier.fillMaxWidth().clickable(
    onClick = { /* ... */ },
    indication = null)
    ) {
    HighScoreTextViews(gameState)
    Canvas(modifier = Modifier.weight(1f)) {
    // Game drawing
    }
    GameOverTextView(gameState.isGameOver,
    modifier = Modifier.align(Alignment.Center))
    }
    @Composable
    fun GameOverTextView(isGameOver: Boolean = true, modifier: Modifier = Mod
    {
    Column(modifier = modifier) {
    Text(
    text = if (isGameOver) "GAME OVER" else "",
    // ... More attributes
    )
    if (isGameOver) {
    Image(
    asset = vectorResource(id = R.drawable.ic_replay),
    modifier = Modifier.preferredSize(40.dp)
    .align(alignment = Alignment.CenterHorizontally)
    )
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    GameOverTextView(gameState.isGameOver,
    modifier = Modifier.align(Alignment.Center))
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = { /* ... */ },
    2
    indication = null)
    3
    ) {
    4
    HighScoreTextViews(gameState)
    5
    Canvas(modifier = Modifier.weight(1f)) {
    6
    // Game drawing
    7
    }
    8
    9
    10
    }
    11
    12
    @Composable
    13
    fun GameOverTextView(isGameOver: Boolean = true, modifier: Modifier = Mod
    14
    {
    15
    Column(modifier = modifier) {
    16
    Text(
    17
    text = if (isGameOver) "GAME OVER" else "",
    18
    // ... More attributes
    19
    )
    20
    if (isGameOver) {
    21
    Image(
    22
    asset = vectorResource(id = R.drawable.ic_replay),
    23
    modifier = Modifier.preferredSize(40.dp)
    24
    .align(alignment = Alignment.CenterHorizontally)
    25
    )
    26
    }
    27
    }
    28
    }
    29
    fun GameOverTextView(isGameOver: Boolean = true, modifier: Modifier = Mod
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = { /* ... */ },
    2
    indication = null)
    3
    ) {
    4
    HighScoreTextViews(gameState)
    5
    Canvas(modifier = Modifier.weight(1f)) {
    6
    // Game drawing
    7
    }
    8
    GameOverTextView(gameState.isGameOver,
    9
    modifier = Modifier.align(Alignment.Center))
    10
    }
    11
    12
    @Composable
    13
    14
    {
    15
    Column(modifier = modifier) {
    16
    Text(
    17
    text = if (isGameOver) "GAME OVER" else "",
    18
    // ... More attributes
    19
    )
    20
    if (isGameOver) {
    21
    Image(
    22
    asset = vectorResource(id = R.drawable.ic_replay),
    23
    modifier = Modifier.preferredSize(40.dp)
    24
    .align(alignment = Alignment.CenterHorizontally)
    25
    )
    26
    }
    27
    }
    28
    }
    29
    text = if (isGameOver) "GAME OVER" else "",
    if (isGameOver) {
    Column(modifier = Modifier.fillMaxWidth().clickable(
    1
    onClick = { /* ... */ },
    2
    indication = null)
    3
    ) {
    4
    HighScoreTextViews(gameState)
    5
    Canvas(modifier = Modifier.weight(1f)) {
    6
    // Game drawing
    7
    }
    8
    GameOverTextView(gameState.isGameOver,
    9
    modifier = Modifier.align(Alignment.Center))
    10
    }
    11
    12
    @Composable
    13
    fun GameOverTextView(isGameOver: Boolean = true, modifier: Modifier = Mod
    14
    {
    15
    Column(modifier = modifier) {
    16
    Text(
    17
    18
    // ... More attributes
    19
    )
    20
    21
    Image(
    22
    asset = vectorResource(id = R.drawable.ic_replay),
    23
    modifier = Modifier.preferredSize(40.dp)
    24
    .align(alignment = Alignment.CenterHorizontally)
    25
    )
    26
    }
    27
    }
    28
    }
    29
    37

    View full-size slide

  117. Summary
    It was super fun to make a game in Jetpack Compose
    Canvas
    Game Loop
    Vector Paths
    State Handling
    Bounding Box
    38

    View full-size slide

  118. Thank You for Listening!
    Code is available at github.com/wajahatkarim3/DinoCompose
    WajahatKarim
    wajahatkarim.com/subscribe
    wajahatkarim.com
    39

    View full-size slide