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

Cb3e26de5b49f50cc74a98a7729b3f93?s=47 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

Cb3e26de5b49f50cc74a98a7729b3f93?s=128

Wajahat Karim

September 26, 2020
Tweet

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
  2. Compose ❤ Dino Building Chrome’s T-Rex Game in Jetpack Compose

    2
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  20. Compose Canvas 101 DrawScope - Handles the drawing API drawRect()

    drawOval() drawLine() drawImage() drawRoundRect() drawCircle() drawArc() drawPath() fun DrawScope.drawMyShape() { } 9
  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
  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
  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
  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
  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
  26. Wanna make Dino Game? 11

  27. Drawing Game Scene Basics 12

  28. Drawing Game Scene Basics X 12

  29. Drawing Game Scene Basics X Y 12

  30. Drawing Game Scene Basics deviceWithInPixels X Y 12

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

  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
  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
  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
  35. Drawing Game Scene Earth 13

  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
  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
  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
  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
  40. Drawing Game Scene Vector Icons (SVG) Icons made by from

    Freepik www.flaticon.com 14
  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
  42. Drawing Game Scene Clouds Text 16

  43. Drawing Game Scene Clouds Text No method to draw VectorDrawables

    in Canvas yet! 16
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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<CloudModel> = arrayListOf<CloudModel>(), 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
  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<CloudModel> = arrayListOf<CloudModel>(), 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<CloudModel> = arrayListOf<CloudModel>(), 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
  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<CloudModel> = arrayListOf<CloudModel>(), 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<CloudModel> = arrayListOf<CloudModel>(), 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<CloudModel> = arrayListOf<CloudModel>(), 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
  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<CloudModel> = arrayListOf<CloudModel>(), 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<CloudModel> = arrayListOf<CloudModel>(), 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<CloudModel> = arrayListOf<CloudModel>(), 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<CloudModel> = arrayListOf<CloudModel>(), 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
  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<CloudModel> = arrayListOf<CloudModel>(), 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<CloudModel> = arrayListOf<CloudModel>(), 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<CloudModel> = arrayListOf<CloudModel>(), 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<CloudModel> = arrayListOf<CloudModel>(), 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<CloudModel> = arrayListOf<CloudModel>(), 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
  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<CloudModel> = arrayListOf<CloudModel>(), 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
  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
  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
  66. Game Loop in Compose Attempt # 3 - Compose Couroutines

    @Composable fun animationTimeMillis(gameloopCallback: () -> Unit): State<Long> { 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
  67. Game Loop in Compose Attempt # 3 - Compose Couroutines

    @Composable fun animationTimeMillis(gameloopCallback: () -> Unit): State<Long> { 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<Long> 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
  68. Game Loop in Compose Attempt # 3 - Compose Couroutines

    @Composable fun animationTimeMillis(gameloopCallback: () -> Unit): State<Long> { 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<Long> 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<Long> 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
  69. Game Loop in Compose Attempt # 3 - Compose Couroutines

    @Composable fun animationTimeMillis(gameloopCallback: () -> Unit): State<Long> { 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<Long> 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<Long> 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<Long> 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
  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
  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
  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
  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
  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
  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
  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
  77. Scrolling Earth Two blocks chasing each other 28

  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  95. Dino Running Steps Keyframes in Jetpack Compose data class DinoState(

    // .... var keyframe: Int = 0, private var pathList: ArrayList<Path> = 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
  96. Dino Running Steps Keyframes in Jetpack Compose data class DinoState(

    // .... var keyframe: Int = 0, private var pathList: ArrayList<Path> = 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<Path> = 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
  97. Dino Running Steps Keyframes in Jetpack Compose data class DinoState(

    // .... var keyframe: Int = 0, private var pathList: ArrayList<Path> = 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<Path> = 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<Path> = 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
  98. Dino Running Steps Keyframes in Jetpack Compose data class DinoState(

    // .... var keyframe: Int = 0, private var pathList: ArrayList<Path> = 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<Path> = 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<Path> = 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<Path> = 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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  117. Summary It was super fun to make a game in

    Jetpack Compose Canvas Game Loop Vector Paths State Handling Bounding Box 38
  118. Thank You for Listening! Code is available at github.com/wajahatkarim3/DinoCompose WajahatKarim

    wajahatkarim.com/subscribe wajahatkarim.com 39