Scala usergroup stockholm - reactive integrations with akka streams
ScalaDays 2014 - Reactive Scala 3D Game Engine
-
Upload
aleksandar-prokopec -
Category
Software
-
view
1.014 -
download
0
description
Transcript of ScalaDays 2014 - Reactive Scala 3D Game Engine
1
A Reactive 3D Game Engine in Scala
Aleksandar Prokopec@_axel22_
2
What’s a game engine?
3
Simulation
4
Real-time simulation
5
15 ms
Real-time simulation
7
Input Simulator
Interaction
Renderer
9
Reactive values
Reactive[T]
10
val ticks: Reactive[Long]
11
ticks 1
1
2
2
3
3
4
4
60
60
61
61
ticks onEvent { x => log.debug(s”tick no.$x”)}
12
1 2 3 4 60 61
tick no.1tick no.2tick no.3tick no.4
tick no.60tick no.61
...
ticks foreach { x => log.debug(s”tick no.$x”)}
13
1 2 3 4 60 61
14
for (x <- ticks) { log.debug(s”tick no.$x”)}
15
Reactive combinators
for (x <- ticks) yield { x / 60 }
16
val seconds: Reactive[Long] = for (x <- ticks) yield { x / 60 }
17
6061
val seconds: Reactive[Long] = for (x <- ticks) yield { x / 60 }
18
ticks 1
1
2
2
3
3 60 61
seconds
0 0 0 1 1
ticks
seconds
00011
val days: Reactive[Long] = seconds.map(_ / 86400)
19
val days: Reactive[Long] = seconds.map(_ / 86400)val secondsToday =
20
val days: Reactive[Long] = seconds.map(_ / 86400)val secondsToday = (seconds zip days) { (s, d) => s – d * 86400 } 21
val days: Reactive[Long] = seconds.map(_ / 86400)val secondsToday = (seconds zip days) { _ – _ * 86400 }
22
seconds days
secondsToday
val angle = secondsInDay.map(angleFunc)
23
val angle = secondsInDay.map(angleFunc)val light = secondsInDay.map(lightFunc)
24
25
https://www.youtube.com/watch?v=5g7DvNEs6K8&feature=youtu.be
Preview
26
val rotate = keys
a ↓shift ↓ a ↑ shift ↑pgup ↓ pgup ↑keys
27
val rotate = keys.filter(_ == PAGEUP)
a ↓shift ↓ a ↑ shift ↑pgup ↓ pgup ↑keys
pgup ↓ pgup ↑filter
28
val rotate = keys.filter(_ == PAGEUP) .map(_.down)
a ↓shift ↓ a ↑ shift ↑pgup ↓ pgup ↑keys
pgup ↓ pgup ↑filter
true falsemap
29
if (rotate()) viewAngle += 1
true falsemap
30
Signals
31
Reactives arediscrete
32
Signals are continuous
33
trait Signal[T]extends Reactive[T] { def apply(): T}
34
val rotate = keys.filter(_ == PAGEUP) .map(_.down) .signal(false)
true falsemap
signal
35
val rotate: Signal[Boolean] = keys.filter(_ == PAGEUP) .map(_.down) .signal(false)
true falsemap
signal
36
val rotate: Signal[Boolean]val ticks: Reactive[Long]
ticks
37
val rotate: Signal[Boolean]val ticks: Reactive[Long]
ticks
rotate
38
val rotate: Signal[Boolean]val ticks: Reactive[Long]val viewAngle: Signal[Double] =
ticks
rotate
viewAngle
39
List(1, 2, 3).scanLeft(0)(_ + _)
40
List(1, 2, 3).scanLeft(0)(_ + _)
→ List(0, 1, 3, 6)
41
def scanLeft[S](z: S)(f: (S, T) => S) : List[S]
42
def scanLeft[S](z: S)(f: (S, T) => S) : List[S]
def scanPast[S](z: S)(f: (S, T) => S) : Signal[S]
43
val rotate: Signal[Boolean]val ticks: Reactive[Long]val viewAngle: Signal[Double] = ticks.scanPast(0.0)
ticks
rotate
viewAngle
44
val rotate: Signal[Boolean]val ticks: Reactive[Long]val viewAngle: Signal[Double] = ticks.scanPast(0.0) { (a, _) => if (rotate()) a + 1 else a }
ticks
rotate
viewAngle
46
val velocity = ticks.scanPast(0.0) { (v, _) => }val viewAngle =
47
val velocity = ticks.scanPast(0.0) { (v, _) => if (rotate()) v + 1 }val viewAngle =
48
val velocity = ticks.scanPast(0.0) { (v, _) => if (rotate()) v + 1 else v – 0.5 }val viewAngle =
49
val velocity = ticks.scanPast(0.0) { (v, _) => if (rotate()) v + 1 else v – 0.5 }val viewAngle = velocity.scanPast(0.0)(_ + _)
51
Higher-orderreactive values
52
(T => S) => (List[S] => List[T])
53
(T => S) => (List[S] => List[T])
Reactive[Reactive[S]]
54
val mids = mouse .filter(_.button == MIDDLE)
mids ↓ ↓ ↓↑ ↑ ↑
55
val mids = mouse .filter(_.button == MIDDLE)val up = mids.filter(!_.down)val down = mids.filter(_.down)
mids ↓ ↓ ↓
up
down ↓ ↓ ↓
↑ ↑ ↑
↑ ↑ ↑
56
val mids = mouse .filter(_.button == MIDDLE)val up = mids.filter(!_.down)val down = mids.filter(_.down)val drags = down .map(_ => mouse)
up
down ↓ ↓ ↓
↑ ↑ ↑
57
val mids = mouse .filter(_.button == MIDDLE)val up = mids.filter(!_.down)val down = mids.filter(_.down)val drags = down .map(_ => mouse)
up
down ↓ ↓ ↓
↑ ↑ ↑
Reactive[Reactive[MouseEvent]]
58
val mids = mouse .filter(_.button == MIDDLE)val up = mids.filter(!_.down)val down = mids.filter(_.down)val drags = down .map(_ => mouse)
up
down ↓ ↓ ↓
↑ ↑ ↑
drags
drags
up ↑ ↑ ↑
59
val mids = mouse .filter(_.button == MIDDLE)val up = mids.filter(!_.down)val down = mids.filter(_.down)val drags = down .map(_ => mouse.until(up))
down ↓ ↓ ↓
mouse.until(up)
60
val mids = mouse .filter(_.button == MIDDLE)val up = mids.filter(!_.down)val down = mids.filter(_.down)val drags = down .map(_ => mouse.until(up))
down ↓ ↓ ↓
up ↑ ↑ ↑
drags
61
val drags = down .map(_ => mouse.until(up)) .map(_.map(_.xy))
drags
1, 12, 3
3, 5
4, 6
6, 9
9, 9
62
val drags = down .map(_ => mouse.until(up)) .map(_.map(_.xy)) .map(_.diffPast(_.xy - _.xy))
drags
0, 01, 2
1, 2
0, 0
2, 3
0, 0
63
val drags = down .map(_ => mouse.until(up)) .map(_.map(_.xy)) .map(_.diffPast(_.xy - _.xy)) .concat()
drags 0, 0 1, 2 1, 2 0, 0 2, 3 0, 0
64
val drags = down .map(_ => mouse.until(up)) .map(_.map(_.xy)) .map(_.diffPast(_.xy - _.xy)) .concat()val pos = drags.scanPast((0, 0))(_ + _)
drags 0, 0 1, 2 1, 2 0, 0 2, 3 0, 0
pos 0, 0 1, 2 2, 4 2, 4 4, 7 4, 7
65
http://youtu.be/RsMSZ7OH2fo
Preview
However, a lot of object allocations lead to GC issues.
66
Reactive mutators
67
class Matrix { def apply(x: Int, y: Int): Double def update(x: Int, y: Int, v: Double)}
68
val screenMat: Signal[Matrix] = (projMat zip viewMat)(_ * _)val invScreenMat = screenMat.map(_.inverse)
69
Reactive[immutable.Matrix[T]]
70
val screenMat: Signal[Matrix] = (projMat zip viewMat)(_ * _)val invScreenMat = screenMat.map(_.inverse)
(4*4*8 + 16 + 16)*4*100 = 64 kb/s
71
val screenMat = Mutable(new Matrix)(projMat, viewMat).mutate(screenMat) { (p, v) => screenMat().assignMul(p, v)}val invScreenMat = Mutable(new Matrix)screenMat.mutate(invScreenMat) { m => invScreenMat().assignInv(m)}
72
Reactive collections
73
http://youtu.be/ebbrAHNsexc
Preview
How do we model that a character is selected?
74
val selected: Reactive[Set[Character]]
75
val selected: ReactSet[Character]
76
trait ReactSet[T]extends ReactContainer[T] { def apply(x: T): Boolean}
77
trait ReactContainer[T] { def inserts: Reactive[T] def removes: Reactive[T]}
78
A reactive collectionis a pair
of reactive values
79
val selected = new ReactHashSet[Character]
80
81
val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c)))
82
83
class ReactContainer[T] { self => def inserts: Reactive[T] def removes: Reactive[T]
def map[S](f: T => S) = new ReactContainer[S] { def inserts: Reactive[T] = self.inserts.map(f) def removes: Reactive[T] = self.removes.map(f) }}
84
val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .to[ReactHashMap]
85
val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .to[ReactHashMap]
86
val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .to[ReactHashMap]
87
val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .to[ReactHashMap]
88
val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .react.to[ReactHashMap]
89
val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .react.to[ReactHashMap]
90
val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .react.to[ReactHashMap]
91
val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .react.to[ReactHashMap]
92
• reactive mutators• reactive collections• @specialized• Scala Macros• shipping computations to the GPU
93
http://storm-enroute.com/macrogl/
MacroGL
94
https://www.youtube.com/watch?v=UHCeXdxkx70
GC Preview
95
Is Scala Ready?
96
YES!
97
Thank you!