Time Travel

for Game Development

with Elm

20th of November 2015 - Codemotion - Milan

@doppioslash

+ClaudiaDoppioslash

blog.doppioslash.com

About me

Game Developer &

Functional Programmer

Table of Contents

  • GameDev workflow = hell
  • Time Travelling Debugger?
  • Meet Elm
  • Structuring programs in Elm
  • Time Travelling Demo
  • Gotchas

What is the single most soul destroying thing in game development?

What is the single most soul destroying thing in game development?

C++?

What is the single most soul destroying thing in game development?

Having to redo a 10 moves combo every time you check if the bug is gone?

What is the single most soul destroying thing in game development?

Projects getting canned?

Slow Iteration times

What is the single most soul destroying thing in game development?

Example Common Game Development Workflow

(starring Unity)

1. Write some code

Example Common

Game Development Workflow

2. Playtest it

(in the Unity Editor)

Example Common

Game Development Workflow

Now to test that same change on mobile devices:

Example Common Game Development Workflow (starring Unity)

Make a change and test on Device

1. Start build

Make a change and test on Device

3. load the ipa/apk on device

Make a change and test on Device

4. playtest until your code runs

Make a change and test on Device

5. Find new and exciting bugs

(only on device)

Slow Iteration times

Say bye to Flow

Slow Iteration times

Error prone forest of build settings

Slow Iteration times

Deadline panic 10x increase

(suffering together at late hours might be team-building but surely we don't need this :P)

What can fix this?

What can help?

Jenkins churning builds out in background

What can help?

Type system to catch bugs before running

What can help?

Unit testing

What is the ideal workflow?

Inventing

on

Principle

As seen on LambdaCat:

Inventing on Principle

Hot reload: reload changes to the code without stopping the game

Inventing on Principle

Time Travel:

being able to scrub to any point in the session

Inventing on Principle

Omniscience:

see all the state in the session

Is that even possible?

Racket,VR and John Carmack (watch it)

John Carmack

John Carmack

LISP (Racket)

Which language makes it all possible RIGHT NOW?

Elm

(I'm sure no one saw this coming)

(debug.elm-lang.org 

Mario demo)

What sort of language is Elm?

What sort of language is Elm?

Purely Functional

Functional Reactive

(FRP)

What sort of language is Elm?

Eager

What sort of language is Elm?

Static Type System

What sort of language is Elm?

Compiles to Javascript, HTML, CSS

What sort of language is Elm?

Interoperates with Javascript while still being type safe

What sort of language is Elm?

Small

What sort of language is Elm?

Invented by Evan Czaplicki for his thesis

What sort of language is Elm?

Meant to be approachable and practical

What sort of language is Elm?

Meant to be approachable and practical

No 'scary' terms like Monad

In Production at

Elm Reactor

(the Time Travelling Debugger)

Elm Reactor

Inspired by Inventing on Principle

Elm Reactor

Fundamentals made in a few days by Laszlo

Elm Reactor

Elm's language design is accidentally 'compatible'

Elm Reactor

You can "step through" while moving the mouse

How do you structure programs in Elm?

The Functional Triforce

Maybe

Union Types

Pattern Matching

The Functional Triforce

Union Types

define the Model:

type Tile
    = Door Size
    | Column
    | BackGround BackGroundTile
    | Shadow ShadowTile

The Functional Triforce

Maybe

gets rid of NULL errors

type Maybe a 
    = Just a 
    | Nothing

The Functional Triforce

Pattern Matching

String.toInt : String -> Maybe Int

toMonth : String -> Maybe Int toMonth rawString = 
    case String.toInt rawString of
      Nothing -> 
         Nothing 
      Just n -> 
         if n > 0 && n <= 12 then Just n else Nothing

Elm Architecture

Elm is opinionated

Elm Architecture

Use the

"Elm Architecture"

Elm Architecture

Model

View

Update

Overview

Elm Architecture

Model =

the Data Structure we pass around

Model

type alias UserInput = {}

userInput : Signal UserInput

userInput Signal.constant {}

type alias Input =

    { timeDelta : Float

​    , userInput : UserInput

    }

Model

type alias UserInput = {}

userInput : Signal UserInput

userInput Signal.constant {}

type alias Input =

    { timeDelta : Float

​    , userInput : UserInput

    }

Type signature

Model

type alias UserInput = {}

userInput : Signal UserInput

userInput Signal.constant {}

type alias Input =

    { timeDelta : Float

​    , userInput : UserInput

    }

A record

Type alias

Signal

Model

Record =

set of key value pairs

Model

Type alias =

give name to a set of fields in a record

Model

Signal =

updates every time variable changes

Model

type alias GameState = {}

defaultGame : GameState

defaultGame {}

Elm Architecture

View =

the code that renders from the Model

Display

display : (Int,Int) -> GameState -> Element

display (w,h) gameState show gameState

Display

display : (Int,Int) -> GameState -> Element

display (w,h) gameState show gameState

Type signature

Elm Architecture

Update =

the function that does the change of state 

Update

stepGame : Input -> GameState -> GameState

stepGame {timeDelta,userInput} gameState 

                    gameState

Update

stepGame uses the current Input

(which is UserInput and a timeDelta) 

to make a new GameState

Elm Architecture

Signals

Signals

delta : Signal Float

delta Time.fps 30

input : Signal Input

input Signal.sampleOn delta    

     (Signal.map2 Input delta userInput)

sampleOn

Signals

delta : Signal Float

delta Time.fps 30

input : Signal Input

input Signal.sampleOn delta    

     (Signal.map2 Input delta userInput)

map2

Signals

gameState : Signal GameState

gameState Signal.foldp stepGame

           defaultGame input

Signals

gameState : Signal GameState

gameState Signal.foldp stepGame

           defaultGame input

foldp

Signals

Signal.map2 = 

applies a function that takes 2 arguments 

to a signal

Signals

Signal.sampleOn = samples from 2nd input anytime an event occurs in the 1st

Signals

Signal.foldp =

a signal dependent on the past 

Main

(where you wire everything up)

Elm Architecture

Main

main : Signal Element

main Signal.map2 display

       Window.dimensions gameState

Main

main : Signal Element

main Signal.map2 display

       Window.dimensions gameState

Signal of Element values

Overview (again)

What are Signals?

Stream of values

What are Signals?

What are Signals?

What are Signals?

a different way of thinking about variables

What are Signals?

an explicit model of variable mutation in time

What are Signals?

non-awkward way of structuring callbacks

What are Signals?

they are wired in signal graphs 

(directed acyclic graphs)

What are Signals?

What kind of functions can we apply on signals?

What kind of functions can we apply on signals?

merge : Signal a -> 

Signal a -> Signal a

What kind of functions can we apply on signals?

 

filter : (a -> Bool) -> a -> 

Signal a -> Signal a

What kind of functions can we apply on signals?

map : (a -> result) -> Signal a -> Signal result

applies a function on a signal

returns another, transformed, signal

What kind of functions can we apply on signals?

map2 : (a -> b -> result) -> 

Signal a -> Signal b -> 

Signal result

applies a function on two signals

What kind of functions can we apply on signals?

makes a signal that depends on the past values of a signal

foldp : (a -> state -> state) -> state -> Signal a -> 

Signal state

What is a foldp?

Fold from the past

Fold from the past

Fold from the past

What is a foldp?

foldp takes (a -> state -> state) 

a function

What is a foldp?

... -> state -> ...

a default state

What is a foldp?

... -> Signal a -> ...

an input signal

What is this foldp thing?

... -> Signal state

returns a signal

(= next state of the program, after applying update)

How Elm Reactor works

Record Inputs

How Elm Reactor works

Reapplies functions to inputs

How Elm Reactor works

previous state

+

previous inputs =

next state

How Elm Reactor works

snapshotting for performance

How Elm Reactor works

How Elm Reactor works

changes to types will break hot swapping

also if a change doesn't compile

How Elm Reactor works

Why is Elm good for Time Travelling?

Why Elm Reactor works

Applying the same inputs will return the same output

(referential transparency)

Why Elm Reactor works

No side effects =

can replay code

Why Elm Reactor works

All mutable state is stored in the foldp

Static signal graph

Why Elm Reactor works

But, can it scale?

Castle of Elm

But, can it scale?

Game Jam game

Castle of Elm

Somewhat Roguelike

In 2 days

From scratch

Flexible tile system

Castle of Elm

Collisions

(let's break the collisions system)

Castle of Elm

Gotchas

Out of memory

Gotchas

Schroedinger Hot Swapping

Gotchas

But you can help!

It's OSS

<- CODE HERE

Why use statically typed pure Functional languages in Game Development?

Why it's worth to use new language research in Game Development

No runtime exceptions

Why it's worth to use new language research in Game Development

No race conditions

Why it's worth to use new language research in Game Development

Better tools

Why it's worth to use new language research in Game Development

Less code

Why it's worth to use new language research in Game Development

Ease of parallelisation

elm-lang.org/docs

@elmlang

#elmlang

Where to learn Elm?

#haskell.it @ freenode

haskell-ita.it

@Haskell_ITA

Maybe join Haskell ITA

lambdacat.com

@lambda_cat

Read LambdaCat!

Time Travel in Game Development with Elm at Codemotion Milan 2015

By Claudia Doppioslash

Time Travel in Game Development with Elm at Codemotion Milan 2015

  • 3,905