---
title: Gamedev with Elm Types
teaser: Elm's type system gives us some great tools for modeling gamedev.
tags: elm,gamedev,web
author: Joël Quenneville
published_on: 2018-04-04
---

![screenshot of game](https://images.thoughtbot.com/jq-building-a-game-in-elm/UfCoazYcSfWrla5fLunh_safe-tea-1-1.png)

The [Elm] `#gamedev` community on [Slack] has started having monthly [game
jams]. The rules are simple:

1. Write most of the source in Elm
2. Make the source publicly available
3. Focus on code readability

This is a collaborative rather than a competitive event so developers are
encouraged to read each other's code and learn from each other.

[February's game jam] set a theme of **safety**. I decided to write a tower
defense game where you play as a tea merchant who has run aground and has to
defend against incoming pirates. The game was named "Safe Tea" (yes, this whole
game is a bad pun 😛)

The game can be [played online on itch.io], the [source is on GitHub].

Leveraging Elm's type system helped make the experience of building a game much
nicer.

[Elm]: http://elm-lang.org/
[Slack]: https://elmlang.herokuapp.com/
[game jams]: http://elmgames.club/
[February's game jam]: https://itch.io/jam/elm-game-jam-feb-2018
[played online on itch.io]: https://joelq.itch.io/safe-tea
[source is on GitHub]: https://github.com/JoelQ/safe-tea

## Wrap units in custom types

Originally all numbers in the game represented pixels. However, this started
changing as the game grew more complex. Sometimes distances were measured in map
tiles rather than pixels. Sometimes an (X,Y) pair was relative to the corner of
the map, sometimes it was relative to a given pirate. All these numbers were
getting confusing.

So I started wrapping them in [domain-specific types]. This improved both type
safety and readability.

Consider the following two signatures:

```elm
distance : (Int, Int) -> (Int, Int) -> Int

-- AND

distance : Coordinate.Global -> Coordinate.Global -> Feet
```

Which one tells the better story?

[domain-specific types]: https://thoughtbot.com/blog/lessons-learned-avoiding-primitives-in-elm

## Composition vs inheritance

There are several in-game entities that need to be rendered visually to the
screen: pirates, player ship, bullets. There are a few things in common that all
renderable entities need to have.

1. A position
2. Width and height (in pixels)
3. A path to an image (I didn't have time to figure out [spritesheets]).

With this information, I can render any entity to the screen. My first idea was
to use an [extensible record].

```elm
type alias Entity a =
  { a
  | position : Coordinate.Global
  , width : Int
  , height : Int
  , imagePath : String
  }

render : Entity a -> Element
render entity =
  -- Render any entity!
```

It turns out that this approach is like trying to simulate object-oriented
inheritance in Elm. It's a little awkward to deal with and constrains how I'm
allowed to model the various game entities. It also means that game entities
drag around these extra fields that aren't relevant until render time. This got
particularly tricky when size or image varied based on other state.

So I ditched the extensible record and instead decided to write conversion
functions:

```elm
-- Entity is now a plain old record

type alias Entity =
  { position : Coordinate.Global
  , width : Int
  , height : Int
  , imagePath : String
  }

render : Entity -> Element
render entity =
  -- Render any entity!

Bullet.toEntity : Bullet -> Entity
Bullet.toEntity bullet =
  -- build an Entity based on the bullet's state
```

This approach proved particularly flexible. I could now model bullets any way I
wanted. They didn't need to be records, they could be union types or anything
else.

Bullets change image and size based on whether they are flying or in one of 3
states of exploding. By [deriving the `Entity` values] instead of storing them,
I avoided some invalid states.

By taking a derived data approach, I got this flexibility and safety while still
being able to have a single `render` function that could render any entity. Best
of both worlds!

[spritesheets]: https://gamedevelopment.tutsplus.com/tutorials/an-introduction-to-spritesheet-animation--gamedev-13099
[extensible record]: https://medium.com/@ckoster22/advanced-types-in-elm-extensible-records-67e9d804030d
[deriving the `Entity` values]: https://github.com/JoelQ/safe-tea/blob/c46e5547aa4a292f9f265211150b02236960c643/Bullet.elm#L104-L152

## Vector math and pipelines

![problem solving with vectors](https://images.thoughtbot.com/master/uspTrVMQdylbvb982vhZ_vectors.png)

I was trying to figure out how to move a pirate some distance `d` towards a
point that I knew I couldn't reach this tick. Where does the pirate end up?

The logic ended up looking like this:

1. The pirate's current position is the vector `A`
2. The place I want to reach eventually is the vector `B`
3. The full movement for the pirate is the vector `A - B`
4. I can't do the full movement so I create a new vector `C` whose _direction_
   is the same as `A - B` but whose _magnitude_ is `d`
5. The movement I can do this turn is the vector `A + C`

If you read these steps and went 😕😟😰😱🙈, you're not alone. The math was confusing
and the code was gnarly.

I was discussing my frustration in the `#gamedev` channel of the elm-lang Slack.
User [@mordrax] mentioned that it's nice to have two different coordinate
spaces. **Global** coordinates where `(0,0)` is the corner of the world and
**local** coordinates where `(0,0)` is the position of the entity.

A lot of the math, particularly rotations, is much easier if the entity being
acted on is at `(0,0)`. Mordrax suggested I create a series of pipeline-able
functions to convert between the global and local coordinate spaces as well as
for common tranforms.

This ends up looking really nice:

```elm
bullet.position
  |> Coordinate.toLocalSpace
  |> Coordinate.moveMilesTowardsPosition distanceInThisInterval
  |> Coordinate.rotateTowardsDestination
  |> Coordinate.toGlobalSpace
```

It turns out that converting to local space and back into global space is the
equivalent of having to do `- A` and `+ A` in my vector drawing above!

Adding custom types `Coordinate.Global` and `Coordinate.Local` to wrap the raw
[`Vector`] values helps make code more readable and gives me safety of knowing
I'm only doing some kinds of operations on certain kinds of positions.

[@mordrax]: https://github.com/mordrax/cotwelm
[`Vector`]: http://package.elm-lang.org/packages/SwiftsNamesake/euclidean-space/2.0.0/Euclid-Vector

## Conclusion

Joining a game jam was both an instructive and rewarding experience. Some big
takeaways were:

1. Use custom types to improve safety and readability of your code.
2. Ask for help. The `#gamedev` community is really helpful and supportive.
3. [Draw your problems]. Expressing your problems in a different visual medium
   really helps gain understanding.
4. [Decouple all things game model] from where and when they will be represented
   on the screeen.

[Draw your problems]: stop-coding-and-start-drawing
[Decouple all things game model]: decoupling-all-the-things
