Slide 1

Slide 1 text

Kotlin pour le développement d’un jeu avec libGDX Avantages & inconvénients

Slide 2

Slide 2 text

David Wursteisen

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

John Carmack

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

Slide 11

Slide 11 text

PRODUCTIVITYYYYYYYY

Slide 12

Slide 12 text

Core iOS GWT Android Desktop

Slide 13

Slide 13 text

Core Android Desktop

Slide 14

Slide 14 text

class HelloWorld : ScreenAdapter() { override fun show() { } override fun render(delta: Float) { } } Initialisation de l’écran de jeu Rendu de l’écran de jeu

Slide 15

Slide 15 text

class HelloWorld : ScreenAdapter() { var batch: SpriteBatch? override fun show() { batch = SpriteBatch() } override fun render(delta: Float) { batch!!.begin() // ... batch!!.end() } }

Slide 16

Slide 16 text

class HelloWorld : ScreenAdapter() { var batch: SpriteBatch? override fun show() { batch = SpriteBatch() } override fun render(delta: Float) { batch!!.begin() // ... batch!!.end() } }

Slide 17

Slide 17 text

class HelloWorld : ScreenAdapter() { lateinit var batch: SpriteBatch override fun show() { batch = SpriteBatch() } override fun render(delta: Float) { batch.begin() // ... batch.end() } }

Slide 18

Slide 18 text

class HelloWorld : ScreenAdapter() { lateinit var batch: SpriteBatch override fun show() { batch = SpriteBatch() } override fun render(delta: Float) { batch.begin() // ... batch.end() } }

Slide 19

Slide 19 text

fun String.toColor(): Color = Color.valueOf(this) val background = « 0089d5".toColor() fun ShapeRenderer.rect(pos: Vector2, size: Vector2) { this.rect(pos.x, pos.y, size.x, size.y) } batch.rect(pos1, pos2)

Slide 20

Slide 20 text

fun String.toColor(): Color = Color.valueOf(this) val background = « 0089d5".toColor() fun ShapeRenderer.rect(pos: Vector2, size: Vector2) { this.rect(pos.x, pos.y, size.x, size.y) } batch.rect(pos1, pos2) Méthode d’extension = étendre une API à son besoin

Slide 21

Slide 21 text

Hexagonal Architecture ?

Slide 22

Slide 22 text

object EntityFactory { fun player(engine: PooledEngine, assets: AssetManager): Unit { // … } Couplage ! Couplage !

Slide 23

Slide 23 text

const val TOUCH_DOWN = 1 const val TOUCH_UP = 2 const val TOUCH_MOVE = 3 Compile-Time Constants Properties the value of which is known at compile time can be marked as compile time constants using the const modifier. Such properties need to fulfil the following requirements: Top-level or member of an object Initialized with a value of type String or a primitive type No custom getter

Slide 24

Slide 24 text

private void readMeasureHeader(InputStream is, int measureIndex) throws IOException { int header = helper.readUnsignedByte(is); boolean repeatStart = ((header & ParserHelper.BITMASK_3) != 0); boolean doubleBar = ((header & ParserHelper.BITMASK_8) != 0); int numerator = 0; if ((header & ParserHelper.BITMASK_1) != 0) { numerator = helper.readByte(is); } int denominator = 0; if ((header & ParserHelper.BITMASK_2) != 0) { denominator = helper.readByte(is); } }

Slide 25

Slide 25 text

private void readMeasureHeader(InputStream is, int measureIndex) throws IOException { int header = helper.readUnsignedByte(is); boolean repeatStart = ((header & ParserHelper.BITMASK_3) != 0); boolean doubleBar = ((header & ParserHelper.BITMASK_8) != 0); int numerator = 0; if ((header & ParserHelper.BITMASK_1) != 0) { numerator = helper.readByte(is); } int denominator = 0; if ((header & ParserHelper.BITMASK_2) != 0) { denominator = helper.readByte(is); } }

Slide 26

Slide 26 text

TOUCH_DOWN shl 3 TOUCH_DOWN and 2 TOUCH_DOWN << 3 TOUCH_DOWN & 2

Slide 27

Slide 27 text

Direction Position

Slide 28

Slide 28 text

Vector2(3f, 5f).set(entity[cameraData].direction) .scl(5f) .add(entity[position].value) .add(halfX, halfY val v1 = Vector2(3f, 5f) + Vector2(4f, 2f) v2 = v1 * 5f

Slide 29

Slide 29 text

Vector2(3f, 5f).set(entity[cameraData].direction) .scl(5f) .add(entity[position].value) .add(halfX, halfY val v1 = Vector2(3f, 5f) + Vector2(4f, 2f) v2 = v1 * 5f Surcharge d’opérateur (add) Surcharge d’opérateur (scl)

Slide 30

Slide 30 text

engine.addEntity({ e -> val left = spriteDef.slices("left") val leftRegion = left.textureRegion(assets["sheets/pico8/keyboard.png"]) e.add(Controller()) e.add(Position(42 v2 26)) e.add(Size(16 v2 16)) e.add(EntityRender(texture = leftRegion)) e.add(ButtonComponent(ButtonSystem.LEFT)) e.add(StateComponent()) }) Vector2

Slide 31

Slide 31 text

engine.addEntity({ e -> val left = spriteDef.slices("left") val leftRegion = left.textureRegion(assets["sheets/pico8/keyboard.png"]) e.add(Controller()) e.add(Position(42 v2 26)) e.add(Size(16 v2 16)) e.add(EntityRender(texture = leftRegion)) e.add(ButtonComponent(ButtonSystem.LEFT)) e.add(StateComponent()) }) infix fun Int.v2(that: Int): Vector2 = Vector2(this.toFloat(), that.toFloat())

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

Position Couleur Joueur Etat

Slide 35

Slide 35 text

val player = Entity() player.add(Player())) player.add(Position(100 v2 100)) player.add(ColorSplash(Color.GREEN)) player.add(StateComponent()) Position Couleur Joueur Etat

Slide 36

Slide 36 text

ComponentMapper pm = ComponentMapper.getFor(PositionComponent.class); ComponentMapper vm = ComponentMapper.getFor(VelocityComponent.class); ... PositionComponent position = pm.get(entity); VelocityComponent velocity = vm.get(entity); Extracteur Extraction

Slide 37

Slide 37 text

inline fun EntitySystem.get(): ComponentMapper = ComponentMapper.getFor(T::class.java) private val move: ComponentMapper = ComponentMapper.getFor(TurnMove::class.java) Extracteur

Slide 38

Slide 38 text

inline fun EntitySystem.get(): ComponentMapper = ComponentMapper.getFor(T::class.java) private val move: ComponentMapper = ComponentMapper.getFor(TurnMove::class.java) Inline + reified

Slide 39

Slide 39 text

inline fun EntitySystem.get(): ComponentMapper = ComponentMapper.getFor(T::class.java) private val move: ComponentMapper = get()

Slide 40

Slide 40 text

private val move: ComponentMapper = get() private val position: ComponentMapper = get() private val state: ComponentMapper = get() private val direction: ComponentMapper = get()

Slide 41

Slide 41 text

PositionComponent position = pm.get(entity); Extraction

Slide 42

Slide 42 text

val position: Position = pm.get(entity)

Slide 43

Slide 43 text

val position: Position = entity.get(pm)

Slide 44

Slide 44 text

val position: Position = entity[pm]

Slide 45

Slide 45 text

entity[move].turn(entity[direction].value) entity[state].time = 0f entity[animated].animation = animation["dragon_move"] Direction Etat …

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

altLayers = map.layers .mapIndexed { index, layer -> index to layer } .filter { it.second.name.startsWith("alt_") } .map { it.first } .toIntArray() mutableList !

Slide 50

Slide 50 text

val a = Array(5, { "" }) val b = Array() Différence ? Array libGDX

Slide 51

Slide 51 text

import com.badlogic.gdx.utils.Array as GdxArray val a = Array(5, { "" }) val b = GdxArray()

Slide 52

Slide 52 text

interface EventListener { fun onEvent(event: Event, eventData: EventData) } fun register(eventListener: EventListener, vararg events: Event) { events.forEach { val lst = listeners[it] if (lst == null) { listeners.put(it, listOf(eventListener)) } else { listeners.put(it, lst + eventListener) } } }

Slide 53

Slide 53 text

interface EventListener { fun onEvent(event: Event, eventData: EventData) } fun register(eventListener: EventListener, vararg events: Event) { events.forEach { val lst = listeners[it] if (lst == null) { listeners.put(it, listOf(eventListener)) } else { listeners.put(it, lst + eventListener) } } } Interface avec une méthode

Slide 54

Slide 54 text

eventBus.register(object : EventListener { override fun onEvent(event: Event, eventData: EventData) { game.loadingScreen() } }, GameEvents.EVENT_GAME_MAIN_MENU) Class anonyme

Slide 55

Slide 55 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 56

Slide 56 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 57

Slide 57 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) } Etat Event Transition

Slide 58

Slide 58 text

Slide 59

Slide 59 text

On reste en contact ? @dwursteisen

Slide 60

Slide 60 text

https://goo.gl/4UcQYx En attendant… téléchargez 
 Wiz le magicien 
 sur le Play Store Android