Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

-Gilles Allain In a farm… far, far away…

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

-Gilles Allain In a mighty move, he stole all the chickens of the farm...

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

-Gilles Allain those chickens were our...friends.

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

-Gilles Allain ...this lightning has given you power to bring back... ...our friends!

Slide 11

Slide 11 text

Let's help him !
 (by making a game)

Slide 12

Slide 12 text

David Wursteisen

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

John Carmack

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

Game 
 development Kotlin libGDX

Slide 20

Slide 20 text

You, at the end of 
 this conference Game 
 development Kotlin libGDX

Slide 21

Slide 21 text

Game Development?

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

public void render(float delta) {
 movePlayer(delta);
 moveEnemies(delta);
 moveWorld(delta);
 
 renderWorld();
 renderEnemies();
 renderPlayer();
 } 60x
 seconds

Slide 31

Slide 31 text

(0,0) x y

Slide 32

Slide 32 text

Why libGDX?

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

Slide 35

Slide 35 text

Pavel Quest Destroy Blocks Mirage Realms

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

Hair Dash - http://cleancutgames.com/

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

Bob & Prickle - http://www.crealodegames.com/

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

Let’s make a game!

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

Core iOS GWT Android Desktop

Slide 45

Slide 45 text

void render(float delta) { }

Slide 46

Slide 46 text

fun render(delta: float) { }

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Position = (0, 0) Position = (10, 0) Position = (0, -5) Position = (-10, 5)

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

/** * 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

Slide 66

Slide 66 text

/** * 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)

Slide 67

Slide 67 text

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)

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

Collision engine

Slide 72

Slide 72 text

No content

Slide 73

Slide 73 text

Side Racket Ball

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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)

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

No content

Slide 79

Slide 79 text

No content

Slide 80

Slide 80 text

Box2D

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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 »

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

Traversable

Slide 88

Slide 88 text

The ball and boxes use 
 AABB Physic Engine Wreckages use 
 Box2D Physic Engine

Slide 89

Slide 89 text

Animations

Slide 90

Slide 90 text

No content

Slide 91

Slide 91 text

No content

Slide 92

Slide 92 text

// load texture val texture = Texture(Gdx.files.internal("texture.png")) val split = TextureRegion.split(texture, 128, 128) // select frames val frames = Array() 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(100f, frames) // select frame to draw val keyToRender = animation.getKeyFrame(time)

Slide 93

Slide 93 text

// load texture val texture = Texture(Gdx.files.internal("texture.png")) val split = TextureRegion.split(texture, 128, 128) // select frames val frames = Array() 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(100f, frames) // select frame to draw val keyToRender = animation.getKeyFrame(time)

Slide 94

Slide 94 text

// load texture val texture = Texture(Gdx.files.internal("texture.png")) val split = TextureRegion.split(texture, 128, 128) // select frames val frames = Array() 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(100f, frames) // select frame to draw val keyToRender = animation.getKeyFrame(time)

Slide 95

Slide 95 text

// 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() 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(100f, frames) // select frame to draw val keyToRender = animation.getKeyFrame(time) import com.badlogic.gdx.utils.Array as GdxArray

Slide 96

Slide 96 text

// load texture val texture = Texture(Gdx.files.internal("texture.png")) val split = TextureRegion.split(texture, 128, 128) // select frames val frames = GdxArray() 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(100f, frames) // select frame to draw val keyToRender = animation.getKeyFrame(time)

Slide 97

Slide 97 text

// load texture val texture = Texture(Gdx.files.internal("texture.png")) val split = TextureRegion.split(texture, 128, 128) // select frames val frames = GdxArray() 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(100f, frames) // select frame to draw val keyToRender = animation.getKeyFrame(time) Duration per frame

Slide 98

Slide 98 text

100ms 100ms 100ms 100ms

Slide 99

Slide 99 text

800ms 100ms 100ms 100ms 100ms 100ms 100ms 100ms 100ms 100ms 100ms

Slide 100

Slide 100 text

y = Interpolation.bounceOut.apply(time) * 100f Non linear Should be a percentage (0..1) y will go from 0 to 100

Slide 101

Slide 101 text

No content

Slide 102

Slide 102 text

Linear Bounce Elastic Pow2

Slide 103

Slide 103 text

Field Of View

Slide 104

Slide 104 text

No content

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

viewport.camera.rotate(10f, 0f, 0f, 1f)

Slide 109

Slide 109 text

No content

Slide 110

Slide 110 text

No content

Slide 111

Slide 111 text

No content

Slide 112

Slide 112 text

No content

Slide 113

Slide 113 text

Our world
 (Orientation: Portrait)

Slide 114

Slide 114 text

ScreenViewport

Slide 115

Slide 115 text

StretchViewport

Slide 116

Slide 116 text

FitViewport Clear this zone Clear this zone

Slide 117

Slide 117 text

FillViewport

Slide 118

Slide 118 text

ExtendViewport Displaying this zone

Slide 119

Slide 119 text

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

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

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

Slide 122

Slide 122 text

Around the game

Slide 123

Slide 123 text

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" }

Slide 124

Slide 124 text

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" }

Slide 125

Slide 125 text

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" }

Slide 126

Slide 126 text

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" }

Slide 127

Slide 127 text

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" }

Slide 128

Slide 128 text

Entities & Ashley

Slide 129

Slide 129 text

No content

Slide 130

Slide 130 text

Entity Player Monster Knight Ghoul

Slide 131

Slide 131 text

Composition 
 over 
 Inheritance

Slide 132

Slide 132 text

Position Animation Player State Entity

Slide 133

Slide 133 text

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

Slide 134

Slide 134 text

ComponentMapper player = ComponentMapper.getFor(Player.class); ComponentMapper position = ComponentMapper.getFor(Position.class); ComponentMapper animation = ComponentMapper.getFor(Animation.class); ComponentMapper state = ComponentMapper.getFor(StateComponent.class); ... player.get(entity).life; position.get(entity).xy; animation.get(entity).getFrame(time); state.get(entity).time; Extractor Extraction

Slide 135

Slide 135 text

inline fun EntitySystem.get(): ComponentMapper = 
 ComponentMapper.getFor(T::class.java) inline operator fun Entity.get(mapper: ComponentMapper): T = 
 mapper.get(this)

Slide 136

Slide 136 text

val player: ComponentMapper = get() val position: ComponentMapper = get() val animation: ComponentMapper = get() val state: ComponentMapper = get() // access to components entity[player].life entity[position].xy entity[animation].frame(time) entity[state].time

Slide 137

Slide 137 text

val player = get() val position = get() val animation = get() val state = get() // access to components entity[player].life entity[position].xy entity[animation].frame(time) entity[state].time

Slide 138

Slide 138 text

val player = get() val position = get() val animation = get() val state = get() // access to components entity[player].life entity[position].xy entity[animation].frame(time) entity[state].time Accessing like a map

Slide 139

Slide 139 text

inline fun EntitySystem.get(): ComponentMapper = 
 ComponentMapper.getFor(T::class.java) inline operator fun Entity.get(mapper: ComponentMapper): T = 
 mapper.get(this)

Slide 140

Slide 140 text

Close Open

Slide 141

Slide 141 text

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

Slide 142

Slide 142 text

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

Slide 143

Slide 143 text

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

Slide 144

Slide 144 text

Loading…

Slide 145

Slide 145 text

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)

Slide 146

Slide 146 text

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)

Slide 147

Slide 147 text

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)

Slide 148

Slide 148 text

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)

Slide 149

Slide 149 text

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

Slide 150

Slide 150 text

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)

Slide 151

Slide 151 text

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 AssetManager.get(filename: String): T { return this.get(filename, T::class.java) }

Slide 152

Slide 152 text

Integration with other tools

Slide 153

Slide 153 text

No content

Slide 154

Slide 154 text

No content

Slide 155

Slide 155 text

No content

Slide 156

Slide 156 text

No content

Slide 157

Slide 157 text

No content

Slide 158

Slide 158 text

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

Slide 159

Slide 159 text

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

Slide 160

Slide 160 text

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

Slide 161

Slide 161 text

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

Slide 162

Slide 162 text

No content

Slide 163

Slide 163 text

hit 4 sound chicken.wav

Slide 164

Slide 164 text

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

Slide 165

Slide 165 text

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 }

Slide 166

Slide 166 text

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 }

Slide 167

Slide 167 text

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

Slide 168

Slide 168 text

inline operator fun 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 }

Slide 169

Slide 169 text

val chicken: Aseprite = assets["sheets/chicken"] val idleAnimation: Animation = 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.

Slide 170

Slide 170 text

Json Json Json

Slide 171

Slide 171 text

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= or using aseprite extension in your build.gradle aseprite { exec= } MacOS specific : point to aseprite located into /Aseprite.app/Contents/MacOS/aseprite""") } }

Slide 172

Slide 172 text

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= or using aseprite extension in your build.gradle aseprite { exec= } MacOS specific : point to aseprite located into /Aseprite.app/Contents/MacOS/aseprite""") } } What happen if null? Encountered an error, lol!

Slide 173

Slide 173 text

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= or using aseprite extension in your build.gradle aseprite { exec= } MacOS specific : point to aseprite located into /Aseprite.app/Contents/MacOS/aseprite""") } }

Slide 174

Slide 174 text

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= or using aseprite extension in your build.gradle aseprite { exec= } MacOS specific : point to aseprite located into /Aseprite.app/Contents/MacOS/aseprite""") } }

Slide 175

Slide 175 text

Screens

Slide 176

Slide 176 text

Scrollpane Label Button Select box

Slide 177

Slide 177 text

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

Slide 178

Slide 178 text

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

Slide 179

Slide 179 text

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

Slide 180

Slide 180 text

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

Slide 181

Slide 181 text

Game screen screen screen screen screen

Slide 182

Slide 182 text

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

Slide 183

Slide 183 text

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 ⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠

Slide 184

Slide 184 text

Pitfalls

Slide 185

Slide 185 text

Garbage Collector Garbage Collector Garbage Collector Garbage Collecto arbage C Garbage STOP THE WORLD

Slide 186

Slide 186 text

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

Slide 187

Slide 187 text

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

Slide 188

Slide 188 text

val pool = object : Pool() { 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)

Slide 189

Slide 189 text

val pool = object : Pool() { 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)

Slide 190

Slide 190 text

val pool = object : Pool() { 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)

Slide 191

Slide 191 text

val pool = object : Pool() { 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)

Slide 192

Slide 192 text

Shaders This section has nothing to do with Kotlin

Slide 193

Slide 193 text

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)

Slide 194

Slide 194 text

No content

Slide 195

Slide 195 text

No content

Slide 196

Slide 196 text

No content

Slide 197

Slide 197 text

No content

Slide 198

Slide 198 text

No content

Slide 199

Slide 199 text

No content

Slide 200

Slide 200 text

No content

Slide 201

Slide 201 text

No content

Slide 202

Slide 202 text

No content

Slide 203

Slide 203 text

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

Slide 204

Slide 204 text

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

Slide 205

Slide 205 text

No content

Slide 206

Slide 206 text

No content

Slide 207

Slide 207 text

No content

Slide 208

Slide 208 text

https://www.shadertoy.com/view/ld3Gz2

Slide 209

Slide 209 text

fun makeGames()

Slide 210

Slide 210 text

fun makeGames()

Slide 211

Slide 211 text

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

Slide 212

Slide 212 text

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

Slide 213

Slide 213 text

@dwursteisen http://bit.ly/2poORfk