Coordinate transformations

 from Red Blob Games
DRAFT
Aug 2016

STATUS: ABANDONED, again :( -- not happy with it (see blog post[1])

Windows into worlds

Let’s start with this 2000 pixel wide platformer-style game world:

We don’t normally show the entire game world to the player. Instead, we display a “window” into the game world. Drag the world left/right to see how we want to display 600 pixels of the game world at a time:

How would we convert world to screen coordinates for drawing? In this simple case, we can subtract the left side of the window. {arrow here, point to the left side}. For example, if the object in the world is at x=300, and the left side of the screen is at x=200, then we will draw that object at 300-200 = 100.

The key idea is that there can be than one coordinate system in a game. There are world x-coordinates in blue (measured in tiles pixels) and screen x-coordinates in red (measured in pixels).

{todo: the demo shouldn't highlight the window in the minimap until you've dragged the world}

Mouse clicks

For drawing, we converted world coordinates to screen coordinates. For mouse clicks, we want to do the opposite.

{demo}

In general, whatever steps we take to turn world coordinates into screen coordinates, we’ll reverse them to turn screen coordinates into world coordinates.

Rescaling

The size of the world map doesn’t have to be expressed in terms of pixels. It might be meters, tiles, or some other unit. Here’s the same map measured in tiles:

{same viz but different units}

If the world map size and the screen size aren’t expressed in the same units, we need to convert the number to a different scale. We can use this useful mathematical function called rescale or remap:

function rescale(value, from_begin, from_end, to_begin, to_end) {
    var t = (value - from_begin) / (from_end - from_begin);
    return to_begin + t * (to_end - to_begin);
}

For any position world.x in the world we can calculate the position on the screen by rescaling rescale(world.x, view_left, view_right, 0, 600) {how to explain this??}

For mouse clicks, fortunately the rescale operation can work in reverse. Instead of rescale(world.x, world_begin, world_end, screen_begin, screen_end) we’ll call rescale(screen.x, screen_begin, screen_end, world_begin, world_end) and it will return x in world coordinates.

Player sprite

Above I’ve assumed we already know where to scroll the window. In some games the player can scroll around manually. In other games the camera follows the player.

How does a camera work?

A camera has a position in the game world. We then want to make sure the camera’s position is in the center of the window.

{demo? math? not sure yet}

Transforms

For cameras I showed the brute force approach to the math. There’s a more elegant approach using transforms.

transform via camera → shift the center of the window

Two dimensional movement

{different style game}

The math works the same as before. Where we worked with just x, now we work with both x and y, independently.

Two dimensions, with constraints

We can separately control how x and y work. Let’s make x freely move, but y only moves if you get too close to the border.

Multiple cameras

Rotation

Order of transforms

Transform A;B is not the same as B;A. Show a demo.

Zooming

{Zooming without centering on the screen first can lead to the wrong result.}

Axis stretch

Stretch y axis, but also flip y axis to show negative numbers work too

Isometric view

Demo, then break it down into shear and rotate transforms, then show mouse clicks

Appendix: Matrices

THIS SECTION will go at the very end of the page

motivation: pattern for all of the transformations; introduce: matrix

Matrices aren’t transforms. Matrices are representations of transforms.

Matrices allow you to optimize a bit. A chain of transforms might be q = f(g(h(p))). In math we can “compose” functions. We can combine f, g, h ahead of time into q = (f o g o h)(p). We don’t have a way to compose functions at run time in most programming languages we use.

All of our transforms happen to be representable as matrix multiplies, q = F * G * H * p. Matrix multiply is associative so F * (G * (H * p)) = ((F * G) * H) * p. By representing our functions as matrix operations, we can compose them ahead of time. Instead of applying a long chain of 8 operations to every point p, we can first combine those 8 operations into 1, and then apply that to p. This is common in 3D programming, and there is both CPU and GPU acceleration for 4x4 matrix operations.

u,v vectors; show how they are transformed; show how they are in the matrix; show a unit circle too

More about matrices: