https://slides.com/tobsefritz/korge/
ITscope GmbH
Java Entwickler
Kotlin Fanboy
Hobby Fotograf
Gaming Nerd
3D Printer
Daddy
Hospital
Kathis Kleiner Sattelit
Swing Plus Remake
Hit Klack Remake
Candy Crush Clone
Magic Maze Online Multiplayer
https://wallpapercave.com/w/wp2967926
It's ME
https://wallpapercave.com/w/wp2967926
KorGE
It's ME
https://wallpapercave.com/w/wp2967926
KorGE
It's ME
Kotlin
https://wallpapercave.com/w/wp2967926
KorGE
It's ME
Kotlin
BeiSpiel
https://wallpapercave.com/w/wp2967926
KorGE
It's ME
Kotlin
BeiSpiel
https://wallpapercave.com/w/wp2967926
Corutines
KorGE
It's ME
Kotlin
BeiSpiel
Performance
https://wallpapercave.com/w/wp2967926
Corutines
KorGE
It's ME
Kotlin
BeiSpiel
Tools
Performance
https://wallpapercave.com/w/wp2967926
Corutines
KorGE
It's ME
Kotlin
BeiSpiel
Tools
Performance
https://wallpapercave.com/w/wp2967926
Testing
Corutines
KorGE
It's ME
Kotlin
BeiSpiel
Tools
Performance
https://wallpapercave.com/w/wp2967926
Testing
Multiplayer
Corutines
KorGE
It's ME
Kotlin
BeiSpiel
Tools
Performance
https://wallpapercave.com/w/wp2967926
Testing
Multiplayer
Corutines
Press Start
Warum ein Spiel programmieren?
JVM
Win
Mac
Linux
JS
Browser
Native
Android
IOS
Kotlin Multiplatform
2.5.3
JVM
Win
Mac
Linux
JS
Browser
Native
Android
IOS
JVM
Win
Mac
Linux
JS
Browser
Win
Mac
Linux
Native
Android
IOS
Common
Code
Kotlin
Compiler
LLVM IR
JVM
Win
Mac
Linux
JS
Browser
Win
Mac
Linux
Native
Android
IOS
KorGE
Libs
Common
Code
Kotlin
Compiler
LLVM IR
JVM
Win
Mac
Linux
JS
Browser
Win
Mac
Linux
Native
Android
IOS
KorGE
Libs
Common
Code
expect
expect fun play(file: Sound)
actual
actual
actual
actual
actual
Features
public class Person {
private final String firstName;
private final String lastName;
private final LocalDate birthDay;
public Person(String firstName, String lastName,
LocalDate birthDay) {
this.firstName = firstName;
this.lastName = lastName;
this.birthDay = birthDay;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public LocalDate getBirthDay() {
return birthDay;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(firstName, person.firstName) &&
Objects.equals(lastName, person.lastName) &&
Objects.equals(birthDay, person.birthDay);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName, birthDay);
}
@Override
public String toString() {
return "Person{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", birthDay=" + birthDay +
'}';
}
}
data class PersonK(
val firstName: String,
val lastName: String,
val birthDay: LocalDate
)
Java
Kotlin
Prägnanz
public class RectangleJava {
private int x = 0;
private int y = 0;
private int width = 0;
private int height = 0;
public boolean contains(int x, int y) {
var inWidth = (x >= this.x) && (x <= this.x + width);
var inHeight = (y >= this.y) && (y <= this.y + height);
return inWidth && inHeight;
}
}
Java
public class RectangleKotlin {
private val x = 0;
private val y = 0;
private val width = 0;
private val height = 0;
public fun contains(x: Int, y: Int): Boolean {
val inWidth = (x >= this.x) && (x <= this.x + width);
val inHeight = (y >= this.y) && (y <= this.y + height);
return inWidth && inHeight;
}
}
Kotlin
public fun public private fun
new
;
class RectangleKotlinNice {
private val x = 0
private val y = 0
private val width = 0
private val height = 0
fun contains(x: Int, y: Int): Boolean {
val inWidth = x in (x .. this.x + width)
val inHeight = y in (y .. this.y + height)
return inWidth && inHeight
}
}
Kotlin
Features
Prägnant
Sicher
Interoperabel
Optimales Tooling
Frei & OpenSource
Inline Classes
Extensions
Contracts
Operator Overloading
Gradle DSL
Data Classes
Coroutines
The Tree Game
The Tree Game
The Tree Game
JS
Browser
runAndroidRelease
runJs
runJvm
runNativeRelease
Native
Android
Native
IOS
Win
Mac
Linux
Native
JVM
Win
Mac
Linux
Tasks > run
iosRunSimulatorDebug
Lesson learned
The Tree Game
Lesson learned
image(backgroundBitmap){
size(gameWidth, gameHeight)
}
image(treeBitmap).centerOnStage()
Image(appleBitmap).addTo(this)
container {
name = "Happy Sun"
circle(radius = 50.0, fill = YELLOW)
circle(radius = 8.0, fill = BLACK).position(60, 20)
circle(radius = 8.0, fill = BLACK).position(24, 20)
circle(radius = 10.0, fill = RED).position(50, 60)
position(50, 50)
}
The Tree Game
class Bird(...){
init {
onClick {
hit()
}
}
class Bird(...){
init {
onCollision(filter = { it is Image}) {
if (it.name == "apple") {
it.removeFromParent()
eatApple()
}
}
}
}
Lesson learned
resourcesVfs["bird.mp3"].readSound().play()
The Tree Game
private suspend fun hit() {
tween(this::y[gameHeight],
time = 1.seconds, easing = EASE_IN)
tween(this::rotation[Random[-40, 40].degrees],
time = 100.milliseconds, easing = EASE_IN)
}
EASE_IN_ELASTIC EASE_OUT_ELASTIC EASE_OUT_BOUNCE LINEAR EASE_IN EASE_OUT EASE_IN_OUT EASE_OUT_IN EASE_IN_BACK EASE_OUT_BACK
EASE_IN_OUT_BACK EASE_OUT_IN_BACK EASE_IN_OUT_ELASTIC EASE_OUT_IN_ELASTIC EASE_IN_BOUNCE EASE_IN_OUT_BOUNCE EASE_OUT_IN_BOUNCE EASE_IN_QUAD EASE_OUT_QUAD EASE_IN_OUT_QUAD
Lesson learned
The Tree Game
private suspend fun hit() {
tween(this::y[gameHeight],
time = 1.seconds, easing = EASE_IN)
tween(this::rotation[Random[-40, 40].degrees],
time = 100.milliseconds, easing = EASE_IN)
}
Lesson learned
class Bird(...){
suspend fun startFlying() {
while (!hit) {
x += 5
delay(20.milliseconds)
if (x > gameWidth) {
respawn()
}
}
}
}
Coroutines
synchron & blockierend
main Thread
IO
Verarbeitung
App tut nix. CPU schläft.
synchron & blockierend
Neue Threads
sind teuer!
komplex!
main Thread
wait & join
Coroutines
Coroutinen sind leichtgewichtige Threads.
Sie können angehalten werden,
ohne den Thread zu blockieren.
Suspending
Launch startet eine Coroutine und
gibt einen Job zurück.
Async ist wie Launch, nur mit Rückgabewert.
Await wartet auf den Rückgabewert.
Async / Await
Launch
Coroutines
fun loopWithThreads() {
val c = AtomicLong()
println("🦙🦙🦙🦙🦙🦙🦙🦙🦙🦙")
measureTime {
for (i in 1..100_000) {
thread(start = true) {
c.addAndGet(1).printProgress()
}
}
}
}
fun loopWithCoroutine() {
val c = AtomicLong()
println("🦙🦙🦙🦙🦙🦙🦙🦙🦙🦙")
measureTime {
for (i in 1..100_000) {
GlobalScope.launch {
c.addAndGet(1).printProgress()
}
}
}
}
🦙🦙🦙🦙🦙🦙🦙🦙🦙🦙
🥩🥩🥩
Lama Progress Bar
Coroutines
🦙🦙🦙🦙🦙🦙🦙🦙🦙🦙
🥩🥩🥩🥩🥩🥩🥩🥩🥩🥩
time: 0m 5s 363ms
🦙🦙🦙🦙🦙🦙🦙🦙🦙🦙
🥩🥩🥩🥩🥩🥩🥩🥩🥩🥩
time: 0m 0s 145ms
fun loopWithThreads() {
val c = AtomicLong()
println("🦙🦙🦙🦙🦙🦙🦙🦙🦙🦙")
measureTime {
for (i in 1..100_000) {
thread(start = true) {
c.addAndGet(1).printProgress()
}
}
}
}
fun loopWithCoroutine() {
val c = AtomicLong()
println("🦙🦙🦙🦙🦙🦙🦙🦙🦙🦙")
measureTime {
for (i in 1..100_000) {
GlobalScope.launch {
c.addAndGet(1).printProgress()
}
}
}
}
Coroutines
Coroutines
mario.tween(mario::x[120], time = 4.seconds)
lugigi.tween(mario::x[120], time = 4.seconds)
Coroutines
mario.tween(mario::x[120], time = 4.seconds)
lugigi.tween(mario::x[120], time = 4.seconds)
Coroutines
mario.tween(mario::x[120], time = 4.seconds)
lugigi.tween(mario::x[120], time = 4.seconds)
Coroutines
launch{
mario.tween(mario::x[120], time = 4.seconds)
}
launch{
lugigi.tween(mario::x[120], time = 4.seconds)
}
Coroutines
launch{
mario.tween(mario::x[120], time = 4.seconds)
}
launch{
lugigi.tween(mario::x[120], time = 4.seconds)
}
Coroutines
The Tree Game
Coming soon
🆕
Jetzt mit Hörnchen!
The Tree Game
gamepad.down(0, GameButton.LEFT) {
x -= 40
}
gamepad.down(0, GameButton.RIGHT) {
x += 40
}
gamepad.down(0, GameButton.BUTTON0) {
shoot()
}
keys {
down {
if (it.key == Key.LEFT) {
x -= 20
}
if (it.key == Key.RIGHT) {
x += 20
}
if (it.key == Key.SPACE) {
shoot()
}
}
}
Coming soon
solidRect(50, 50, Colors.RED)
solidRect(50, 50, Colors.RED)
solidRect(50, 50, Colors.RED)
solidRect(600, 100, Colors.WHITE)
solidRect(50, 50, Colors.RED)
.registerBodyWithFixture(type = BodyType.DYNAMIC, density = 2, friction = 0.01)
solidRect(50, 50, Colors.RED)
.registerBodyWithFixture(type = BodyType.DYNAMIC)
solidRect(50, 50, Colors.RED)
.registerBodyWithFixture(type = BodyType.DYNAMIC)
solidRect(600, 100, Colors.WHITE)
.registerBodyWithFixture(
type = BodyType.STATIC,
friction = 0.2
)
solidRect(50, 50, Colors.RED)
.registerBodyWithFixture(type = BodyType.DYNAMIC, density = 2, friction = 0.01)
solidRect(50, 50, Colors.RED)
.registerBodyWithFixture(type = BodyType.DYNAMIC)
solidRect(50, 50, Colors.RED)
.registerBodyWithFixture(type = BodyType.DYNAMIC)
solidRect(600, 100, Colors.WHITE)
.registerBodyWithFixture(
type = BodyType.STATIC,
friction = 0.2
)
onClick {
val pos = it.currentPosLocal
solidRect(50, 50, Colors.RED).position(pos.x, pos.y).rotation(randomAngle())
.registerBodyWithFixture(type = BodyType.DYNAMIC)
}
fun randomAngle(): Angle = Random.nextInt(0, 90).degrees
val container = fixedSizeContainer(width, height)
container.korui {
vertical {
layoutChildrenPadding = 2
horizontal {
preferredWidth = 100.percent
button("HELLO", {
preferredWidth = 70.percent
})
button("WORLD", {
preferredWidth = 30.percent
preferredHeight = 32.pt
})
}
button("TEST")
checkBox("CheckBox", checked = true)
comboBox("test", listOf("test", "demo"))
}
}
DSL
Absolute Positionierung
uiButton(256.0, 32.0) {
text = "Disabled Button"
position(128, 128)
}
val emitter = resourcesVfs["particle2.pex"].readParticleEmitter()
particleEmitter(emitter)
Axiom Verge
Brigador
2016
2015
Edit Worlds
10K
10K
1Mio
100K
need
more?
flag.png 230 Bytes
suspend fun main() = Korge(width = 512, height = 512) {
val flagImage = resourcesVfs["flag.png"].readBitmap()
val flagFilter = FlagFilter()
// "Flag Pole"
solidRect(10, 582, Colors.BLACK).position(30, 30)
solidRect(20, 5, Colors.BLACK).position(25, 25)
// Flag
image(flagImage) {
position(40, 40)
filter = FlagFilter()
}
text("FREE UKRAINE", textSize = 64.0).position(90, 420)
// Propagates the wave over time
addUpdater { dt: TimeSpan -> flagFilter.time = flagFilter.time.plus(dt) }
}
flag.png 230 Bytes
Vorbereiten
Ausrühren
Verifizieren
val world = loadGame()
val mario = Mario()
mario.setPos(0, 5)
mario.jump()
assertEquals(
toad, level[0, 1]
)
Play online together
Game Server
Browser Clients
Websockets
Game Model
Websockets
WebApp
Java Fat Jar
Ktor
runJvm
jar
https://www.hdwallpapers.in/super_mario_3d_world-wallpapers.html
https://globalgamejam.org/news/poster-showcase-2018-0
git clone https://github.com/korlibs/korge-hello-world
😊
Tools