Beat the high-score: build a game using libGDX and Kotlin

7843bb075c05be6886a97b77e36758ff?s=47 David
September 11, 2019

Beat the high-score: build a game using libGDX and Kotlin

Playing games is fun but making games is even better, especially with Kotlin and libGDX. Let’s build together a breakout game and let’s explore the gaming framework libGDX.

This session will show some libGDX’s concepts: how to draw and animate elements of our game, how collision system works to destroy our bricks, what Kotlin brings to libGDX. Then we will dive into more advanced topics like shaders, in order to handle pixels from our images.

Why are we doing all of this? To break the high score, of course!

(11 September 2019 - Oslo - JavaZone 2019)
Video: https://vimeo.com/362771304

7843bb075c05be6886a97b77e36758ff?s=128

David

September 11, 2019
Tweet

Transcript

  1. Build a game using libGDX and Kotlin Build a game

    using libGDX and Kotlin Build a game using libGDX and Kotlin Build a game using libGDX and Kotlin Beat the High score Beat the High score Build a game using libGDX and Kotlin
  2. -Gilles Allain In a farm… far, far away…

  3. -Gilles Allain a fox appears in the darkness of the

    night.
  4. None
  5. -Gilles Allain In a mighty move, he stole all the

    chickens of the farm...
  6. None
  7. -Gilles Allain those chickens were our...friends.

  8. -Gilles Allain the same night, a lightning strikes you!

  9. None
  10. -Gilles Allain ...this lightning has given you power to bring

    back... ...our friends!
  11. Let's help him !
 (by making a game)

  12. David Wursteisen

  13. None
  14. None
  15. John Carmack

  16. None
  17. None
  18. None
  19. Game 
 development Kotlin libGDX

  20. You, at the end of 
 this conference Game 


    development Kotlin libGDX
  21. Game Development?

  22. None
  23. None
  24. None
  25. None
  26. None
  27. public void render(float delta) {
 movePlayer(delta);
 moveEnemies(delta);
 moveWorld(delta);
 
 renderWorld();


    renderEnemies();
 renderPlayer();
 }
  28. public void render(float delta) {
 movePlayer(delta);
 moveEnemies(delta);
 moveWorld(delta);
 
 renderWorld();


    renderEnemies();
 renderPlayer();
 }
  29. public void render(float delta) {
 movePlayer(delta);
 moveEnemies(delta);
 moveWorld(delta);
 
 renderWorld();


    renderEnemies();
 renderPlayer();
 }
  30. public void render(float delta) {
 movePlayer(delta);
 moveEnemies(delta);
 moveWorld(delta);
 
 renderWorld();


    renderEnemies();
 renderPlayer();
 } 60x
 seconds
  31. (0,0) x y

  32. Why libGDX?

  33. None
  34. Pavel Quest Destroy Blocks Mirage Realms

  35. None
  36. Hair Dash - http://cleancutgames.com/

  37. None
  38. Bob & Prickle - http://www.crealodegames.com/

  39. None
  40. Let’s make a game!

  41. None
  42. None
  43. Core iOS GWT Android Desktop

  44. void render(float delta) { }

  45. fun render(delta: float) { }

  46. class HelloScreen : ScreenAdapter() { override fun render(delta: Float) {

    } }
  47. class HelloScreen : ScreenAdapter() { override fun render(delta: Float) {

    Gdx.gl.glClearColor(0f, 0f, 0f, 1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) } }
  48. class HelloScreen : ScreenAdapter() { private val shape = ShapeRenderer()

    override fun render(delta: Float) { Gdx.gl.glClearColor(0f, 0f, 0f, 1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) } }
  49. class HelloScreen : ScreenAdapter() { private val shape = ShapeRenderer()

    override fun render(delta: Float) { Gdx.gl.glClearColor(0f, 0f, 0f, 1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) shape.begin(ShapeRenderer.ShapeType.Filled) shape.end() } }
  50. class HelloScreen : ScreenAdapter() { private val shape = ShapeRenderer()

    override fun render(delta: Float) { Gdx.gl.glClearColor(0f, 0f, 0f, 1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) shape.begin(ShapeRenderer.ShapeType.Filled) shape.color = Color.RED shape.circle(0f, 0f, 20f) shape.end() } }
  51. None
  52. None
  53. class HelloScreen : ScreenAdapter() { override fun render(delta: Float) {

    Gdx.gl.glClearColor(0f, 0f, 0f, 1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) } }
  54. class HelloScreen : ScreenAdapter() { private val batch = SpriteBatch()

    override fun render(delta: Float) { Gdx.gl.glClearColor(0f, 0f, 0f, 1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) } }
  55. class HelloScreen : ScreenAdapter() { private val batch = SpriteBatch()

    private val texture = Texture("badlogic.jpg") override fun render(delta: Float) { Gdx.gl.glClearColor(0f, 0f, 0f, 1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) } }
  56. class HelloScreen : ScreenAdapter() { private val batch = SpriteBatch()

    private val texture = Texture("badlogic.jpg") override fun render(delta: Float) { Gdx.gl.glClearColor(0f, 0f, 0f, 1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) batch.begin() batch.end() } }
  57. class HelloScreen : ScreenAdapter() { private val batch = SpriteBatch()

    private val texture = Texture("badlogic.jpg") override fun render(delta: Float) { Gdx.gl.glClearColor(0f, 0f, 0f, 1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) batch.begin() batch.draw(texture, 0f, 0f) batch.end() } }
  58. Position = (0, 0) Position = (10, 0) Position =

    (0, -5) Position = (-10, 5)
  59. class MyScreen : ScreenAdapter() { private val batch = ShapeRenderer()

    private val position: Vector2 = Vector2(0f, 0f) override fun render(delta: Float) { position.add(0f, -0.5f) batch.begin(ShapeRenderer.ShapeType.Filled) batch.color = Color.CHARTREUSE batch.circle(position.x, position.y, radius) batch.end() } }
  60. class MyScreen : ScreenAdapter() { private val batch = ShapeRenderer()

    private val position: Vector2 = Vector2(0f, 0f) override fun render(delta: Float) { position.add(0f, -0.5f) batch.begin(ShapeRenderer.ShapeType.Filled) batch.color = Color.CHARTREUSE batch.circle(position.x, position.y, radius) batch.end() } }
  61. class MyScreen : ScreenAdapter() { private val batch = ShapeRenderer()

    private val position: Vector2 = Vector2(0f, 0f) override fun render(delta: Float) { position.add(0f, -0.5f) batch.begin(ShapeRenderer.ShapeType.Filled) batch.color = Color.CHARTREUSE batch.circle(position.x, position.y, radius) batch.end() } }
  62. class MyScreen : ScreenAdapter() { private val batch = ShapeRenderer()

    private val position: Vector2 = Vector2(0f, 0f) override fun render(delta: Float) { position.add(0f, -0.5f) batch.begin(ShapeRenderer.ShapeType.Filled) batch.color = Color.CHARTREUSE batch.circle(position.x, position.y, radius) batch.end() } }
  63. class MyScreen : ScreenAdapter() { private val batch = ShapeRenderer()

    private val position: Vector2 = Vector2(0f, 0f) override fun render(delta: Float) { position.add(0f, -0.5f) val (x, y) = position batch.begin(ShapeRenderer.ShapeType.Filled) batch.color = Color.CHARTREUSE batch.circle(x, y, radius) batch.end() } }
  64. /** * Operator function that allows to deconstruct this vector.

    * @return X component. */ operator fun Vector2.component1(): Float = this.x /** * Operator function that allows to deconstruct this vector. * @return Y component. */ operator fun Vector2.component2(): Float = this.y val vector = Vector2(45f, 50f) val (x, y) = vector
  65. /** * Operator function that allows to deconstruct this vector.

    * @return X component. */ operator fun Vector2.component1(): Float = this.x /** * Operator function that allows to deconstruct this vector. * @return Y component. */ operator fun Vector2.component2(): Float = this.y val (x, y) = Vector2(45f, 50f)
  66. val alpha = Math.abs(ball.x - raquet.x) val direction: Vector2 =

    Vector2(10f, 20f) direction.scl(1f, -1f) .scl(alpha) .nor() .rotate(306f) .add(10f, 20f)
  67. entity.add(Ball()) .add(Position(Vector2(-100f, -100f))) .add(Size(Vector2(8f, 9f))) .add(Offset(Vector2(4f, 4f)))

  68. entity.add(Ball()) .add(Position(Vector2(-100f, -100f))) .add(Size(Vector2(8f, 9f))) .add(Offset(Vector2(4f, 4f)))

  69. entity.add(Ball()) .add(Position(-100 v2 -100f)) .add(Size(8 v2 9)) .add(Offset(4 v2 4))

    Create a 
 new Vector2 infix fun Number.v2(other: Number): Vector2 { return Vector2(this.toFloat(), other.toFloat()) }
  70. Collision engine

  71. None
  72. Side Racket Ball

  73. val ball = Rectangle(68f, 64f, 5f, 5f) val player =

    Rectangle(64f, 64f, 100f, 10f) // the ball hit the player? player.overlaps(ball)
  74. val ball = Rectangle(68f, 64f, 5f, 5f) val player =

    Rectangle(64f, 64f, 100f, 10f) // the ball hit the player? player.overlaps(ball)
  75. val ball = Rectangle(68f, 64f, 5f, 5f) val player =

    Rectangle(64f, 64f, 100f, 10f) // the ball hit the player? player.overlaps(ball) (Simple AABB Engine)
  76. None
  77. None
  78. None
  79. Box2D

  80. var world = World(Vector2(0, -10), true) // ... val bodyDef

    = BodyDef() // Set its world position bodyDef.position.set(position.x, position.y) bodyDef.type = BodyDef.BodyType.DynamicBody bodyDef.angle = 0f val body = world.createBody(bodyDef) val shape = CircleShape() shape.radius = 8f // 2. Create a FixtureDef, as usual. val fd = FixtureDef() fd.density = 1f fd.friction = 2f fd.shape = shape body.createFixture(fd) body.userData = Any // attach sprite data shape.dispose()
  81. var world = World(Vector2(0, -10), true) // ... val bodyDef

    = BodyDef() // Set its world position bodyDef.position.set(position.x, position.y) bodyDef.type = BodyDef.BodyType.DynamicBody bodyDef.angle = 0f val body = world.createBody(bodyDef) val shape = CircleShape() shape.radius = 8f // 2. Create a FixtureDef, as usual. val fd = FixtureDef() fd.density = 1f fd.friction = 2f fd.shape = shape body.createFixture(fd) body.userData = Any // attach sprite data shape.dispose() Gravity
  82. var world = World(Vector2(0, -10), true) // ... val bodyDef

    = BodyDef() // Set its world position bodyDef.position.set(position.x, position.y) bodyDef.type = BodyDef.BodyType.DynamicBody bodyDef.angle = 0f val body = world.createBody(bodyDef) val shape = CircleShape() shape.radius = 8f // 2. Create a FixtureDef, as usual. val fd = FixtureDef() fd.density = 1f fd.friction = 2f fd.shape = shape body.createFixture(fd) body.userData = Any // attach sprite data shape.dispose() Configuration of the « thing »
  83. var world = World(Vector2(0, -10), true) // ... val bodyDef

    = BodyDef() // Set its world position bodyDef.position.set(position.x, position.y) bodyDef.type = BodyDef.BodyType.DynamicBody bodyDef.angle = 0f val body = world.createBody(bodyDef) val shape = CircleShape() shape.radius = 8f // 2. Create a FixtureDef, as usual. val fd = FixtureDef() fd.density = 1f fd.friction = 2f fd.shape = shape body.createFixture(fd) body.userData = Any // attach sprite data shape.dispose() Attach data from your world to Box2D world
  84. var world = World(Vector2(0, -10), true) // ... val bodyDef

    = BodyDef() // Set its world position bodyDef.position.set(position.x, position.y) bodyDef.type = BodyDef.BodyType.DynamicBody bodyDef.angle = 0f val body = world.createBody(bodyDef) val shape = CircleShape() shape.radius = 8f // 2. Create a FixtureDef, as usual. val fd = FixtureDef() fd.density = 1f fd.friction = 2f fd.shape = shape body.createFixture(fd) body.userData = Any // attach sprite data shape.dispose()
  85. var world = World(Vector2(0, -10), true) // ... val bodyDef

    = BodyDef() // Set its world position bodyDef.position.set(position.x, position.y) bodyDef.type = BodyDef.BodyType.DynamicBody bodyDef.angle = 0f val body = world.createBody(bodyDef) val shape = CircleShape() shape.radius = 8f // 2. Create a FixtureDef, as usual. val fd = FixtureDef() fd.density = 1f fd.friction = 2f fd.shape = shape body.createFixture(fd) body.userData = Any // attach sprite data shape.dispose()
  86. Traversable

  87. The ball and boxes use 
 AABB Physic Engine Wreckages

    use 
 Box2D Physic Engine
  88. Animations

  89. None
  90. None
  91. // load texture val texture = Texture(Gdx.files.internal("texture.png")) val split =

    TextureRegion.split(texture, 128, 128) // select frames val frames = Array<TextureRegion>() frames.add(split[0][0]) frames.add(split[0][1]) frames.add(split[0][2]) frames.add(split[0][4]) // create animation val animation = Animation<TextureRegion>(100f, frames) // select frame to draw val keyToRender = animation.getKeyFrame(time)
  92. // load texture val texture = Texture(Gdx.files.internal("texture.png")) val split =

    TextureRegion.split(texture, 128, 128) // select frames val frames = Array<TextureRegion>() frames.add(split[0][0]) frames.add(split[0][1]) frames.add(split[0][2]) frames.add(split[0][4]) // create animation val animation = Animation<TextureRegion>(100f, frames) // select frame to draw val keyToRender = animation.getKeyFrame(time)
  93. // load texture val texture = Texture(Gdx.files.internal("texture.png")) val split =

    TextureRegion.split(texture, 128, 128) // select frames val frames = Array<TextureRegion>() frames.add(split[0][0]) frames.add(split[0][1]) frames.add(split[0][2]) frames.add(split[0][4]) // create animation val animation = Animation<TextureRegion>(100f, frames) // select frame to draw val keyToRender = animation.getKeyFrame(time)
  94. // load texture val texture = Texture(Gdx.files.internal("texture.png")) val split =

    TextureRegion.split(texture, 128, 128) // select frames val frames = com.badlogic.gdx.utils.Array<TextureRegion>() frames.add(split[0][0]) frames.add(split[0][1]) frames.add(split[0][2]) frames.add(split[0][4]) // create animation val animation = Animation<TextureRegion>(100f, frames) // select frame to draw val keyToRender = animation.getKeyFrame(time) import com.badlogic.gdx.utils.Array as GdxArray
  95. // load texture val texture = Texture(Gdx.files.internal("texture.png")) val split =

    TextureRegion.split(texture, 128, 128) // select frames val frames = GdxArray<TextureRegion>() frames.add(split[0][0]) frames.add(split[0][1]) frames.add(split[0][2]) frames.add(split[0][4]) // create animation val animation = Animation<TextureRegion>(100f, frames) // select frame to draw val keyToRender = animation.getKeyFrame(time)
  96. // load texture val texture = Texture(Gdx.files.internal("texture.png")) val split =

    TextureRegion.split(texture, 128, 128) // select frames val frames = GdxArray<TextureRegion>() frames.add(split[0][0]) frames.add(split[0][1]) frames.add(split[0][2]) frames.add(split[0][4]) // create animation val animation = Animation<TextureRegion>(100f, frames) // select frame to draw val keyToRender = animation.getKeyFrame(time) Duration per frame
  97. 100ms 100ms 100ms 100ms

  98. 800ms 100ms 100ms 100ms 100ms 100ms 100ms 100ms 100ms 100ms

    100ms
  99. y = Interpolation.bounceOut.apply(time) * 100f Non linear Should be a

    percentage (0..1) y will go from 0 to 100
  100. None
  101. Linear Bounce Elastic Pow2

  102. Field Of View

  103. None
  104. viewport.camera.position.add(0f, 40f, 0f) // Recalculates the projection and // view

    matrix of this camera // Use this after you've manipulated any of // the attributes of the camera. viewport.camera.update() viewport.camera.rotate(10f, 0f, 0f, 1f) viewport.camera.update()
  105. viewport.camera.position.add(0f, 40f, 0f) // Recalculates the projection and // view

    matrix of this camera // Use this after you've manipulated any of // the attributes of the camera. viewport.camera.update() viewport.camera.rotate(10f, 0f, 0f, 1f) viewport.camera.update() Will move the camera up
  106. viewport.camera.position.add(0f, 40f, 0f) // Recalculates the projection and // view

    matrix of this camera // Use this after you've manipulated any of // the attributes of the camera. viewport.camera.update() viewport.camera.rotate(10f, 0f, 0f, 1f) viewport.camera.update() Rotate the camera on the z Axis
  107. viewport.camera.rotate(10f, 0f, 0f, 1f)

  108. None
  109. None
  110. None
  111. None
  112. Our world
 (Orientation: Portrait)

  113. ScreenViewport

  114. StretchViewport

  115. FitViewport Clear this zone Clear this zone

  116. FillViewport

  117. ExtendViewport Displaying this zone

  118. class MyScreen : ScreenAdapter() { private lateinit var viewport: Viewport

    private lateinit var bath: SpriteBatch override fun show() { viewport = FitViewport(200f, 200f) bath = SpriteBath() } override fun render(delta: Float) { Gdx.gl.glClearColor(91f / 256f, 110f / 256f, 225f / 256f, 1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) batch.begin() batch.projectionMatrix = viewport.camera.combined // render batch.end() } override fun resize(width: Int, height: Int) { viewport.update(width, height) } }
  119. class MyScreen : ScreenAdapter() { private lateinit var viewport: Viewport

    private lateinit var bath: SpriteBatch override fun show() { viewport = FitViewport(200f, 200f) bath = SpriteBath() } override fun render(delta: Float) { Gdx.gl.glClearColor(91f / 256f, 110f / 256f, 225f / 256f, 1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) batch.begin() batch.projectionMatrix = viewport.camera.combined // render batch.end() } override fun resize(width: Int, height: Int) { viewport.update(width, height) } } Size of the world you’ll display on the screen
  120. class MyScreen : ScreenAdapter() { private lateinit var viewport: Viewport

    private lateinit var bath: SpriteBatch override fun show() { viewport = FitViewport(200f, 200f) bath = SpriteBath() } override fun render(delta: Float) { Gdx.gl.glClearColor(91f / 256f, 110f / 256f, 225f / 256f, 1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) batch.begin() batch.projectionMatrix = viewport.camera.combined // render batch.end() } override fun resize(width: Int, height: Int) { viewport.update(width, height) } }
  121. Around the game

  122. dependencies { compile "com.badlogicgames.gdx:gdx:$gdxVersion" compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion" compile "com.badlogicgames.ashley:ashley:$ashleyVersion" compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"

    compile "io.github.libktx:ktx-ashley:$ktxVersion" compile "io.github.libktx:ktx-graphics:$ktxVersion" compile "io.github.libktx:ktx-scene2d:$ktxVersion" compile "io.github.libktx:ktx-log:$ktxVersion" compile "com.github.dwursteisen.libgdx-addons:aseprite-addons:$libgdx_addons" compile "com.github.dwursteisen.libgdx-addons:ashley-addons:$libgdx_addons" compile "com.strongjoshua:libgdx-inGameConsole:$console" }
  123. dependencies { compile "com.badlogicgames.gdx:gdx:$gdxVersion" compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion" compile "com.badlogicgames.ashley:ashley:$ashleyVersion" compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"

    compile "io.github.libktx:ktx-ashley:$ktxVersion" compile "io.github.libktx:ktx-graphics:$ktxVersion" compile "io.github.libktx:ktx-scene2d:$ktxVersion" compile "io.github.libktx:ktx-log:$ktxVersion" compile "com.github.dwursteisen.libgdx-addons:aseprite-addons:$libgdx_addons" compile "com.github.dwursteisen.libgdx-addons:ashley-addons:$libgdx_addons" compile "com.strongjoshua:libgdx-inGameConsole:$console" }
  124. dependencies { compile "com.badlogicgames.gdx:gdx:$gdxVersion" compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion" compile "com.badlogicgames.ashley:ashley:$ashleyVersion" compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"

    compile "io.github.libktx:ktx-ashley:$ktxVersion" compile "io.github.libktx:ktx-graphics:$ktxVersion" compile "io.github.libktx:ktx-scene2d:$ktxVersion" compile "io.github.libktx:ktx-log:$ktxVersion" compile "com.github.dwursteisen.libgdx-addons:aseprite-addons:$libgdx_addons" compile "com.github.dwursteisen.libgdx-addons:ashley-addons:$libgdx_addons" compile "com.strongjoshua:libgdx-inGameConsole:$console" }
  125. dependencies { compile "com.badlogicgames.gdx:gdx:$gdxVersion" compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion" compile "com.badlogicgames.ashley:ashley:$ashleyVersion" compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"

    compile "io.github.libktx:ktx-ashley:$ktxVersion" compile "io.github.libktx:ktx-graphics:$ktxVersion" compile "io.github.libktx:ktx-scene2d:$ktxVersion" compile "io.github.libktx:ktx-log:$ktxVersion" compile "com.github.dwursteisen.libgdx-addons:aseprite-addons:$libgdx_addons" compile "com.github.dwursteisen.libgdx-addons:ashley-addons:$libgdx_addons" compile "com.strongjoshua:libgdx-inGameConsole:$console" }
  126. dependencies { compile "com.badlogicgames.gdx:gdx:$gdxVersion" compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion" compile "com.badlogicgames.ashley:ashley:$ashleyVersion" compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"

    compile "io.github.libktx:ktx-ashley:$ktxVersion" compile "io.github.libktx:ktx-graphics:$ktxVersion" compile "io.github.libktx:ktx-scene2d:$ktxVersion" compile "io.github.libktx:ktx-log:$ktxVersion" compile "com.github.dwursteisen.libgdx-addons:aseprite-addons:$libgdx_addons" compile "com.github.dwursteisen.libgdx-addons:ashley-addons:$libgdx_addons" compile "com.strongjoshua:libgdx-inGameConsole:$console" }
  127. Entities & Ashley

  128. None
  129. Entity Player Monster Knight Ghoul

  130. Composition 
 over 
 Inheritance

  131. Position Animation Player State Entity

  132. val player = Entity() player.add(Player())) player.add(Position(100 v2 100)) player.add(Animation("idle")) player.add(StateComponent())

    Position Animation Player State
  133. ComponentMapper<Player> player = ComponentMapper.getFor(Player.class); ComponentMapper<Position> position = ComponentMapper.getFor(Position.class); ComponentMapper<Animation> animation

    = ComponentMapper.getFor(Animation.class); ComponentMapper<StateComponent> state = ComponentMapper.getFor(StateComponent.class); ... player.get(entity).life; position.get(entity).xy; animation.get(entity).getFrame(time); state.get(entity).time; Extractor Extraction
  134. inline fun <reified T : Component> EntitySystem.get(): ComponentMapper<T> = 


    ComponentMapper.getFor(T::class.java) inline operator fun <reified T : Component> Entity.get(mapper: ComponentMapper<T>): T = 
 mapper.get(this)
  135. val player: ComponentMapper<Player> = get() val position: ComponentMapper<Position> = get()

    val animation: ComponentMapper<Animation> = get() val state: ComponentMapper<StateComponent> = get() // access to components entity[player].life entity[position].xy entity[animation].frame(time) entity[state].time
  136. val player = get<Player>() val position = get<Position>() val animation

    = get<Animation>() val state = get<StateComponent>() // access to components entity[player].life entity[position].xy entity[animation].frame(time) entity[state].time
  137. val player = get<Player>() val position = get<Position>() val animation

    = get<Animation>() val state = get<StateComponent>() // access to components entity[player].life entity[position].xy entity[animation].frame(time) entity[state].time Accessing like a map
  138. inline fun <reified T : Component> EntitySystem.get(): ComponentMapper<T> = 


    ComponentMapper.getFor(T::class.java) inline operator fun <reified T : Component> Entity.get(mapper: ComponentMapper<T>): T = 
 mapper.get(this)
  139. Close Open

  140. fun on(event: Int, block: Transition): OnState { var currentTransitions =

    parent.transitions[state] ?: emptyMap() currentTransitions += event to block parent.transitions += state to currentTransitions return this } Lambda
  141. startWith(WAIT) onState(WAIT).on(GameEvents.EVENT_COMPUTER_ALLOW_MOVE) { entity, event -> if (entity[dragon].currentTurn > 3)

    { entity[dragon].currentTurn = -1 go(FIRE, entity, event) } else { go(MOVE, entity, event) } } onState(FIRE).on(GameEvents.EVENT_DRAGON_FIRE_BALL) { entity, event -> go(MOVE, entity, event) } Lambda
  142. startWith(WAIT) onState(WAIT).on(GameEvents.EVENT_COMPUTER_ALLOW_MOVE) { entity, event -> if (entity[dragon].currentTurn > 3)

    { entity[dragon].currentTurn = -1 go(FIRE, entity, event) } else { go(MOVE, entity, event) } } onState(FIRE).on(GameEvents.EVENT_DRAGON_FIRE_BALL) { entity, event -> go(MOVE, entity, event) } State Event Transition
  143. Loading…

  144. val assetsManager = AssetManager() assetsManager.load("player.png", Texture::class.java) assetsManager.load("sfx/beat_intro.ogg", Music::class.java) assetsManager.load("krungthep2.fnt", BitmapFont::class.java)

    assetsManager.load("sheets/intro", Aseprite::class.java) // call it until it's loaded val isLoaded = assetsManager.update() val texture = assetsManager.get("player.png", Texture::class.java)
  145. val assetsManager = AssetManager() assetsManager.load("player.png", Texture::class.java) assetsManager.load("sfx/beat_intro.ogg", Music::class.java) assetsManager.load("krungthep2.fnt", BitmapFont::class.java)

    assetsManager.load("sheets/intro", Aseprite::class.java) // call it until it's loaded val isLoaded = assetsManager.update() val texture = assetsManager.get("player.png", Texture::class.java)
  146. val assetsManager = AssetManager() assetsManager.load("player.png", Texture::class.java) assetsManager.load("sfx/beat_intro.ogg", Music::class.java) assetsManager.load("krungthep2.fnt", BitmapFont::class.java)

    assetsManager.load("sheets/intro", Aseprite::class.java) // call it until it's loaded val isLoaded = assetsManager.update() val texture = assetsManager.get("player.png", Texture::class.java)
  147. val assetsManager = AssetManager() assetsManager.load("player.png", Texture::class.java) assetsManager.load("sfx/beat_intro.ogg", Music::class.java) assetsManager.load("krungthep2.fnt", BitmapFont::class.java)

    assetsManager.load("sheets/intro", Aseprite::class.java) // call it until it's loaded val isLoaded = assetsManager.update() val texture = assetsManager.get("player.png", Texture::class.java)
  148. val assetsManager = AssetManager() assetsManager.load("player.png", Texture::class.java) assetsManager.load("sfx/beat_intro.ogg", Music::class.java) assetsManager.load("krungthep2.fnt", BitmapFont::class.java)

    assetsManager.load("sheets/intro", Aseprite::class.java) // call it until it's loaded val isLoaded = assetsManager.update() val texture = assetsManager.get("player.png", Texture::class.java) Call it in the render method
  149. val assetsManager = AssetManager() assetsManager.load("player.png", Texture::class.java) assetsManager.load("sfx/beat_intro.ogg", Music::class.java) assetsManager.load("krungthep2.fnt", BitmapFont::class.java)

    assetsManager.load("sheets/intro", Aseprite::class.java) // call it until it's loaded val isLoaded = assetsManager.update() val texture = assetsManager.get("player.png", Texture::class.java)
  150. val assetsManager = AssetManager() assetsManager.load("player.png", Texture::class.java) assetsManager.load("sfx/beat_intro.ogg", Music::class.java) assetsManager.load("krungthep2.fnt", BitmapFont::class.java)

    assetsManager.load("sheets/intro", Aseprite::class.java) // call it until it's loaded val isLoaded = assetsManager.update() val texture: Texture = assetsManager["player.png"] inline operator fun <reified T> AssetManager.get(filename: String): T { return this.get(filename, T::class.java) }
  151. Integration with other tools

  152. None
  153. None
  154. None
  155. None
  156. None
  157. // can be loaded using AssetManager too val tmxMap =

    TmxMapLoader().load(mapName) tmxMap.layers["bricks"]?.objects?.forEach { brick -> // create game entity } mapRenderer = OrthogonalTiledMapRenderer(tmxMap) // render method mapRenderer.setView(viewport.camera as OrthographicCamera) mapRenderer.render()
  158. // can be loaded using AssetManager too val tmxMap =

    TmxMapLoader().load(mapName) tmxMap.layers["bricks"]?.objects?.forEach { brick -> // create game entity } mapRenderer = OrthogonalTiledMapRenderer(tmxMap) // render method mapRenderer.setView(viewport.camera as OrthographicCamera) mapRenderer.render()
  159. // can be loaded using AssetManager too val tmxMap =

    TmxMapLoader().load(mapName) tmxMap.layers["bricks"]?.objects?.forEach { brick -> // create game entity } mapRenderer = OrthogonalTiledMapRenderer(tmxMap) // render method mapRenderer.setView(viewport.camera as OrthographicCamera) mapRenderer.render()
  160. // can be loaded using AssetManager too val tmxMap =

    TmxMapLoader().load(mapName) tmxMap.layers["bricks"]?.objects?.forEach { brick -> // create game entity } mapRenderer = OrthogonalTiledMapRenderer(tmxMap) // render method mapRenderer.setView(viewport.camera as OrthographicCamera) mapRenderer.render()
  161. None
  162. hit 4 sound chicken.wav

  163. tmxMap.layers["bricks"].objects.forEach { brick -> val hit: Any = brick.properties["hit"] }

    Error prone Error prone
  164. class BrickProperties(properties: MapProperties) { val x: Double by properties val

    y: Double by properties val hit: Int by properties val sound: String by properties } tmxMap.layers["bricks"].objects.forEach { brick -> val props = BrickProperties(brick.properties) val hit = props.hit }
  165. class BrickProperties(properties: MapProperties) { val x: Double by properties val

    y: Double by properties val hit: Int by properties val sound: String by properties } tmxMap.layers["bricks"].objects.forEach { brick -> val props = BrickProperties(brick.properties) val hit = props.hit }
  166. class BrickProperties(properties: MapProperties) { val x: Double by properties val

    y: Double by properties val hit: Int by properties val sound: String by properties } tmxMap.layers["bricks"].objects.forEach { brick -> val props = BrickProperties(brick.properties) val hit = props.hit } Delegates
  167. inline operator fun <reified T> MapProperties.getValue(thisRef: Any?, property: KProperty<*>): T

    { val asStr = this[property.name].toString() return when (T::class) { Double::class -> asStr.toDouble() Int::class -> asStr.toInt() Boolean::class -> "true" == asStr String::class -> asStr else -> this[property.name] } as T }
  168. val chicken: Aseprite = assets["sheets/chicken"] val idleAnimation: Animation<TextureRegion> = chicken["idle"]

    Inverser slides. Je veux accéder aux animations -> export des metas -> méthode d'extensions. 
 -> explication j'ai fais un plugin qui fait ça pour moi.
  169. Json Json Json

  170. open class AsepriteTask : DefaultTask() { // ... @TaskAction fun

    export() { val exec = getExecActionFactory().newExecAction() val exts = project.extensions.getByType(AsepritePluginExtentions::class.java) val aseprite = exts.exec ?: invalideAsepritePath() // ... } private fun invalideAsepritePath(): Nothing { TODO("""Missing aseprite executable path. Please configure it using aseprite.exec property (ie: in your ~/.gradle/gradle.properties) aseprite.exec=<path to exec> or using aseprite extension in your build.gradle aseprite { exec=<path to exec> } MacOS specific : point to aseprite located into <aseprite directory>/Aseprite.app/Contents/MacOS/aseprite""") } }
  171. open class AsepriteTask : DefaultTask() { // ... @TaskAction fun

    export() { val exec = getExecActionFactory().newExecAction() val exts = project.extensions.getByType(AsepritePluginExtentions::class.java) val aseprite = exts.exec ?: invalideAsepritePath() // ... } private fun invalideAsepritePath(): Nothing { TODO("""Missing aseprite executable path. Please configure it using aseprite.exec property (ie: in your ~/.gradle/gradle.properties) aseprite.exec=<path to exec> or using aseprite extension in your build.gradle aseprite { exec=<path to exec> } MacOS specific : point to aseprite located into <aseprite directory>/Aseprite.app/Contents/MacOS/aseprite""") } } What happen if null? Encountered an error, lol!
  172. open class AsepriteTask : DefaultTask() { // ... @TaskAction fun

    export() { val exec = getExecActionFactory().newExecAction() val exts = project.extensions.getByType(AsepritePluginExtentions::class.java) val aseprite = exts.exec ?: invalideAsepritePath() // ... } private fun invalideAsepritePath(): Nothing { TODO("""Missing aseprite executable path. Please configure it using aseprite.exec property (ie: in your ~/.gradle/gradle.properties) aseprite.exec=<path to exec> or using aseprite extension in your build.gradle aseprite { exec=<path to exec> } MacOS specific : point to aseprite located into <aseprite directory>/Aseprite.app/Contents/MacOS/aseprite""") } }
  173. open class AsepriteTask : DefaultTask() { // ... @TaskAction fun

    export() { val exec = getExecActionFactory().newExecAction() val exts = project.extensions.getByType(AsepritePluginExtentions::class.java) val aseprite = exts.exec ?: invalideAsepritePath() // ... } private fun invalideAsepritePath(): Nothing { TODO("""Missing aseprite executable path. Please configure it using aseprite.exec property (ie: in your ~/.gradle/gradle.properties) aseprite.exec=<path to exec> or using aseprite extension in your build.gradle aseprite { exec=<path to exec> } MacOS specific : point to aseprite located into <aseprite directory>/Aseprite.app/Contents/MacOS/aseprite""") } }
  174. Screens

  175. Scrollpane Label Button Select box

  176. val scene = verticalGroup { setFillParent(true) pad(10f) label("Hello JavaZone!") horizontalGroup

    { textButton("Click On Me") label("<-- here") } label("Another label...") selectBoxOf(GdxArray<String>().apply { add("value 1") add("value 2") add("value 3") }) } stage = Stage(FitViewport(screenWidth, screenHeight)) stage.addActor(scene) Hello JavaZone!
  177. val scene = verticalGroup { setFillParent(true) pad(10f) label("Hello JavaZone!") horizontalGroup

    { textButton("Click On Me") label("<-- here") } label("Another label...") selectBoxOf(GdxArray<String>().apply { add("value 1") add("value 2") add("value 3") }) } stage = Stage(FitViewport(screenWidth, screenHeight)) stage.addActor(scene) Hello JavaZone!
  178. val scene = verticalGroup { setFillParent(true) pad(10f) label("Hello JavaZone!") horizontalGroup

    { textButton("Click On Me") label("<-- here") } label("Another label...") selectBoxOf(GdxArray<String>().apply { add("value 1") add("value 2") add("value 3") }) } stage = Stage(FitViewport(screenWidth, screenHeight)) stage.addActor(scene) Hello JavaZone!
  179. val scene = verticalGroup { setFillParent(true) pad(10f) label("Hello JavaZone!") horizontalGroup

    { textButton("Click On Me") label("<-- here") } label("Another label...") selectBoxOf(GdxArray<String>().apply { add("value 1") add("value 2") add("value 3") }) } stage = Stage(FitViewport(screenWidth, screenHeight)) stage.addActor(scene) Hello JavaZone!
  180. Game screen screen screen screen screen

  181. open class MenuScreen(val game: Game) : ScreenAdapter() { override fun

    render(delta: Float) { // clear the screen Gdx.gl.glClearColor(0f, 0f, 0f, 1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) batch.begin() // ... batch.end() if (goToMenu) { game.setScreen(menuScreen) } } }
  182. open class MenuScreen(val game: Game) : ScreenAdapter() { override fun

    render(delta: Float) { // clear the screen Gdx.gl.glClearColor(0f, 0f, 0f, 1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) batch.begin() // ... batch.end() if (goToMenu) { game.setScreen(menuScreen) } } } ⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠ game.setScreen(screen) 
 is not the same that
 game.screen = screen ⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠
  183. Pitfalls

  184. Garbage Collector Garbage Collector Garbage Collector Garbage Collecto arbage C

    Garbage STOP THE WORLD
  185. altLayers = map.layers .mapIndexed { index, layer -> index to

    layer } .filter { it.second.name.startsWith("alt_") } .map { it.first } .toIntArray() List 1 List 2 List 3 List 4 mutableList
  186. val ball = Rectangle(68f, 64f, 5f, 5f) val player =

    Rectangle(64f, 64f, 100f, 10f) // the ball hit the player? player.overlaps(ball)
  187. val pool = object : Pool<Rectangle>() { override fun newObject():

    Rectangle = Rectangle() } // ... val ball = pool.obtain().apply { set(68f, 64f, 5f, 5f) } val player = pool.obtain().apply { set(64f, 64f, 100f, 10f) } // the ball hit the player? player.overlaps(ball) pool.free(ball) pool.free(player)
  188. val pool = object : Pool<Rectangle>() { override fun newObject():

    Rectangle = Rectangle() } // ... val ball = pool.obtain().apply { set(68f, 64f, 5f, 5f) } val player = pool.obtain().apply { set(64f, 64f, 100f, 10f) } // the ball hit the player? player.overlaps(ball) pool.free(ball) pool.free(player)
  189. val pool = object : Pool<Rectangle>() { override fun newObject():

    Rectangle = Rectangle() } // ... val ball = pool.obtain().apply { set(68f, 64f, 5f, 5f) } val player = pool.obtain().apply { set(64f, 64f, 100f, 10f) } // the ball hit the player? player.overlaps(ball) pool.free(ball) pool.free(player)
  190. val pool = object : Pool<Rectangle>() { override fun newObject():

    Rectangle = Rectangle() } // ... val ball = pool.obtain().apply { set(68f, 64f, 5f, 5f) } val player = pool.obtain().apply { set(64f, 64f, 100f, 10f) } // the ball hit the player? player.overlaps(ball) pool.free(ball) pool.free(player)
  191. Shaders This section has nothing to do with Kotlin

  192. void main() {
 vec4 sum = vec4(0);
 
 float step

    = iResolution.y;
 // y
 for(float i = -area ; i <= area ; i += 1.) {
 // x
 for(float j = -area ; j <= area ; j += 1.) {
 float x = v_texCoords.x + i / iResolution.x;
 float y = v_texCoords.y + j / iResolution.y;
 sum += texture2D(u_texture, vec2(x, y)) * 0.005;
 }
 }
 gl_FragColor = texture2D(u_texture, v_texCoords) + sum;
 } OpenGL Shading Language (GLSL)
  193. None
  194. None
  195. None
  196. None
  197. None
  198. None
  199. None
  200. None
  201. None
  202. if (color_pixel < cutoff) { // light dark pixel =

    gl_FragColor = vec4(0.3, 0.2, 0.4., 1.0); } else { pixel = color_pixel; } 0 1 Cut off
  203. if (color_pixel < cutoff) { // light dark pixel =

    gl_FragColor = vec4(0.3, 0.2, 0.4., 1.0); } else { pixel = color_pixel; }
  204. None
  205. None
  206. None
  207. https://www.shadertoy.com/view/ld3Gz2

  208. fun makeGames()

  209. fun makeGames()

  210. Beat The High Score Game https://github.com/dwursteisen/beat-the-high-score libGDX Lib https://libgdx.badlogicgames.com/ KTX

    
 (This is NOT Android KTX) Lib https://github.com/libktx/ktx/ libGDX addons Lib https://github.com/dwursteisen/libgdx-addons/ kTerminal Lib https://github.com/heatherhaks/kterminal Game Services Lib https://github.com/MrStahlfelge/gdx-gamesvcs In Game Console
 (A la quake) Lib https://github.com/StrongJoshua/libgdx-inGameConsole Gif Recorder Lib https://github.com/Anuken/GDXGifRecorder Tiled Editor Editor https://www.mapeditor.org Aseprite Editor https://www.aseprite.org
  211. Beat The High Score Game https://github.com/dwursteisen/beat-the-high-score libGDX Lib https://libgdx.badlogicgames.com/ KTX

    
 (This is NOT Android KTX) Lib https://github.com/libktx/ktx/ libGDX addons Lib https://github.com/dwursteisen/libgdx-addons/ kTerminal Lib https://github.com/heatherhaks/kterminal Game Services Lib https://github.com/MrStahlfelge/gdx-gamesvcs In Game Console
 (A la quake) Lib https://github.com/StrongJoshua/libgdx-inGameConsole Gif Recorder Lib https://github.com/Anuken/GDXGifRecorder Tiled Editor Editor https://www.mapeditor.org Aseprite Editor https://www.aseprite.org
  212. @dwursteisen http://bit.ly/2poORfk