3. Agenda
• What am I doing here?
• A small tour of functional reactive programming
in Scala
• What’s a game, from a functional perspective
• Type-safe Entity-Component-System
implementation(bonus)
5. What Am I Doing Here?
• Introducing Scala
!
•
•
6. What Am I Doing Here?
• Introducing Scala
• Promoting LibGDX(hopefully)
•
•
7. What Am I Doing Here?
• Introducing Scala
• Promoting LibGDX(hopefully)
• Sharing my humble opinions about reactive
functional programming
•
8. What Am I Doing Here?
• Introducing Scala
• Promoting LibGDX(hopefully)
• Sharing my humble opinions about reactive
functional programming
• It’s not really about game developement
9. What Am I Doing Here?
• Introducing Scala
• Promoting LibGDX(hopefully)
• Sharing my humble opinions about reactive
functional programming
• It’s not really about game developement
(Yes I lied :))
11. I’m Not an Evangelist!
• Scala(or RxScala, or functional programming, or
any other buzzword) is not the holy grail…
!
•
12. I’m Not an Evangelist!
• Scala(or RxScala, or functional programming, or
any other buzzword) is not the holy grail…
• Sometimes they’re ugly…
•
implicit def toGdxArray[T<:AnyRef]!
(iterable: Iterable[T])!
(implicit arg0: ClassTag[T])!
: com.badlogic.gdx.utils.Array[T] = {!
new com.badlogic.gdx.utils.Array[T](iterable.toArray)!
}!
18. LibGDX
• A cross-platform game engine
• Desktop/Android/iOS(robovm)/HTML5(GWT)
• You can use any JVM-based language
• (except HTML5, casue GWT compiles Java
directely)
19. More on LibGDX
• Basic Structure
!
!
!
class MyGame extends Game {!
override def create() {!
// initialize!
}!
!
override def render() {!
val delta = Gdx.graphics.getDeltaTime!
// updateGame(delta)!
// renderGame()!
}!
}!
20. More on LibGDX
• Render
!
!
!
spriteBatch.begin()!
val texture = new TextureRegion(!
! new Texture(Gdx.files.internal(“images/bg.png"))!
)!
spriteBatch.draw(texture, 0, 0}!
spriteBatch.end()!
22. More on LibGDX
• Load assets with different resolutions
!
!
!
val resolutions = List(new Resolution(320, 480, "320x480"),!
new Resolution(640, 1136, "640x1136"))!
val resolver = new ResolutionFileResolver(!
! ! ! ! ! ! new InternalFileHandleResolver(), resolutions)!
val manager = new AssetManager()!
manager.setLoader(classOf[Texture], new TextureLoader(resolver))!
manager.load("images/bg.png", classOf[Texture])!
// will load "images/640x1136/bg.png" on iPhone5!
23. More on LibGDX
• Call native code through Java
!
!
!
new IOSApplication(new Retinol(), config) {!
@Override!
public void log (String tag, String message) {!
Foundation.log("[info] " + tag + ": " + message);!
}!
};!
24. Small Tour of Functional Reactive
Programming in Scala
25. Small Tour of Functional Reactive
Programming in Scala
!
!
!
!
26. Small Tour of Functional Reactive
Programming in Scala
• The “Better Java”
!
!
!
27. Small Tour of Functional Reactive
Programming in Scala
• The “Better Java”
!
!
!
class Vector2 {!
private float x;!
private float y;!
public Vector2(float x, float y) {!
this.x = x;!
this.y = y;!
}!
public int getX() { return x; }!
public int getY() { return y; }!
}!
28. Small Tour of Functional Reactive
Programming in Scala
• The “Better Java”
!
!
!
case class Vector2(x: Float, y: Float)!
29. Small Tour of Functional Reactive
Programming in Scala
• The “Better Java”
!
!
• If there is a missing ID, get ready to watch the world burn
idList.stream().map(id -> dict.get(id).contains(name))!
30. Small Tour of Functional Reactive
Programming in Scala
• The “Better Java”
!
!
• If there is a missing ID, get ready to watch the world burn
… later!
idList.stream().map(id -> dict.get(id).contains(name))!
31. Small Tour of Functional Reactive
Programming in Scala
• The “Better Java”
!
!
•
idList.flatMap(dict.get(_))!
32. Small Tour of Functional Reactive
Programming in Scala
• The “Better Java”
!
!
• Thus, Scala is not just for “lambda expression”.
idList.flatMap(dict.get(_))!
33. Small Tour of Functional Reactive
Programming in Scala
34. Small Tour of Functional Reactive
Programming in Scala
35. Small Tour of Functional Reactive
Programming in Scala
•
36. Small Tour of Functional Reactive
Programming in Scala
• “Purely functional” language = Stateless.
!
!
!
37. Small Tour of Functional Reactive
Programming in Scala
• “Purely functional” language = Stateless.
• Scala is not pure, fortunately.
!
!
38. Small Tour of Functional Reactive
Programming in Scala
• “Purely functional” language = Stateless.
• Scala is not pure, fortunately?
!
!
list.foldLeft(List[Int]()) {!
(accu, elem) => f(elem) :: accu!
}!
for(elem <- list)!
newList = newList :+ elem!
39. Small Tour of Functional Reactive
Programming in Scala
• But it’s definitely a bad idea to pass mutable
non-singleton objects all around.
• Code like this really makes me cringe:
(from Unity)
GameObject.Find("Hand");!
40. Small Tour of Functional Reactive
Programming in Scala
• Thinking “what if I have to explicitly pass every
single dependeny through constructor” makes
me write better OOP code.
• More on this later.
41. Small Tour of Functional Reactive
Programming in Scala
42. Small Tour of Functional Reactive
Programming in Scala
43. Small Tour of Functional Reactive
Programming in Scala
• A way to implement Observer Pattern.
• An Observable is an event source. When a
new event occurs, it notifis all its Observers.
!
!
44. Small Tour of Functional Reactive
Programming in Scala
• A way to implement Observer Pattern.
• An Observable is an event source. When a
new event occurs, it notifis all its Observers.
!
!
val numbers = Observable.from(List(1,2,3,4))!
numbers.subscribe(n => println(n))!
45. Small Tour of Functional Reactive
Programming in Scala
• A way to implement Observer Pattern.
• An Observable is an event source. When a
new event occurs, it notifis all its Observers.
!
!
• Why bother?
val numbers = Observable.from(List(1,2,3,4))!
numbers.subscribe(n => println(n))!
46. Small Tour of Functional Reactive
Programming in Scala
• java.util.Observable isn’t good enough:
• No type parameter
• No composition
• No scheduling
47. Small Tour of Functional Reactive
Programming in Scala
• Type parameter
!
!
!
• util.Observable can only pass Object …
Observable.from(List(1,2,3))!
.map(_.toString)!
// Observable[String]!
48. Small Tour of Functional Reactive
Programming in Scala
• Composition
!
!
• The game loop should wait the assets to get
loaded.
val tickEventRx = ...!
val assetLoadingEventRx = ...!
val processor = GameProcessor(tickEventRx)!
49. Small Tour of Functional Reactive
Programming in Scala
• Composition
!
!
• Naturally dependency injection = better OO!
// The main game loop starts once all assets are loaded!
val newTickRx = tickRx awaitComplete assetLoadingRx!
val processor = GameProcessor(newTickRx)!
50. Small Tour of Functional Reactive
Programming in Scala
• Composition
!
!
•
def awaitComplete[U](that: Observable[U]): Observable[T] = {!
var completed = false!
that.subscribe(!
t => {},!
e => e.printStackTrace(),!
() => completed = true!
)!
observable.filter(t => completed)!
}!
51. Small Tour of Functional Reactive
Programming in Scala
• Scheduling
!
!
!
• It’s easy to integrate with other libraries cause
we can control when an Observable emits.
val TickScheduler: Scheduler = new rx.Scheduler {!
override def createWorker: rx.Scheduler.Worker = new TimerWorker!
}!
class TimerWorker extends rx.Scheduler.Worker {!
override def schedule(…): Subscription = {!
Timer.schedule(task, delaySeconds.toFloat)!
}!
}
52. What’s a Game, from a
Functional Perspective
• Game = Simulation with Inputs
!
!
53. What’s a Game, from a
Functional Perspective
• Simulation = apply a function repeatively
!
!
finalWorld = update(update(...update(world)...))!
update(world) = world of next tick
54. What’s a Game, from a
Functional Perspective
• Simulation = apply a function per frame
!
!
def onFrame(delta) {!
world = update(world, delta)!
render(world)!
}!
55. What’s a Game, from a
Functional Perspective
• Simulation = apply a function per frame
!
!
tickEventRx.subscribe(tickEvent =>!
world = update(world, tickEvent.delta)!
render(world)!
)!
56. What’s a Game, from a
Functional Perspective
• Wait a second! Inputs are events too!
!
!
touchDownEventRx.subscribe(touchDownEvent =>!
world = birdFlyUp(world)!
)!
57. What’s a Game, from a
Functional Perspective
• For example:
!
!
!
val touchSubject = Subject[TouchEvent]()!
!
gdxInput.setInputProcessor(new InputAdapter {!
override def touchDown(screenX: Int, screenY: Int,!
pointer: Int, button: Int): Boolean = {!
touchSubject.onNext(TouchDownEvent(screenX, screenY, pointer, button))!
true!
}!
})!
!
InputEventRxs(!
touchEventRx = touchSubject!
)!
58. What’s a Game, from a
Functional Perspective
• Now we have a consistent way to render images
& update objects & handle player
input(including network input)
!
!
class Processor(rxA, rxB...) {!
rxA.subscribe({!
world = foo(world)!
sideEffectA()!
})!
rxB.subscribe({!
world = foo(world)!
sideEffectB()!
})!
}!
59. What’s a Game, from a
Functional Perspective
• Now we have a consistent way to render images
& update objects & handle player
input(including network input)
!
!
class Processor(rxA, rxB...) {!
rxA.subscribe({!
world = foo(world)!
sideEffectA()!
})!
rxB.subscribe({!
world = foo(world)!
sideEffectB()!
})!
}!
60. What’s a Game, from a
Functional Perspective
• Necessary Evil…?
!
!
!
61. What’s a Game, from a
Functional Perspective
• The middle way!
!
!
!
trait Procedure!
trait SideEffect extends (World => Unit) with Procedure!
trait Transformation extends (World => World) with Procedure!
object Processor {!
def apply(rxA, rxB...): Observable[Procedure] {!
Observable.from(!
List(rxA.map(...),!
rxB.map(...)...)!
).flatten!
}!
}!
!
Observable.from(processorA, processorB...).flatten!
62. What’s a Game, from a
Functional Perspective
!
• Now we can decide the execution order, log the
procedures, combine similar side-effects, etc…
!
68. Type-Safe Another Entity-
Component-System
• Two questions:
• getComponent may return null.
• How to represent the dependencies between
components? e.g. Rigidbody requires
Transform.
69. Type-Safe Another Entity-
Component-System
• getComponent may return null
• We can make it return Option[Component]
instead of Component
• However it still leads unnecessary pattern
matching and collection traversal…
76. Type-Safe Another Entity-
Component-System
!
• Cake Pattern solved both problems: type safety
and dependency management.
• Downside: we can no longer dynamically add/
remove components into/from an entity.