ScalaDays 2014 - Reactive Scala 3D Game Engine

  • View
    1.014

  • Download
    0

  • Category

    Software

Preview:

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.

Citation preview

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!

Recommended