---
title: Decoupling All the Things
teaser: In gamedev, decoupling the in-game world from the real world makes math easier.
tags: elm,gamedev,web
author: Joël Quenneville
published_on: 2018-04-04
---

The [Elm] `#gamedev` community on [Slack] has started having monthly [game
jams]. That sounded fun so I built a [pirate-themed tower defense game] for the
[February jam].

This was my first attempt at gamedev so I started with the simplest approach.
Along the way, I found that I'd created a system where my game world was tightly
coupled to environmental factors.

[Elm]: http://elm-lang.org/
[Slack]: https://elmlang.herokuapp.com/
[game jams]: http://elmgames.club/
[pirate-themed tower defense game]: https://github.com/JoelQ/safe-tea
[February jam]: https://itch.io/jam/elm-game-jam-feb-2018

## Decouple your in-game coordinate system from the screen

I made an early decision to use [canvas] to render my game, using the [graphics]
library. At first things were simple. If I wanted to render a pirate 100 pixels
from the left of the screen and 200 pixels from the top, I'd give the pirate a
position like `(100, 200)`.

This way of modeling things is simple. There is a one-to-one correspondence
between the in-game model and the screen. This tight coupling has several
drawbacks though.

This approach **assumes you're showing the whole map**. You can't have any
portions offscreen, offer any zoom or scroll features. I didn't end up needing
these features but it might have been nice to have an off-screen area of the map
to act as a staging area for the next wave of pirates. As it was I didn't get
far enough to really tinker with waves of pirates.

This approach **makes coordinate math more difficult**. In a traditional
Cartesian coordinate system, points exist in one of four quadrants. A rendering
system where `(0,0)` is in the top-left corner and both X and Y increase going
right and down respectively doesn't fit on the traditional Cartesian system.
It's closest to quadrant IV, but with the Y axis flipped. This meant I kept
confusing myself over whether or not I needed to negate the Y value in a given
situation.

![cartesian coordinate system](https://images.thoughtbot.com/jq-building-a-game-in-elm/FVa63FUQIQxhXLkETSyA_classical-cartesian-plane.png)
![screen coordinate system](https://images.thoughtbot.com/jq-building-a-game-in-elm/0IqD2SKJTzmITgig1x74_screen-coordinate-system.png)

This approach **forces you to measure in-game distances in pixels**. This means
you are stuck with a given map and tile sizes. My map tiles were 64x64 pixels
and my map was a square 15x15 tiles, making it 960x960 pixels. This is a bit
large to put on a screen and I considered making the map tiles a smaller size
such as 32x32 pixels. However changing this rendering detail would have broken a
lot of my game code. For example, all the starting positions for the pirates and
players would now be wrong.

The solution to all of these problems is *use a separate coordinate system and
distance measure for all the game logic*. Then at render time you convert "world"
coordinates and distances into "screen" coordinates and pixels. You could even
create [custom types] to distinguish positions in different coordinate spaces.

I figured this lesson out too late to implement those changes everywhere before
the deadline so the game still uses a lot of screen positions.

[canvas]: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API
[graphics]: http://package.elm-lang.org/packages/evancz/elm-graphics/latest
[custom types]: gamedev-with-elm-types

## Decouple your game speed from the speed of the render loop

Similar to the previous lesson, this one is a result of coupling game logic to
visual decisions.

I originally set my game to tick forward 3 times a second. Every tick, the
pirates would move forward by a constant number of pixels. This was helpful as I
was programming the movement logic for the pirates but the movement is obviously
very jerky. So I sped up the game speed to 30 times a second. Now the pirates
moved smoothly but were also _much_ too fast. I had to go and divide that speed
constant by 30 to get the desired speed. Ideally the speed of in-game entities
remains the same regardless of how fast the game is ticking along.

I was able to do this by subscribing to time elapsed since the last tick. I can
then multiply the time elapsed by my rate of speed to get the distance moved in
the elapsed period. It ends up looking like this:

```elm
move : Time -> Pirate -> Pirate
move diff pirate =
  let
    speedPerSecond =
        60

    distanceInThisInterval =
        (Time.inSeconds diff) * speedPerSecond
  in
    -- actually move the pirate
```

This system has a bonus advantage: you still get smooth movement even when ticks
aren't 100% regular. I used the [animation-frame] package to subscribe to the
browser's paint rate. This is usually about 60 frames per second but varies a
bit from frame to frame based on a variety of conditions.

[animation-frame]: http://package.elm-lang.org/packages/elm-lang/animation-frame/latest
