1 of 28

2 of 28

What’s wrong with OOP for games?

  • Game logic strewn across many different files
  • Performance overhead
  • ‘Composition over Inheritance’ supposedly an OOP principle, but nothing composes well
  • I just don’t like it

von Karma

Who cares if a foolish fool like you doesn’t like it? OOP is the natural way to model a game world.

3 of 28

Pure Ideology

4 of 28

Natural?

Thanks Lakitu. Plumber, please reduce your HP by 1

Hey Bill, you just

hit that plumber!

Of-a course Bill, that’s-a my job after all!

5 of 28

We want…

  • to make a game using Haskell…
  • but how should we organise it?

6 of 28

Glossy MVC

  • Model
  • View
  • Controller
  • `data Model = …`
  • `Model -> Picture`
  • `Event -> Model -> Model`
  • `Float -> Model -> Model`

deltaTime

von Karma

Tsk, tsk, tsk. A design pattern? You’re not running back to OOP already now are you?

7 of 28

Glossy MVC

  • Model
  • View
  • Controller

8 of 28

Bristol CSS Game Jam 2018

9 of 28

What does gloss help you with?

  • 2D graphics
  • Handling user input
  • Time-dependent state updates
  • Games where you can nicely model and update a monolithic state, like…
    • Snake
    • Chess
    • Ace Attorney…

von Karma

What is this "Ace Attorney"? Just another simple game, no doubt. How convenient that none of these games have complex interactions between different… objects.

10 of 28

What doesn’t gloss help you with?

  • It’s just some functions updating a data type, it can do anything!
  • …but assembly can do anything too
  • No help at all for what the model should be
  • …or what the controller should do
  • Not easy to specify interactions between game ‘objects’

11 of 28

Solution: ECS

  • Entities
  • Components
  • Systems
  • Indexes (e.g. entity 1, entity 2 etc.)
  • Data for entities (e.g. HP, position etc.)
  • Functions over sets of components (possibly monadic, e.g. for all entities with HP and position do X)

von Karma

Another design pattern? You are becoming predictable…

12 of 28

Defining Basic Components

newtype Velocity = Vel (Float, Float)

instance Component Velocity where type Storage Velocity = Map Velocity

newtype Position = Pos (Float, Float)

instance Component Position where type Storage Position = Map Position

~Map Entity Velocity

{-# language TypeFamilies #-}

~Map Entity Position

13 of 28

Creating new entities

createEntities :: System' ()

createEntities = do

newEntity_ (Pos (0,0), Vel (10,10))

newEntity_ (Pos (50,50), Vel (-10,-10))

von Karma

Set'? System'? I know what you Haskellers are like. Those aren’t the real types, are they? What are you hiding?

newEntity_

:: Set' component

=> component -> System' ()

newEntity_

:: (MonadIO m, Set world m component, Get world m EntityCounter)

=> component -> SystemT world m ()

14 of 28

Defining basic systems

stepPos :: Float -> System' ()

stepPos dt

= cmap (\(Pos (x,y), Vel (vX, vY))

-> Pos (x + vX * dt, y + vY * dt))

cmap

:: (Get' cx, Members' cx, Set' cy)

=> (cx -> cy) -> System' ()

15 of 28

Gloss integration: Model

Generates:

  • Model data type
  • Associated typeclasses
  • initModel :: IO Model

{-# language TemplateHaskell #-}

import Apecs.Gloss

type System' a = System Model a

16 of 28

Gloss integration: View

view :: System Model Picture

view

= foldDraw (\(Pos (x,y))

-> translate x y (circle 10))

foldDraw

:: (Get' c, Members' c)

=> (c -> Picture) -> System Model Picture

17 of 28

Gloss integration: Controller

inputController :: Event -> System Model ()

inputController event = case event of

EventKey (SpecialKey KeyEsc) Down _ _

-> liftIO exitSuccess

_ -> pure ()

stepPos :: Float -> System Model ()

stepPos dt

= cmap (\(Pos (x,y), Vel (vX, vY))

-> Pos (x + vX * dt, y + vY * dt))

Time step controller

reminder

18 of 28

Gloss integration: Modified play

19 of 28

Gloss integration

Live demo

von Karma

You promised complex interactions, but all I see are circles moving in a straight line. I don’t have time for your stalling, hurry up and show me some real evidence!

20 of 28

Emergent systems: Boids

What a boid brain!

boid = “bird-oid object”

21 of 28

Boids: Separation

22 of 28

Boids: Separation

separation :: System' ()

separation = cmapM $ \(Boid, Position p, Velocity v) -> do

collect :: _ => (components -> Maybe a) -> System' [a]

localDistVecs

<- collect

(\(Obstacle, Position p')

-> (p - p') `justIf` (withinRange separationDist p p'))

let dv = separationFactor *^ sumV localDistVecs

pure $ Velocity (v + dv)

cmapM :: (Get' cx, Members' cx, Set' cy) => (cx -> System' cy) -> System' ()

justIf :: a -> Bool -> Maybe a

23 of 28

Boids: Cohesion

24 of 28

Boids: Cohesion

cohesion :: System' ()

cohesion = cmapM $ \(Boid, Position p, Velocity v, etyX :: Entity) -> do

localPs <- collect $ \(Boid, Position p', etyY :: Entity)

-> p' `justIf` (withinRange sightRadius p p'

&& etyX /= etyY)

let dv = case localPs of

[] -> 0

_ -> cohesionFactor *^ (mean localPs - p)

pure $ Velocity (v + dv)

25 of 28

Boids: Alignment

26 of 28

Boids: Alignment

alignment :: System' ()

alignment = cmapM $ \(Boid, Position p, Velocity v, etyX :: Entity) -> do

localVs <- collect $ \(Boid, Position p', Velocity v', etyY :: Entity)

-> v' `justIf` (withinRange sightRadius p p'

&& etyX /= etyY)

let dv = case localVs of

[] -> 0

_ -> alignmentFactor *^ (mean localVs - v)

pure $ Velocity (v + dv)

27 of 28

von Karma

Hmph. I suppose your case isn’t completely worthless. Just be glad this is not a real court. If it were, I would not be letting you off so easy.

Boidemic

Live demo

28 of 28

Conclusion

  • OOP isn’t the only way to make games
  • Haskell’s gloss library
    • MVC (Model-View-Controller)
    • Easy 2D graphics
    • Input handling
  • Haskell’s apecs library
    • ECS (Entities-Components-Systems)
    • Generates model, based on components
    • Dynamic and expressive game logic
    • Fast, flexible, and fun!
  • Tetramine-o: https://github.com/ratherforky/glossy-haskell-game
  • Boidemic: https://github.com/ratherforky/boidemic
  • Email: jess.foster@bristol.ac.uk
  • Interested in a PL summer internship or PhD at Bristol? Talk to me after

Questions?

https://plrg-bristol.github.io/