scaladays 2014 - reactive scala 3d game engine

Post on 27-Aug-2014

1.018 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

Slides for the Reactive 3D Game Engine presented at ScalaDays 2014. Shows the demo of the 3D engine, followed by the description of the reactive 3D game engine - how reactive dependencies between input, time and game logic are expressed, how to deal with GC issues, how to model game state using Reactive Collections.

TRANSCRIPT

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

6

Demo first!http://youtu.be/pRCzSRhifLs

7

Input Simulator

Interaction

Renderer

Reactive Collectionshttp://reactive-collections.com

8

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

45

http://youtu.be/blG95W5uWQ8

Preview

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)(_ + _)

50

http://youtu.be/NMVhirZLWmA

Preview

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!

top related