Kotlin pour le développement d’un jeu avec libGDX : avantage & inconvénient

7843bb075c05be6886a97b77e36758ff?s=47 David
March 14, 2018

Kotlin pour le développement d’un jeu avec libGDX : avantage & inconvénient

libGDX est un framework de développement de jeux vidéo Java. Mais quand on développe un jeu, on veut se concentrer sur le design du jeu, itérer rapidement : on ne veut pas perdre de temps avec des problèmes de syntaxes ou autre. Mais Kotlin s’intègre t’il facilement avec les APIs Java de libGDX ? Qu’apporte Kotlin dans ce contexte et surtout. Les avantages de Kotlin sont-ils toujours des avantages dans le code d’un jeu vidéo ?

(Kotlin Paris Meetup - https://www.meetup.com/fr-FR/Kotlin-Paris-Meetup/events/247733641/ )

7843bb075c05be6886a97b77e36758ff?s=128

David

March 14, 2018
Tweet

Transcript

  1. Kotlin pour le développement d’un jeu avec libGDX Avantages &

    inconvénients
  2. David Wursteisen

  3. None
  4. John Carmack

  5. None
  6. None
  7. None
  8. None
  9. None
  10. PRODUCTIVITYYYYYYYY

  11. Core iOS GWT Android Desktop

  12. Core Android Desktop

  13. class HelloWorld : ScreenAdapter() { override fun show() { }

    override fun render(delta: Float) { } } Initialisation de l’écran de jeu Rendu de l’écran de jeu
  14. class HelloWorld : ScreenAdapter() { var batch: SpriteBatch? override fun

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

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

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

    fun show() { batch = SpriteBatch() } override fun render(delta: Float) { batch.begin() // ... batch.end() } }
  18. 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)
  19. 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
  20. Hexagonal Architecture ?

  21. object EntityFactory { fun player(engine: PooledEngine, assets: AssetManager): Unit {

    // … } Couplage ! Couplage !
  22. 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
  23. 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); } }
  24. 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); } }
  25. TOUCH_DOWN shl 3 TOUCH_DOWN and 2 TOUCH_DOWN << 3 TOUCH_DOWN

    & 2
  26. Direction Position

  27. 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
  28. 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)
  29. 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
  30. 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())
  31. None
  32. None
  33. Position Couleur Joueur Etat

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

    Position Couleur Joueur Etat
  35. ComponentMapper<PositionComponent> pm = ComponentMapper.getFor(PositionComponent.class); ComponentMapper<VelocityComponent> vm = ComponentMapper.getFor(VelocityComponent.class); ... PositionComponent

    position = pm.get(entity); VelocityComponent velocity = vm.get(entity); Extracteur Extraction
  36. inline fun <reified T : Component> EntitySystem.get(): ComponentMapper<T> = ComponentMapper.getFor(T::class.java)

    private val move: ComponentMapper<TurnMove> = ComponentMapper.getFor(TurnMove::class.java) Extracteur
  37. inline fun <reified T : Component> EntitySystem.get(): ComponentMapper<T> = ComponentMapper.getFor(T::class.java)

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

    private val move: ComponentMapper<TurnMove> = get()
  39. private val move: ComponentMapper<TurnMove> = get() private val position: ComponentMapper<Position>

    = get() private val state: ComponentMapper<StateComponent> = get() private val direction: ComponentMapper<Direction> = get()
  40. PositionComponent position = pm.get(entity); Extraction

  41. val position: Position = pm.get(entity)

  42. val position: Position = entity.get(pm)

  43. val position: Position = entity[pm]

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

  45. public void render(float delta) {
 movePlayer(delta);
 moveEnemies(delta);
 moveWorld(delta);
 
 renderWorld();


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


    renderEnemies();
 renderPlayer();
 } 60x
 secondes
  47. Garbage Collector Garbage Collector Garbage Collector Garbage Collecto arbage C

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

    layer } .filter { it.second.name.startsWith("alt_") } .map { it.first } .toIntArray() mutableList !
  49. val a = Array<String>(5, { "" }) val b =

    Array<String>() Différence ? Array libGDX
  50. import com.badlogic.gdx.utils.Array as GdxArray val a = Array<String>(5, { ""

    }) val b = GdxArray<String>()
  51. 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) } } }
  52. 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
  53. eventBus.register(object : EventListener { override fun onEvent(event: Event, eventData: EventData)

    { game.loadingScreen() } }, GameEvents.EVENT_GAME_MAIN_MENU) Class anonyme
  54. 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
  55. 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
  56. 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
  57. On reste en contact ? @dwursteisen

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

    le Play Store Android