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).
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.}Conceptually, if you want a point to stay in the same place and everything else zoom around it, you need to scale when that point is at 0,0. That’s because 0,0 is the only position that doesn’t move when scaled. So:
- Translate() the point to be at 0,0
- Scale() with the desired zoom factor
- Translate() the point back, undoing the first translate
https://www.steveruiz.me/posts/zoom-ui -- explains another way of thinking about this
https://github.com/anvaka/panzoom -- js library that implements zoom on a point, along with all the many event handlers you need to make it work right
Axis stretch
Stretch y axis, but also flip y axis to show negative numbers work tooIsometric view
Demo, then break it down into shear and rotate transforms, then show mouse clicksAppendix: Matrices
THIS SECTION will go at the very end of the page
motivation: pattern for all of the transformations; introduce: matrixMatrices 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 tooMore about matrices:
- http://ncase.me/matrix/
- http://franklinta.com/2014/09/08/computing-css-matrix3d-transforms/
- http://maxgoldste.in/invitation-to-another-dimension/
- https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab - 3Blue1Brown’s videos