Share
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!
Code: https://github.com/dwursteisen/beat-the-high-score
Game: https://play.google.com/store/apps/details?id=com.github.dwursteisen.beat
Video: https://www.youtube.com/watch?v=kDxerDYelLs
(KotlinConf 2018 - 4th October 2018 - Amsterdam)
David
Transcript
199 slides
-
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 -
-Gilles Allain In a mighty move, he stole all the
chickens of the farm... -
-Gilles Allain ...this strike have given you powers to bring
back... ...our friends! -
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" } -
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" } -
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" } -
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" } -
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" } -
public void render(float delta) { movePlayer(delta); moveEnemies(delta); moveWorld(delta); renderWorld();
renderEnemies(); renderPlayer(); } -
public void render(float delta) { movePlayer(delta); moveEnemies(delta); moveWorld(delta); renderWorld();
renderEnemies(); renderPlayer(); } -
public void render(float delta) { movePlayer(delta); moveEnemies(delta); moveWorld(delta); renderWorld();
renderEnemies(); renderPlayer(); } -
public void render(float delta) { movePlayer(delta); moveEnemies(delta); moveWorld(delta); renderWorld();
renderEnemies(); renderPlayer(); } 60x seconds -
class MyScreen : ScreenAdapter() { lateinit var batch: SpriteBatch override
fun create() { batch = SpriteBatch() } override fun render() { batch.begin() batch.draw(image, x, y) batch.end() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: SpriteBatch override
fun create() { batch = SpriteBatch() } override fun render() { batch.begin() batch.draw(image, x, y) batch.end() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: SpriteBatch override
fun create() { batch = SpriteBatch() } override fun render() { batch.begin() batch.draw(image, x, y) batch.end() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: SpriteBatch override
fun create() { batch = SpriteBatch() } override fun render() { batch.begin() batch.draw(image, x, y) batch.end() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: ShapeRenderer override
fun create() { batch = ShapeRenderer() } override fun render() { batch.begin(ShapeRenderer.ShapeType.Filled) batch.color = Color.CHARTREUSE batch.circle(x, y, radius) batch.end() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: ShapeRenderer override
fun create() { batch = ShapeRenderer() } override fun render() { batch.begin(ShapeRenderer.ShapeType.Filled) batch.color = Color.CHARTREUSE batch.circle(x, y, radius) batch.end() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: ShapeRenderer override
fun create() { batch = ShapeRenderer() } override fun render() { batch.begin(ShapeRenderer.ShapeType.Filled) batch.color = Color.CHARTREUSE batch.circle(x, y, radius) batch.end() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: ShapeRenderer override
fun create() { batch = ShapeRenderer() } override fun render() { batch.begin(ShapeRenderer.ShapeType.Filled) batch.color = Color.CHARTREUSE batch.circle(x, y, radius) batch.end() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: SpriteBatch override
fun create() { batch = SpriteBatch() } override fun render() { batch.begin() batch.draw(image, x, y) batch.end() } } -
class MyScreen : KtxScreen { val batch: SpriteBatch = SpriteBatch()
override fun render() { batch.use { it.draw(sprite, x, y) } } } Inherit -
class MyScreen : ScreenAdapter() { lateinit var batch: ShapeRenderer private
val position: Vector2 = Vector2(0f, 0f) override fun create() { batch = ShapeRenderer() } 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() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: ShapeRenderer private
val position: Vector2 = Vector2(0f, 0f) override fun create() { batch = ShapeRenderer() } 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() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: ShapeRenderer private
val position: Vector2 = Vector2(0f, 0f) override fun create() { batch = ShapeRenderer() } 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() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: ShapeRenderer private
val position: Vector2 = Vector2(0f, 0f) override fun create() { batch = ShapeRenderer() } 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() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: ShapeRenderer private
val position: Vector2 = Vector2(0f, 0f) private var time: Float = 0f override fun create() { batch = ShapeRenderer() } override fun render(delta: Float) { val (x, y) = position time += delta batch.begin(ShapeRenderer.ShapeType.Filled) batch.color = Color.CHARTREUSE batch.circle(x + MathUtils.cos(time), y, radius) batch.end() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: ShapeRenderer private
val position: Vector2 = Vector2(0f, 0f) private var time: Float = 0f override fun create() { batch = ShapeRenderer() } override fun render(delta: Float) { val (x, y) = position time += delta batch.begin(ShapeRenderer.ShapeType.Filled) batch.color = Color.CHARTREUSE batch.circle(x + MathUtils.cos(time), y, radius) batch.end() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: ShapeRenderer private
val position: Vector2 = Vector2(0f, 0f) private var time: Float = 0f override fun create() { batch = ShapeRenderer() } override fun render(delta: Float) { val (x, y) = position time += delta batch.begin(ShapeRenderer.ShapeType.Filled) batch.color = Color.CHARTREUSE batch.circle(x + MathUtils.cos(time), y, radius) batch.end() } } -
class MyScreen : ScreenAdapter() { lateinit var batch: ShapeRenderer private
val position: Vector2 = Vector2(0f, 0f) private var time: Float = 0f override fun create() { batch = ShapeRenderer() } override fun render(delta: Float) { val (x, y) = position time += delta batch.begin(ShapeRenderer.ShapeType.Filled) batch.color = Color.CHARTREUSE batch.circle(x + MathUtils.cos(time), y, radius) batch.end() } } -
/** * 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 -
/** * 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) -
entity.add(Ball()) .add(Position(-100 v2 -100f)) .add(Size(8 v2 9)) .add(Rotation(4 v2 4))
Create a new Vector2 infix fun Number.v2(other: Number): Vector2 { return Vector2(this.toFloat(), other.toFloat()) } -
Racket Side Side Ball Alpha dy = dy * -1
* alpha dx = dx * alpha -
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) -
val ball = Rectangle(68f, 64f, 5f, 5f) val player =
Rectangle(64f, 64f, 100f, 10f) // the ball hit the player? player.overlaps(ball) -
val ball = Rectangle(68f, 64f, 5f, 5f) val player =
Rectangle(64f, 64f, 100f, 10f) // the ball hit the player? player.overlaps(ball) -
val ball = Rectangle(68f, 64f, 5f, 5f) val player =
Rectangle(64f, 64f, 100f, 10f) // the ball hit the player? player.overlaps(ball) -
var world = World(Vector2(0, -10), true) // ... // Do
in render method world.step(1/60f, 6, 2); 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() -
var world = World(Vector2(0, -10), true) // ... // Do
in render method world.step(1/60f, 6, 2); 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 -
var world = World(Vector2(0, -10), true) // ... // Do
in render method world.step(1/60f, 6, 2); 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() -
var world = World(Vector2(0, -10), true) // ... // Do
in render method world.step(1/60f, 6, 2); 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 » -
var world = World(Vector2(0, -10), true) // ... // Do
in render method world.step(1/60f, 6, 2); 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() -
var world = World(Vector2(0, -10), true) // ... // Do
in render method world.step(1/60f, 6, 2); 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() -
var world = World(Vector2(0, -10), true) // ... // Do
in render method world.step(1/60f, 6, 2); 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() -
y = Interpolation.bounceOut.apply(time) * 100f Non linear Should be a
percentage (0..1) y will go from 0 to 100 -
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) } } } -
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 ⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠ -
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) } } -
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 -
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) } } -
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() -
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 -
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 -
// 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) -
// 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) -
// 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) -
// 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 -
// 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) -
// 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 -
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) -
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) -
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) -
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) -
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 -
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) -
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) } -
val scene = verticalGroup { setFillParent(true) pad(10f) label("Hello KotlinConf!") 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) -
val scene = verticalGroup { setFillParent(true) pad(10f) label("Hello KotlinConf!") 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) -
val scene = verticalGroup { setFillParent(true) pad(10f) label("Hello KotlinConf!") 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) -
val scene = verticalGroup { setFillParent(true) pad(10f) label("Hello KotlinConf!") 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) -
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 -
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) -
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 -
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 Accessing like a map -
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 -
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 -
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 -
Garbage Collector Garbage Collector Garbage Collector Garbage Collecto arbage C
Garbage STOP THE WORLD -
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 -
val ball = Rectangle(68f, 64f, 5f, 5f) val player =
Rectangle(64f, 64f, 100f, 10f) // the ball hit the player? player.overlaps(ball) -
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) -
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) -
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) -
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) -
// 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() -
// 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() -
// 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() -
// 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() -
tmxMap.layers["bricks"].objects.forEach { brick -> val hit: Any = brick.properties["hit"] }
Error prone Error prone -
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 } -
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 } -
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 -
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 } -
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""") } } -
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 happens if null? Encountered an error, lol! -
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""") } } -
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""") } } -
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) -
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 -
if (color_pixel < cutoff) { // light dark pixel =
gl_FragColor = vec4(0.3, 0.2, 0.4., 1.0); } else { pixel = color_pixel; } -
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